#include <Wire.h>

#define TONE_USE_INT
#define TONE_PITCH 440
#include <TonePitch.h>
#define spkOne 12
const int playbackSpeed = 2;

//the i2c address of your Arduino
#define SLAVE_ADDRESS 0x03  // change this to 0x01, 0x02, 0x03, 0x04 or 0x05, depending on the values chosen by your teammates

//used for communicating the emotion values
#define NUM_VALUES 5                    // number of values to send
int state_machine_values[NUM_VALUES];   // array to store the received emotion values

//These are the values you can change by giving them te value -1, 1, or 0 (no change)
byte change_in_happy = 0;               //the statemachine value for happy ranges from 0 (sad) to 1000 (happy) 
byte change_in_energy = 0;              //the statemachine value for energy ranges from 0 (lethargic) to 1000 (energetic)
byte change_in_friendly = 0;            //the statemachine value for friendly ranges from 0 (hostile) to 1000 (friendly)
byte change_in_assertive = 0;           //the statemachine value for assertive ranges from 0 (shy) to 1000 (assertive)
byte change_in_delight = 0;             //the statemachine value for delight ranges from 0 (disgust) to 1000 (delight)

int emotions[10];

void setup()
{
  //needed for i2c communication between state machine and your arduino
  Wire.begin(SLAVE_ADDRESS);     // join the I2C bus as a slave
  Wire.onRequest(requestEvent);  // register the request event handler
  Wire.onReceive(receiveEvent);  // register event to handle incoming data

  //used for showing data in the serial monitor
  Serial.begin(9600);  // start serial communication

  pinMode(spkOne, OUTPUT);
}


void loop()
{


  doSomething(); //example function
  mainFeeling();
  printStateMachineValues();

  //printStateMachineValues(); //show the values that the state machine communicated

  delay(50); // an arbitrary delay, can be changed to whatever you need
}

void calculateEmotion(int currentStateMachineIndex) 
{

   if (state_machine_values[currentStateMachineIndex] < 500) 
   {
    emotions[currentStateMachineIndex] = abs(state_machine_values[currentStateMachineIndex] - 500);
    emotions[currentStateMachineIndex + 1] = 0;

    return;
   }

   emotions[currentStateMachineIndex] = 0;
   emotions[currentStateMachineIndex + 1] = state_machine_values[currentStateMachineIndex] - 500;
}

void mainFeeling() 
{
  int maxEmotionValue = emotions[0];  // Initialize with the first emotion value
  int maxEmotionIndex = 0;  // Initialize index for the emotion with the highest value

  for (int i = 0; i < sizeof(state_machine_values) / sizeof(state_machine_values[0]); i++) 
  {
    calculateEmotion(i);
  }


  // Find the index of the emotion with the highest value
  for (int i = 1; i < 10; i++) {
    if (emotions[i] > maxEmotionValue) {
      maxEmotionValue = emotions[i];
      maxEmotionIndex = i;
    }
  }


  maxEmotionValue = emotions[0];  // Initialize with the first emotion value
  maxEmotionIndex = 0;  // Initialize index for the emotion with the highest value

  // Find the index of the emotion with the highest value
  for (int i = 1; i < 10; i++) 
  {
    if (emotions[i] > maxEmotionValue) 
    {
      maxEmotionValue = emotions[i];
      maxEmotionIndex = i;
    }
  }

  // Output the predominant emotion based on the index
  switch (maxEmotionIndex) 
  {
    case 0:
      Serial.println("Mainly sad: ");
      Serial.print(emotions[maxEmotionIndex]);
      sadSound();
      break;
    case 1:
      Serial.println("Happy");
      happySound();
      break;
    case 2:
      Serial.println("Lethargic");
      lethargicSound();
      break;
    case 3:
      Serial.println("Energetic");
      energeticSound();
      break;
    case 4:
      Serial.println("Hostile");
      hostileSound();
      break;
    case 5:
      Serial.println("Friendly");
      friendlySound();
      break;
    case 6:
      Serial.println("Shy");
      shySound();
      break;
    case 7:
      Serial.println("Assertive");
      assertiveSound();
      break;
    case 8:
      Serial.println("Disgusted");
      disgustedSound();
      break;
    case 9:
      Serial.println("Delighted");
      delightedSound();
      break;
  }
}

void sadSound() 
{
    Serial.println("Playing Sad Sound - Sad Violin");

    tone(spkOne, NOTE_E6, 290); //high
    delay(300*playbackSpeed);
    tone(spkOne, NOTE_D6, 0); //high
    delay(150*playbackSpeed);
    tone(spkOne, NOTE_E6, 140); //high
    delay(150*playbackSpeed);
    tone(spkOne, NOTE_C6, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_D6, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_C6, 140); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_B5, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_C6, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_B5, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_A5, 300); //high
    delay(300*playbackSpeed);

    delay(2000);
    
}

void happySound()
{
  Serial.println("Playing Happy Sound");

}

void lethargicSound()
{
  tone(spkOne, NOTE_C4, 500); //do
  delay(300*playbackSpeed);
  tone(spkOne, NOTE_B3, 500); //do
  delay(300*playbackSpeed);
  tone(spkOne, NOTE_AS3, 500); //do
  delay(300*playbackSpeed);
  tone(spkOne, NOTE_A3, 500); //do
  delay(300*playbackSpeed);
  tone(spkOne, NOTE_GS3, 2000); //do
  delay(300*playbackSpeed);

  delay(2000);
  
}

