/********************************************************
*  Main intelligence algorithm. Includes IR reception.
*  By: Liliane Barbour and Jonathan Mash
*  Date: Apr. 2009
*********************************************************/
 
 
#include <Wire.h>
#include <Servo.h>
 
#define DEBUG 0
#define LOWERLIMIT 400
#define UPPERLIMIT 650
#define COUNTLIMIT 2
#define OUTPIN 12                                 //Pulse out to Motor Controller
#define SETUPPIN 13                               //Pin to hold Motor at 90
#define srfAddress 0x70                           // Address of the SRF08
#define lightByte 0x01                            // Byte to read light sensor
#define rangeByte 0x02                            // Byte for start of ranging data
#define cmdByte 0x00                              // Command byte
#define TIMER_RESET  TCNT1 = 0
#define SAMPLE_SIZE  38
#define DEBUG 1
#define led_pin 4
#define IRpin 2
 
 
unsigned int TimerValue[SAMPLE_SIZE];
char direction[SAMPLE_SIZE];
byte change_count;
long time;
bool checkIR;
Servo turret;   
 
byte sensor_address = 0xd0;
int PIRREAD[4][5];
int PIR[4];
byte index = 0;
byte currentState = 4;
byte currentServo = 90;
 
void setup() 
{ 
  turret.attach(9);  // attaches the servo on pin 9 to the servo object 
  turret.write(90);
 
  Wire.begin();
  Serial.begin(9600);
 
  pinMode(OUTPIN, OUTPUT);
  pinMode(SETUPPIN, OUTPUT); 
 
  //TCCR1A = 0x00;          // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
  // ICNC1=0 => Capture Noise Canceler disabled -- ICES1=0 => Input Capture Edge Select (not used) -- CTC1=0 => Clear Timer/Counter 1 on Compare/Match
  // CS12=0 CS11=1 CS10=1 => Set prescaler to clock/64
  //TCCR1B = 0x03;          // 16MHz clock with prescaler means TCNT1 increments every 4uS
  // ICIE1=0 => Timer/Counter 1, Input Capture Interrupt Enable -- OCIE1A=0 => Output Compare A Match Interrupt Enable -- OCIE1B=0 => Output Compare B Match Interrupt Enable
  // TOIE1=0 => Timer 1 Overflow Interrupt Enable
  //TIMSK1 = 0x00;          
 
  pinMode(IRpin, INPUT);
  pinMode(led_pin, OUTPUT);
  checkIR = false;
  //attachInterrupt(0, isrIR, FALLING);
 
  digitalWrite(SETUPPIN, HIGH);
  delay(10000);
  digitalWrite(SETUPPIN, LOW);
 
  for(int i=0; i<6; i++) {
    PIRREAD[0][i] = analogRead(0);
    PIRREAD[1][i] = analogRead(1);
    PIRREAD[2][i] = analogRead(2);
    PIRREAD[3][i] = analogRead(3);
  }
  delay(1000);
} 
 
 
void loop() 
{ 
  byte decision;
  int retVal =0;
  int initVal = 90;
  int pingVal = 0;
 
  //START IR CODE 
  if(checkIR == true)
  {    
     int retVal = getIR();
     checkIR = false;
 
     switch(retVal)
     {  
       case 384: //Volume up
         Serial.println("Volume Up");       
         break;
       case 64: //Volume down
         Serial.println("Volume Down");
         break;
       case 224: //Power
         Serial.println("Power");
         break;
       case 48: //Left
         Serial.println("Left");
         break;
       case 50: //Right
         Serial.println("Right");
         break;
 
     }
   }
   //END IR CODE
   recordPIR();
 
   decision = 4;
   if(PIR[0] > UPPERLIMIT || PIR[0] < LOWERLIMIT) {
     if(DEBUG) Serial.println("main loop 0");
     decision = stateZeroHit(0,currentState);
   }
   else if(PIR[1] > UPPERLIMIT || PIR[1] < LOWERLIMIT) {
     if(DEBUG) Serial.println("main loop 1");
     decision = stateOneHit(1,currentState);
   }
   else if(PIR[2] > UPPERLIMIT || PIR[2] < LOWERLIMIT) {
     if(DEBUG) Serial.println("main loop 2");
     decision = stateTwoHit(2,currentState);
   }
   else if(PIR[3] > UPPERLIMIT || PIR[3] < LOWERLIMIT) {
     if(DEBUG) Serial.println("main loop 3");
     decision = stateThreeHit(3,currentState);
   }
 
 
    if(decision < 4) {
      if(DEBUG) Serial.println(PIR[decision]);
 
 
      retVal = recordThermo(decision);
      pingVal = getRange();
 
 
      if(retVal < 200)
      {
        //c = (int)floor(sqrt(11600-8000*(cos((90+retVal)*2*PI/180))));
 
        //beta = (int)floor((90 - acos((-8400+c*c)/80*c)));
        //outServo.write(retVal);
 
 
        float c1, alpha;
        float final;
 
        if(retVal >90) {
          c1 = sqrt(10900.0 - (6000.0*(cos((270.0 - retVal)*PI/180.0))));
          alpha = (-9100.0+(c1*c1))/(60.0*c1);
          final = ((acos(alpha)*180/PI)+ 90);
        }
        else if (retVal <90) {
          c1 = sqrt(10900.0 - (6000.0*(cos((90 + retVal)*PI/180.0))));
          alpha = (-9100.0+(c1*c1))/(60.0*c1);
          final =(90.0-(acos(alpha)*180.0/PI));
        }
        else {
          final = 90.0;
        }
 
 
 
      // float final = retVal;
        if(final < 0)
          final = 0.0;
        if(final > 180)
          final = 180.0;
        Serial.print("Final: ");
        Serial.println((int)floor(final*100));      
        moveTV((int)floor(final));
      }
      currentState = decision;
 
    }
   delay(50); 
} 
 
