/* Multi-core Task Handler*/
TaskHandle_t primaryTask;
TaskHandle_t animationTask;
SemaphoreHandle_t animationMutex;

/* Semaphore for handling mutex */
void createSemaphore() {
  animationMutex = xSemaphoreCreateMutex();
  xSemaphoreGive(animationMutex);
}

void mutexLock() {
  xSemaphoreTake(animationMutex, portMAX_DELAY);
}

void mutexUnlock() {
  xSemaphoreGive(animationMutex);
}

//Set the animation code, suppose to be used in core 0 only
void setAnimationCode(char filename) {
  Serial.println("Updating animation frame to " + String(filename));
  mutexLock();
  animation = filename;
  mutexUnlock();
}

//Get the animation code, suppose to be used in core 1 only
char getAnimationCode() {
  char anicode = 'a';
  mutexLock();
  anicode = animation;
  mutexUnlock();
  return anicode;
}

//Get the current state of the switch
// true = After human pushed
// false = After robot pushed
bool getSwitchState() {
  int switchState = digitalRead(TOGGLE_SWITCH);
  return (switchState == 1);
}


/* Multi-core process definations */
void startCoreTasks() {
  xTaskCreatePinnedToCore(
    AnimationController,   /* Task function. */
    "animator",     /* name of task. */
    8192,       /* Stack size of task */
    NULL,        /* parameter of the task */
    2,           /* priority of the task */
    &animationTask,
    0
  );
  
  xTaskCreatePinnedToCore(
    PrimaryController,   /* Task function. */
    "primary",     /* name of task. */
    24576,       /* Stack size of task */
    NULL,        /* parameter of the task */
    1,           /* priority of the task */
    &primaryTask,      /* Task handle to keep track of created task */
    1
  );

  vTaskStartScheduler();
}

//For movement and primary logics
void PrimaryController( void * pvParameters ) {
  Serial.println("Primary logic process started on core " + String(xPortGetCoreID()));
  clearFrame();
  bool switchPushed = getSwitchState();
  if (switchPushed || ENABLE_WIFI_DEBUG) {
    /* Switch was on when device power on. Start WiFi & Web Server */
    //Set display to AP icon
    setAnimationCode('w');
    //Start AP and web server
    if (ENABLE_WIFI_DEBUG) {
      //Use WiFi client mode
      WiFi.mode(WIFI_STA); //Optional
      WiFi.begin(DEBUG_SSID, DEBUG_PWD);
      Serial.println("\nConnecting");
      while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(100);
      }
      Serial.println("\nConnected to the WiFi network");
      Serial.print("Local IP: ");
      Serial.println(WiFi.localIP());
    } else {
      WiFi.softAP(AP_SSID, NULL);
      Serial.print("Manual mode started. SSID=" + String(AP_SSID) + " listening on : ");
      Serial.println(WiFi.softAPIP());
      //Setup DNS Server
      dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
      dnsServer.start(53, "*", WiFi.softAPIP());
    }

    // Start the web server
    registerAPIEndpoints();
    server.begin();
    while(1) {
      dnsServer.processNextRequest();
      delay(1);
    }
  } else {
    /* Switch is off during power on. Use automatic mode */
    int seqCounter = 0; //Modify this value to change start state of seq
    while(1) {
      switchPushed = getSwitchState();
      if (switchPushed) {
        //Switch pushed
        executePushAnimationSequence(seqCounter);
        seqCounter++;
      }
      delay(1);
    }
  }
}

//For animation rendering
void AnimationController( void * pvParameters ) {
  Serial.println("Animation render started on core " + String(xPortGetCoreID()));
  while(1) {
    char anicode = getAnimationCode();
    handleAnimationRendering(anicode);
    delay(1);
  }
}
