#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <task.h>
#include <semphr.h> 
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#include "icons.h"

#define LCD_CLEAN_LINE	"                    "
#define LCD_I2C_ADRRESS	0x27
#define LCD_COLS				20
#define LCD_ROWS 				4
#define BUTTON_UP				3
#define BUTTON_DOWN			2
#define BUTTON_LEFT			4
#define BUTTON_RIGHT 		5

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(LCD_I2C_ADRRESS,
											 LCD_COLS,
											 LCD_ROWS);

RTC_DS1307 rtc;

// protótipo da função


// variáveis globais
volatile int counter = 0;  // Contador
unsigned long lastDebounceTime = 0; // Tempo da última debounce
const unsigned long debounceDelay = 200; // Delay de debounce (200ms)
unsigned long buttonPressTime = 0; // Tempo que o botão está pressionado

volatile bool buttonUpPressed = false;
volatile bool buttonDownPressed = false;
volatile bool buttonLeftPressed = false;
volatile bool buttonRightPressed = false;
volatile bool buttonUpHeld = false; // Flag para indicar se o botão UP está sendo pressionado
volatile bool buttonDownHeld = false; // Flag para indicar se o botão DOWN está sendo pressionado

// Estrutura do Menu
enum Screen {
  SCREEN_WELCOME,
  SCREEN_MAIN,
  SCREEN_MENU,
  SCREEN_READINGS,
  SCREEN_ALARMS,
  SCREEN_OPERATION_MODE,
  SCREEN_READINGS_AC,
  SCREEN_READINGS_DC,
  SCREEN_READINGS_BATTERY
};

volatile Screen currentScreen = SCREEN_WELCOME;
volatile Screen previousScreen = SCREEN_WELCOME;

int menuSelection = 0; // Para rastrear a posição da seta no menu

// Handle da task
TaskHandle_t displayTaskHandle;
TaskHandle_t buttonTaskHandle;

/* semaforos utilizados */
SemaphoreHandle_t xSerial_semaphore;

void setup()
{
	// configura pinos para botoes
	pinMode(BUTTON_UP, INPUT_PULLUP);
  pinMode(BUTTON_DOWN, INPUT_PULLUP);
	pinMode(BUTTON_LEFT, INPUT_PULLUP);
  pinMode(BUTTON_RIGHT, INPUT_PULLUP);

	// configura interrupcao nos pinos 2 e 3
	attachInterrupt(digitalPinToInterrupt(BUTTON_UP), buttonUpPress, FALLING);
	attachInterrupt(digitalPinToInterrupt(BUTTON_DOWN), buttonDownPress, FALLING);
//	attachInterrupt(digitalPinToInterrupt(BUTTON_UP), buttonLeftPress, FALLING);
//	attachInterrupt(digitalPinToInterrupt(BUTTON_DOWN), buttonRightPress, FALLING);

	// inicializa o LCD, backlight e limpa tela.
	lcd.init();
	lcd.backlight();
	lcd.clear();

	init_icons(lcd);

	/* Criação dos semaforos */
  xSerial_semaphore = xSemaphoreCreateMutex();

	// Criação da task para mostrar o valor do contador no display LCD
  xTaskCreate(
    displayTask,       // Função da task
    "Display Task",    // Nome da task
    256,               // Tamanho da stack (em palavras)
    NULL,              // Parâmetro para a função da task
    1,                 // Prioridade da task
    &displayTaskHandle // Handle da task
  );
	//lcd.setCursor(0, 3);
  //lcd.write(ICON_UNLOCKED_CHAR);

	xTaskCreate(
    buttonTask,
    "Button Task",
    256,
    NULL,
    1,
    &buttonTaskHandle
  );

  // Simulação de alarmes
  //addAlarm(DateTime(2024, 7, 22, 12, 0, 0));
  //addAlarm(DateTime(2024, 7, 23, 15, 30, 0));

	// Inicia o scheduler do FreeRTOS
  vTaskStartScheduler();

}

void loop()
{

}

/*
void buttonUp() {
  unsigned long currentTime = millis();
  if ((currentTime - lastDebounceTime) > debounceDelay) {
    lastDebounceTime = currentTime;
    buttonPressTime = millis();
    buttonUpHeld = true;
    // Incrementa o contador para pressionamentos normais
    counter++;
  }
}
*/

