#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <Servo_ESP32.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

std::string value;

#define audioIn 35
//#define audioOut 7

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

//Commands
#define NEXT_SONG 0x01
#define PREV_SONG 0x02
#define SELECT_SONG 0x03
#define VOL_UP 0x04
#define VOL_DOWN 0x05
#define SET_VOL 0x06
#define PLAY_1ST_SONG 0x08
#define SELECT_STORAGE 0x09
#define TF_CARD 0x02  //TF Card
#define SLEEP 0x0A
#define WAKEUP 0x0B
#define RESET 0x0C
#define PLAY 0x0D
#define PAUSE 0x0E
#define SONG_IN_FOLDER 0x0F
#define STOP 0x16
#define PLAY_FOLDER 0x17
#define SHUFFLE 0x18
#define SINGLE_CYCLE 0x19
#define TOGGLE_DAC 0x1A
#define OFF 0x00
#define ON 0x01
#define PLAY_AT_VOL 0x22

static int8_t Send_buf[8] = {0};

static const int servoPin = 21; //printed G18 on the board
Servo_ESP32 myservo;

char commandRecieve;

  int audioRead = 0;
  int checkSum = 0;
  int countCycles = 0;

class MyCallbacks: public BLECharacteristicCallbacks
{
  void onWrite(BLECharacteristic *pCharacteristic)
  {
    value = pCharacteristic->getValue();

    if (value.length() > 0)
    {
      Serial.println("*********");
      Serial.print("New value: ");
      for (int i = 0; i < value.length(); i++)
      {
        Serial.print(value[i]);
      }

      Serial.println();
      Serial.println("*********");
    }
  }
};

void setup()
{
  Serial.begin(115200);
  Serial.println("1- Download and install an BLE scanner app in your phone");
  Serial.println("2- Scan for BLE devices in the app");
  Serial.println("3- Connect to ESP32-BLE_Server");
  Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
  Serial.println("5- See the magic =)");
  BLEDevice::init("ESP32-BLE-Server");
  BLEServer *pServer = BLEDevice::createServer();

  BLEService *pService = pServer->createService(SERVICE_UUID);

  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setCallbacks(new MyCallbacks());
  pCharacteristic->setValue("Hello World");


  pService->start();

  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->start();
  myservo.attach(servoPin);
  
  Serial2.begin(9600, SERIAL_8N1, 16, 17);
  delay(500);

  sendCommand(SELECT_STORAGE, 0, TF_CARD);
  delay(200);

  //sendCommand(PLAY_AT_VOL, 30, 1);
  Serial.println();
  menu('h', 0);
}

 char c;  // char from Serial
 char cmd=' ';
 char cmd1=' '; 
 

void loop()
{
  //if (value[0] != commandRecieve){
  while(value[0] != 0){
    c = value[0];
    //commandRecieve = c;
    decode_c(); // Decode c.
    for(int i = 0; i < value.length(); i++){
      value[i] = value[i+1];
    }
  }
  audioRead = analogRead(audioIn);
  if(audioRead != 0){
  Serial.print(audioRead);
  Serial.print("\n");
  }
  delayMicroseconds(500);
  if (audioRead != 0){
    myservo.write(90);
    delay(33);
  }
  else{
    myservo.write(180);
  }
}


void sendCommand(byte command, byte option1, byte option2){
  delay(20);

  Send_buf[0] = 0x7E;
  Send_buf[1] = 0xFF;
  Send_buf[2] = 0x06;
  Send_buf[3] = command;
  Send_buf[4] = 0x00;
  Send_buf[5] = option1;
  Send_buf[6] = option2;
  Send_buf[7] = 0xEF;
  for(uint8_t i=0; i<8; i++){
    Serial2.write(Send_buf[i]);
  }
}