void isrIR()
{
  checkIR = true; 
}
 
int recordThermo(byte sector)
{
  byte pos;
  byte heat[9];
 
  switch(sector){
    case 0:
      pos = 10;
      break;
    case 1:
      pos = 60;
      break;
    case 2:
      pos = 120;
      break;
    case 3:
      pos = 170;
      break;
  }
 
 
  turret.write((int)pos);
  if(DEBUG) Serial.print("scanning sector ");
  if(DEBUG) Serial.println((int)sector);
  delay(500);
 
  int i=0;
  int wsum = 0;
  int sum = 0;
 
   for(i=1; i<=9; i++)
   {
      Wire.beginTransmission(sensor_address>>1);
      Wire.send(i);
      Wire.endTransmission();
 
      Wire.requestFrom(sensor_address>>1, (int) 1);
      while(Wire.available() < 1)
      { ; }
 
      heat[i-1] = Wire.receive(); // receive a byte as character
   } 
 
   for(i = 1; i<9; i++)
   {
     heat[i]-=heat[0];
     if(heat[i] > 100)
       heat[i] = 0;
     else if(heat[i] < 3)
       heat[i] = 0;
 
     sum+=heat[i];
     wsum+=heat[i]*i;
     if(DEBUG) Serial.print(heat[i], DEC);
     if(DEBUG) Serial.print(" ");
   }
 
   float div = wsum/sum;
   int val = (int) round(div);
 
 
   if(DEBUG) Serial.println("");
   if(DEBUG) Serial.println(val);
   if(div > 0)
   {
     int newval = pos - (int)floor(5*(div-4));
     Serial.print("TPA Val: ");
     Serial.println(newval);
     return newval;
 
   }
 
   return 200;
}
 
byte stateZeroHit(byte detected, byte lastState) {
  if(DEBUG) Serial.println("hit state 0");
  byte count = 0;
 
  if(lastState != 1) {
    while(count < COUNTLIMIT && detected == 0) {
      recordPIR();
      if(PIR[1] > UPPERLIMIT || PIR[1] < LOWERLIMIT) {
        detected = 1;
        detected = stateOneHit(detected, 0);
      }
      count++;
    }
  }
 
  return detected;
}
 
byte stateOneHit(byte detected, byte lastState) {
  if(DEBUG) Serial.println("hit state 1");
  byte count = 0;
 
  if(lastState == 0) {
    while(count < COUNTLIMIT && detected == 1) {
      recordPIR();
      if(PIR[2] > UPPERLIMIT || PIR[2] < LOWERLIMIT) {
        detected = 2;
        detected = stateTwoHit(detected, 1);
      }
      count++;
    }
  }
 
  else if(lastState == 2) {
    while(count < COUNTLIMIT && detected == 1) {
      if(PIR[0] > UPPERLIMIT || PIR[0] < LOWERLIMIT) {
        detected = 0;
        detected = stateZeroHit(detected, 1);
      }
      else
        recordPIR();
      count++;
    }
  }
 
  return detected;
 
}
 
byte stateTwoHit(byte detected, byte lastState) {
  if(DEBUG) Serial.println("hit state 2");
 byte count = 0;
 
  if(lastState == 1) {
    while(count <COUNTLIMIT && detected == 2) {
      recordPIR();
      if(PIR[3] > UPPERLIMIT || PIR[3] < LOWERLIMIT) {
        detected = 3;
        detected = stateThreeHit(detected, 2);
      }
      count++;
    }
  }
 
  else if(lastState == 3) {
    while(count < COUNTLIMIT && detected == 2) {
      recordPIR();
      if(PIR[1] > UPPERLIMIT || PIR[1] < LOWERLIMIT) {
        detected = 1;
        detected = stateTwoHit(detected, 2);
      }
      count++;
    }
  }
 
  return detected;
}
 