/*
void buttonDown() {
  unsigned long currentTime = millis();
  if ((currentTime - lastDebounceTime) > debounceDelay) {
    lastDebounceTime = currentTime;
    buttonPressTime = millis();
    buttonDownHeld = true;
    // Incrementa o contador para pressionamentos normais
    counter--;
  }
}
*/

void buttonUpPress() {
  buttonUpPressed = true;
}

void buttonDownPress() {
  buttonDownPressed = true;
}

void buttonLeftPress() {
  buttonLeftPressed = true;
}

void buttonRightPress() {
  buttonRightPressed = true;
}

/* displaytask de teste com contador
void displayTask(void *pvParameters) {
  while (1) {
    // Exibe o valor do contador no display LCD
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Contador:");
    lcd.setCursor(0, 1);
    lcd.print(counter);

    // Incrementa o contador a cada 200ms se o botão UP estiver sendo pressionado por mais de 1 segundos
    if (buttonUpHeld && (millis() - buttonPressTime > 1000)) {
      counter = counter + 10;
      vTaskDelay(200 / portTICK_PERIOD_MS);
    } else {
      vTaskDelay(100 / portTICK_PERIOD_MS); // Verifica o estado do botão a cada 100ms
    }
    
		// Incrementa o contador a cada 200ms se o botão DOWN estiver sendo pressionado por mais de 1 segundos
    if (buttonDownHeld && (millis() - buttonPressTime > 1000)) {
      counter = counter - 10;
      vTaskDelay(200 / portTICK_PERIOD_MS);
    } else {
      vTaskDelay(100 / portTICK_PERIOD_MS); // Verifica o estado do botão a cada 100ms
    }

    if (digitalRead(BUTTON_UP) == HIGH) {
      buttonUpHeld = false; // Reseta a flag se o botão não estiver pressionado
    }
		if (digitalRead(BUTTON_DOWN) == HIGH) {
      buttonDownHeld = false; // Reseta a flag se o botão não estiver pressionado
    }

  }
}
*/

void displayTask(void *pvParameters) {
  while (1) {
    switch (currentScreen) {
      case SCREEN_WELCOME:
        displayWelcomeScreen();
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        currentScreen = SCREEN_MAIN;
        break;
      case SCREEN_MAIN:
        displayMainScreen();
        break;
      case SCREEN_MENU:
        displayMenuScreen();
        break;
      case SCREEN_READINGS:
        displayReadingsMenu();
        break;
      case SCREEN_ALARMS:
        displayAlarmsScreen();
        break;
      case SCREEN_OPERATION_MODE:
        displayOperationModeScreen();
        break;
      case SCREEN_READINGS_AC:
        displayReadingsACScreen();
        break;
      case SCREEN_READINGS_DC:
        displayReadingsDCScreen();
        break;
      case SCREEN_READINGS_BATTERY:
        displayReadingsBatteryScreen();
        break;
      default:
        break;
    }

    vTaskDelay(100 / portTICK_PERIOD_MS); // Atualiza a tela a cada 100ms
  }
}

void buttonTask(void *pvParameters) {
  while (1) {
    if (buttonUpPressed) {
      handleButtonUp();
      buttonUpPressed = false;
    }
    if (buttonDownPressed) {
      handleButtonDown();
      buttonDownPressed = false;
    }
    if (buttonLeftPressed) {
      handleButtonLeft();
      buttonLeftPressed = false;
    }
    if (buttonRightPressed) {
      handleButtonRight();
      buttonRightPressed = false;
    }
    vTaskDelay(100 / portTICK_PERIOD_MS); // Verifica os botões a cada 100ms
  }
}

void handleButtonUp() {
  switch (currentScreen) {
    case SCREEN_MENU:
      menuSelection--;
      if (menuSelection < 0) menuSelection = 2;
      break;
    case SCREEN_READINGS:
      currentScreen = SCREEN_MENU;
      break;
    case SCREEN_ALARMS:
      currentScreen = SCREEN_MENU;
      break;
    case SCREEN_OPERATION_MODE:
      currentScreen = SCREEN_MENU;
      break;
    case SCREEN_READINGS_AC:
      currentScreen = SCREEN_READINGS;
      break;
    case SCREEN_READINGS_DC:
      currentScreen = SCREEN_READINGS;
      break;
    case SCREEN_READINGS_BATTERY:
      currentScreen = SCREEN_READINGS;
      break;
    default:
      break;
  }
}