void menu(char op, int nval){
  // Menu
  switch (op){
    case '?':
    case 'h':
        Serial.println("SerialMP3Player Basic Commands:");
        Serial.println(" ? - Display Menu options. ");
        Serial.println(" P0_ - Play _ file");
        Serial.println(" F0_ - Play _ folder");
        Serial.println(" S0_ - Play _ file in loop");
        Serial.println(" V0_ - Play _ file, volume 30");
        Serial.println(" p - Play");
        Serial.println(" a - pause");
        Serial.println(" s - stop ");
        Serial.println(" > - Next");
        Serial.println(" < - Previous");
        Serial.println(" + - Volume UP");
        Serial.println(" - - Volume DOWN");
        Serial.println(" v_ - Set Volume to _");
        Serial.println(" c - Shuffle");
        Serial.println(" q - Loop 0-ON 1-OFF");
        Serial.println(" x_ - Loop _ folder");
        Serial.println(" t - Mute");
        Serial.println(" r - Reset");
        Serial.println(" e - Sleep");
        Serial.println(" w - Wake up");
        break;

    case 'P':
        Serial.print("Play Track ");
        Serial.print(nval);
        Serial.println();
        sendCommand(PLAY, 0, nval);
        break;

    case 'F':
        Serial.print("Play Folder ");
        Serial.print(nval);
        Serial.println();
        sendCommand(PLAY_FOLDER, 0, nval);
        break;

    case 'S':
        Serial.println("Play loop");
        sendCommand(PLAY_1ST_SONG, 0, nval);
        break;

    case 'V':
        Serial.println("Play file at 30 volume");
        sendCommand(PLAY_AT_VOL, 30, nval);
        break;


    case 'p':
        Serial.println("Play");
        sendCommand(PLAY, 0, 0);
        break;

    case 'a':
        Serial.println("Pause");
        sendCommand(PAUSE, 0, 0);
        break;

    case 's':
        Serial.println("Stop");
        sendCommand(STOP, 0, 0);
        break;

    case '>':
        Serial.println("Next");
        sendCommand(NEXT_SONG, 0, 0);
        break;

    case '<':
        Serial.println("Previous");
        sendCommand(PREV_SONG, 0, 0);
        break;

    case '+':
        Serial.println("Volume UP");
        sendCommand(VOL_UP, 0, 0);
        break;

    case '-':
        Serial.println("Volume Down");
        sendCommand(VOL_DOWN, 0, 0);
        break;

    case 'v':
        Serial.print("Set Volume to ");
        Serial.print(nval);
        Serial.println();
        sendCommand(SET_VOL, 0, nval);
        break;

    case 'c':
        Serial.println("Shuffle Play");
        sendCommand(SHUFFLE, 0, 0);
        break;

    case 'q':
        Serial.println("Toggle Loop Mode");
        sendCommand(SINGLE_CYCLE, 0, nval);
        if (nval == 1){
          Serial.println("OFF");
        }
        else if (nval == 0){
          Serial.println("ON");
        }
        break;

    case 'x':
        Serial.println("Loop Folder");
        sendCommand(PLAY_FOLDER, 0, nval);
        break;

    case 't':
        Serial.println("Muted");
        sendCommand(SET_VOL, 0, 0);
        break;

    case 'r':
        Serial.println("Reset");
        sendCommand(RESET, 0, 0);
        break;

    case 'e':
        Serial.println("Sleep");
        sendCommand(SLEEP, 0, 0);
        break;

    case 'w':
        Serial.println("Wake up");
        sendCommand(WAKEUP, 0, 0);
        break;
  }
}

void decode_c(){
  // Decode c looking for a specific command or a digit

  // if c is a 'v', 'P', 'F', 'S' or 'V' wait for the number XX
  if (c=='v' || c=='P' || c=='F' || c=='S' || c=='V'){
    cmd=c;
  }else{
    // maybe c is part of XX number
    if(c>='0' && c<='9'){
      // if c is a digit
      if(cmd1==' '){
        // if cmd1 is empty then c is the first digit
        cmd1 = c;
      }else{
        // if cmd1 is not empty c is the second digit
        menu(cmd, ((cmd1-'0')*10)+(c-'0'));
        cmd = ' ';
        cmd1 = ' ';
      }
    }else{
      // c is not a digit nor 'v', 'P', 'F' or 'S' so just call menu(c, nval);
      menu(c, 0);
    }
  }
}