byte stateThreeHit(byte detected, byte lastState) {
  if(DEBUG) Serial.println("hit state 3");
  byte count = 0;
 
  if(lastState != 2) {
    while(count < COUNTLIMIT && detected == 3) {
      recordPIR();
      if(PIR[2] > UPPERLIMIT || PIR[2] < LOWERLIMIT) {
        detected = 2;
        detected = stateTwoHit(detected, 3);
      }
      count++;
    }
  }
 
  return detected;
}
 
void moveTV(int pos) {
  float newPos = pos;
  int inDelays = (int)floor(1300.0 + (400.0*newPos/180.0));
 // Serial.println(inDelays);
 
  digitalWrite(OUTPIN, HIGH);
  delayMicroseconds(inDelays);
  digitalWrite(OUTPIN, LOW);
}
 
void recordPIR()
{
  int count, currentval;
    PIRREAD[0][index] = analogRead(0);
    delay(5);
    PIRREAD[1][index] = analogRead(1);
    delay(5);
    PIRREAD[2][index] = analogRead(2);
    delay(5);
    PIRREAD[3][index] = analogRead(3);
 
//for each sensor
  for (int i = 0; i<4; i++) {
 
    //for each index in the sensor
    for(int m = 0; m<5; m++) {
      currentval = PIRREAD[i][m];
      count = 0;
      for(int q = 0; q<5; q++) {
        if(currentval >= PIRREAD[i][q])
          count++;
      }
 
      if (count == 3) {
        PIR[i]=PIRREAD[i][m];
        m=20;
      }
 
    }
  }
  index++;
 
  if(index >=5)
    index = 0;
}
 
int getRange()
{                                   // This function gets a ranging from the SRF08  
  int range = 0; 
  byte highByte = 0x00;                             // Stores high byte from ranging
  byte lowByte = 0x00;                              // Stored low byte from ranging
 
  Wire.beginTransmission(srfAddress);             // Start communticating with SRF08
  Wire.send(cmdByte);                             // Send Command Byte
  Wire.send(0x51);                                // Send 0x51 to start a ranging
  Wire.endTransmission();
 
  delay(100);                                     // Wait for ranging to be complete
 
  Wire.beginTransmission(srfAddress);             // start communicating with SRFmodule
  Wire.send(rangeByte);                           // Call the register for start of ranging data
  Wire.endTransmission();
 
  Wire.requestFrom(srfAddress, 2);                // Request 2 bytes from SRF module
  while(Wire.available() < 2);                    // Wait for data to arrive
  highByte = Wire.receive();                      // Get high byte
  lowByte = Wire.receive();                       // Get low byte
 
  range = (highByte << 8) + lowByte;              // Put them together
 
  return(range);                                  // Returns Range
}
 
int getIR()
{
  int value = 0;
 
  //Capture Signal
  change_count = 0;
  while(digitalRead(IRpin) == HIGH) {}                                 
  TIMER_RESET;
  TimerValue[change_count] = TCNT1;
  direction[change_count++] = '0';
  while (change_count < SAMPLE_SIZE) {
    if (direction[change_count-1] == '0') {
      while(digitalRead(IRpin) == LOW) {}
      TimerValue[change_count] = TCNT1;
      direction[change_count++] = '1';
    } else {
      while(digitalRead(IRpin) == HIGH) {}
      TimerValue[change_count] = TCNT1;
      direction[change_count++] = '0';
    }
  }
 
  //Analyze Signal
  change_count = 0;
  time = (long) TimerValue[change_count] * 4;
  change_count++;
  while (change_count < SAMPLE_SIZE) {
    time = (long) TimerValue[change_count] * 4;
    if(direction[change_count-1] == '0' && direction[change_count] == '1')
    {
      //Do nothing
    }
    else if(direction[change_count-1] == '1' && direction[change_count] == '0')
    {
      if(time - (long)TimerValue[change_count-1] * 4 > 2000)
      {
        if(DEBUG) Serial.print("-");
        value = 0;
      }
      else if(time - (long)TimerValue[change_count-1] * 4 > 1000)
      {
        value+=1;
        value = value*2;
        if(DEBUG) Serial.print((int)1);
      }
      else if(time - (long)TimerValue[change_count-1] * 4 < 1000)
      {
        value = value*2;
        if(DEBUG) Serial.print((int)0);       
      }
    }
    change_count++;  
  }
  if(DEBUG) Serial.print("  (");
  if(DEBUG) Serial.print(value);
  if(DEBUG) Serial.println(") end");
  delay(200); 
 
  return value;
}