void energeticSound()
{
  Serial.println("Playing Energetic Sound - Do a Deer");
  tone(spkOne, NOTE_C4, 500); //do
  delay(300*playbackSpeed);
  tone(spkOne, NOTE_D4, 500); //a
  delay(100*playbackSpeed);
  tone(spkOne, NOTE_E4, 500); //deer
  delay(300*playbackSpeed);
  tone(spkOne, NOTE_C4, 500); //a
  delay(100*playbackSpeed);
  tone(spkOne, NOTE_E4, 500); //fe-
  delay(200*playbackSpeed);
  tone(spkOne, NOTE_C4, 500); //-male
  delay(200*playbackSpeed);
  tone(spkOne, NOTE_E4, 500); //deer
  delay(400*playbackSpeed);
  tone(spkOne, NOTE_D4, 500); //ray
  delay(300*playbackSpeed);
  tone(spkOne, NOTE_E4, 500); //a
  delay(100*playbackSpeed);
  tone(spkOne, NOTE_F4, 50); //drop
  delay(100*playbackSpeed);
  tone(spkOne, NOTE_F4, 50); //of
  delay(100*playbackSpeed);
  tone(spkOne, NOTE_E4, 500); //gold-
  delay(100*playbackSpeed);
  tone(spkOne, NOTE_D4, 500); //-en
  delay(100*playbackSpeed);
  tone(spkOne, NOTE_F4, 500); //sun
  delay(400*playbackSpeed);
}

void hostileSound()
{
    tone(spkOne, NOTE_F4, 50); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_FS4, 50); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_E5, 50); //high
    delay(150*playbackSpeed);
    tone(spkOne, NOTE_F4, 50); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_FS4, 50); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_E5, 59); //high
    delay(150*playbackSpeed);
    tone(spkOne, NOTE_F4, 0); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_FS4, 0); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_F4, 0); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_FS4, 0); //high
    delay(75*playbackSpeed);
    tone(spkOne, NOTE_F4, 0); //high
    delay(150*playbackSpeed);
}

void friendlySound()
{

}

void shySound()
{
    tone(spkOne, NOTE_E7, 50); //high
    delay(150*playbackSpeed);
    tone(spkOne, NOTE_C7, 50); //high
    delay(150*playbackSpeed);
    tone(spkOne, NOTE_D7, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_CS7, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_C7, 0); //high
    delay(50*playbackSpeed);
    tone(spkOne, NOTE_B6, 0); //high
    delay(150*playbackSpeed);
}

void assertiveSound()
{

}

void disgustedSound()
{

}

void delightedSound()
{

}

//Yes, this is written inefficient, but very easy to understand ;)
//It sets each of the 5 values for changing the emotions by randomly assigning a 0, 1 or -1 to each
void doSomething()
{
  //just as an example sending random 0, -1 or +1 for each of the 5 emotions
  Serial.print("Changing values: ");

  //for happy
  int rnd = random(-100, 100);
  if(rnd < -75) { change_in_happy = -1; Serial.print("-1 "); }
  else if(rnd > 75) { change_in_happy = 1; Serial.print("1 "); }
  else { change_in_happy = 0; Serial.print("0 "); }

  //for energy
  rnd = random(-100, 100);
  if(rnd < -75) { change_in_energy = -1; Serial.print("-1 "); }
  else if(rnd > 75) { change_in_energy = 1; Serial.print("1 "); }
  else { change_in_energy = 0; Serial.print("0 "); }

  //for friendly
  rnd = random(-100, 100);
  if(rnd < -75) { change_in_friendly = -1; Serial.print("-1 "); }
  else if(rnd > 75) { change_in_friendly = 1; Serial.print("1 "); }
  else { change_in_friendly = 0; Serial.print("0 "); }

  //for assertive
  rnd = random(-100, 100);
  if(rnd < -75) { change_in_assertive = -1; Serial.print("-1 "); }
  else if(rnd > 75) { change_in_assertive = 1; Serial.print("1 "); }
  else { change_in_assertive = 0; Serial.print("0 "); }

  //for delight
  rnd = random(-100, 100);
  if(rnd < -75) { change_in_delight = -1; Serial.print("-1 "); }
  else if(rnd > 75) { change_in_delight = 1; Serial.print("1 "); }
  else { change_in_delight = 0; Serial.print("0 "); }

  Serial.println();  // print a new line
}

//example how to print the values from the state machine
void printStateMachineValues()
{
  Serial.print("State Machine Values: ");
  for (int i = 0; i < NUM_VALUES; i++)
  {
    Serial.print(state_machine_values[i]);
    Serial.print(" ");
  }
  Serial.println();
}

//###############
//below functions are for sending and receiving data to the state machine, they should not be changed
//###############

//The response for the request made by the statemachine to receive change values
void requestEvent()
{
  byte change[NUM_VALUES] = { change_in_happy, change_in_energy, change_in_friendly, change_in_assertive, change_in_delight };                     // array to store the change of each emotion
  Wire.write(change, NUM_VALUES);  // send the bytes to the master
}

// function that executes whenever data is received from the state machine
void receiveEvent(int howMany)
{
  // check if the number of bytes received matches the expected number
  if (howMany == NUM_VALUES * 2)
  {
    // read the bytes and store them in the values array
    for (int i = 0; i < NUM_VALUES; ++i)
    {
      byte x1, x2;
      x1 = Wire.read();  //x1 holds upper byte of received numPWMChannels
      x2 = Wire.read();  //x2 holds lower byte of received numPWMChannels
      state_machine_values[i] = (int)x1 << 8 | (int)x2;
    }
  }
}