void handleButtonDown() {
  switch (currentScreen) {
    case SCREEN_MAIN:
      currentScreen = SCREEN_MENU;
      break;
    case SCREEN_MENU:
      menuSelection++;
      if (menuSelection > 2) menuSelection = 0;
      break;
    case SCREEN_READINGS:
      currentScreen = SCREEN_ALARMS;
      break;
    case SCREEN_ALARMS:
      currentScreen = SCREEN_OPERATION_MODE;
      break;
    case SCREEN_OPERATION_MODE:
      currentScreen = SCREEN_MENU;
      break;
    default:
      break;
  }
}

void handleButtonLeft() {
  switch (currentScreen) {
    case SCREEN_MENU:
      currentScreen = previousScreen;
      break;
    case SCREEN_READINGS_AC:
      currentScreen = SCREEN_READINGS;
      break;
    case SCREEN_READINGS_DC:
      currentScreen = SCREEN_READINGS;
      break;
    case SCREEN_READINGS_BATTERY:
      currentScreen = SCREEN_READINGS;
      break;
    default:
      break;
  }
}

void handleButtonRight() {
  switch (currentScreen) {
    case SCREEN_MENU:
      currentScreen = SCREEN_READINGS;
      break;
    case SCREEN_READINGS:
      currentScreen = SCREEN_READINGS_AC;
      break;
    case SCREEN_READINGS_AC:
      currentScreen = SCREEN_READINGS_DC;
      break;
    case SCREEN_READINGS_DC:
      currentScreen = SCREEN_READINGS_BATTERY;
      break;
    default:
      break;
  }
}

void displayWelcomeScreen() {
  lcd.clear();
  lcd.setCursor(6, 0);
  lcd.print("ENERSYS");
  lcd.setCursor(0, 1);
  lcd.print("RETIFICADOR 6 PULSOS");
  lcd.setCursor(2, 2);
  lcd.print("MODELO 125V 200A");
  lcd.setCursor(4, 3);
  lcd.print("VERSAO 0.0.1");
}

void displayMainScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(" M. RECARGA 120oC");
  lcd.setCursor(0, 1);
  lcd.print("VBAT: ");
  lcd.print(getBatteryVoltage());
  lcd.setCursor(10, 1);
  lcd.print("VCON: ");
  lcd.print(getBatteryVoltage());
  lcd.setCursor(0, 2);
  lcd.print("IBAT: ");
  lcd.print(getBatteryCurrent());
  lcd.setCursor(10, 2);
  lcd.print("ICON: ");
  lcd.print(getBatteryCurrent());
  lcd.setCursor(0, 3);
  lcd.write(ICON_DOWN_CHAR);
  lcd.print(" AA:");
}

void displayMenuScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(menuSelection == 0 ? "> LEITURAS" : "  LEITURAS");
  lcd.setCursor(0, 1);
  lcd.print(menuSelection == 1 ? "> ALARMES" : "  ALARMES");
  lcd.setCursor(0, 2);
  lcd.print(menuSelection == 2 ? "> MODO DE OPERACAO" : "  MODO DE OPERACAO");
}

void displayReadingsMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("ENTRADA CA");
  lcd.setCursor(0, 1);
  lcd.print("ENTRADA CC");
  lcd.setCursor(0, 2);
  lcd.print("BATERIA");
}

void displayAlarmsScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Alarmes");
}

void displayOperationModeScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Modo de Operacao");
}

void displayReadingsACScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Leituras Entrada CA");
}

void displayReadingsDCScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Leituras Entrada CC");
}

void displayReadingsBatteryScreen() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Leituras Bateria");
}


int getBatteryVoltage() {
  // Retorne o valor da tensão da bateria (dummy value por enquanto)
  return 120;
}

int getBatteryCurrent() {
  // Retorne o valor da corrente da bateria (dummy value por enquanto)
  return 101;
}
GND5VSDASCLSQWRTCDS1307+