#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				2
#define BUTTON_DOWN			19
#define BUTTON_LEFT			3
#define BUTTON_RIGHT 		18

// 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;

// Flags para indicar se os botoes estao sendo pressionados
volatile bool buttonUpPressed = false;
volatile bool buttonDownPressed = false;
volatile bool buttonLeftPressed = false;
volatile bool buttonRightPressed = false;
volatile bool buttonUpHeld = false; 
volatile bool buttonDownHeld = false;

// 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;

// Estrutura de dados para alarmes
struct Alarm {
  DateTime timestamp;
};

#define MAX_ALARMS 10
Alarm alarms[MAX_ALARMS];
int alarmCount = 0;

void setup() {
  Serial.begin(9600);

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

  attachInterrupt(digitalPinToInterrupt(BUTTON_UP), buttonUpPress, FALLING);
	attachInterrupt(digitalPinToInterrupt(BUTTON_DOWN), buttonDownPress, FALLING);
  attachInterrupt(digitalPinToInterrupt(BUTTON_LEFT), buttonLeftPress, FALLING);
	attachInterrupt(digitalPinToInterrupt(BUTTON_RIGHT), buttonRightPress, FALLING);


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

  lcd.setCursor(4,0);
  lcd.println("BEM-VINDO!");

  /* 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
  );

  // Criação da task para os botoes
	xTaskCreate(
    buttonTask,
    "Button Task",
    256,
    NULL,
    1,
    &buttonTaskHandle
  );

  	// Inicia o scheduler do FreeRTOS
  vTaskStartScheduler();

}

void loop() {
  // put your main code here, to run repeatedly:

}

void buttonUpPress() {
  buttonUpPressed = true;
}

void buttonDownPress() {
  buttonDownPressed = true;
}

void buttonLeftPress() {
  buttonLeftPressed = true;
}

void buttonRightPress() {
  buttonRightPressed = true;
}

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;
      default:
        break;
    }

    vTaskDelay(200 / 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 = SCREEN_MAIN;
      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:
      if (menuSelection == 0) currentScreen = SCREEN_READINGS;
      else if (menuSelection == 1) currentScreen = SCREEN_ALARMS;
      else if (menuSelection == 2) currentScreen = SCREEN_OPERATION_MODE;
      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("EMPRESA");
  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);
  opMode();
  lcd.setCursor(4, 0);
  lcd.print(" | ");
  opState();
  lcd.setCursor(16, 0);
  lcd.print(getTemperature());
  lcd.write(ICON_DEGREE);
  lcd.print("C");
  lcd.setCursor(0, 1);
  lcd.print("VBAT: ");
  lcd.print(getBatteryVoltage());
  lcd.setCursor(10, 1);
  lcd.print("VCON: ");
  lcd.print(getConverterVoltage());
  lcd.setCursor(0, 2);
  lcd.print("IBAT: ");
  lcd.print(getBatteryCurrent());
  lcd.setCursor(10, 2);
  lcd.print("ICON: ");
  lcd.print(getConverterCurrent());
  lcd.setCursor(0, 3);
  lcd.write(ICON_DOWN_CHAR);
  lcd.print(" AA: 3");
}

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

void opMode(){
  lcd.print("AUTO");
}

void opState(){
  lcd.print("RECARGA");
}

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

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

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

int getConverterCurrent() {
  // Retorne o valor da corrente da bateria (dummy value por enquanto)
  return 150;
}

int getTemperature() {
  // Retorne o valor da corrente da bateria (dummy value por enquanto)
  return 28;
}

void addAlarm(DateTime timestamp) {
  if (alarmCount < MAX_ALARMS) {
    alarms[alarmCount].timestamp = timestamp;
    alarmCount++;
  }
}

void getAlarm(int *count, DateTime *lastAlarmTime) {
  *count = alarmCount;
  if (alarmCount > 0) {
    *lastAlarmTime = alarms[alarmCount - 1].timestamp;
  }
}
GND5VSDASCLSQWRTCDS1307+