#include <LiquidCrystal_I2C.h>
#include <Stepper.h>
#include "HX711.h"

// Configuração do display LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Configuração do motor de passo
#define STEPS_PER_REV 200
Stepper motor(STEPS_PER_REV, 13, 12, 11, 10);

// Configuração do sensor HX711
#define DOUT_PIN A1
#define SCK_PIN A0
HX711 balanca;

// Pinos dos botões
int btnRed = 3;
int btnYellow = 4;
int btnEnter = 6;

int opcaoAtual = 0; // 0 = Rápido, 1 = Completo
int opcoesTotais = 2;

int estadoMenu = 0;  // 0: Seleção de modo, 1: Seleção de temperatura...
int temperaturaSelecionada = 30; 
int velocidadeCentrifugacao = 800;
int secagemAtiva = 0;
int tempoSecagem = 0;

void setup() {
  delay(3000);

  lcd.init();
  lcd.backlight();

  pinMode(btnRed, INPUT_PULLUP);
  pinMode(btnYellow, INPUT_PULLUP);
  pinMode(btnEnter, INPUT_PULLUP);

  motor.setSpeed(60);

  balanca.begin(DOUT_PIN, SCK_PIN);
  balanca.set_scale(420.0);

  updateLCD();
}

void loop() {
  if (digitalRead(btnRed) == LOW) { //cima
    if (estadoMenu == 0) {

      opcaoAtual--; // Decrementa a opção atual
      if (opcaoAtual < 0) {
        opcaoAtual = 2 - 1;
      }
      updateLCD();

    } else if (estadoMenu == 1) {
      if (temperaturaSelecionada == 30) {
        temperaturaSelecionada = 90; 
      } else {
        temperaturaSelecionada -= 10; 
      }
      updateLCD();

    } else if (estadoMenu == 2) {
      // 800 -> 1000 -> 1200 -> 1400
      if (velocidadeCentrifugacao == 800) {
        velocidadeCentrifugacao = 1400;
      } else {
        velocidadeCentrifugacao -= 200;
      }
      updateLCD();

    } else if (estadoMenu == 3) {
      secagemAtiva = (secagemAtiva == 0) ? 1 : 0;
      updateLCD();

    } else if (estadoMenu == 4) {
      if (tempoSecagem == 10) {
        tempoSecagem = 40;
      } else {
        tempoSecagem -= 10;
      }
      updateLCD();

    }
    delay(200);
  }

  if (digitalRead(btnYellow) == LOW) {
    if (estadoMenu == 0) {

      opcaoAtual++;
      if (opcaoAtual >= 2) {
        opcaoAtual = 0;
      }
      updateLCD();

    } else if (estadoMenu == 1) {
      if (temperaturaSelecionada == 90) {
        temperaturaSelecionada = 30;
      } else {
        temperaturaSelecionada += 10;
      }
      updateLCD();

    } else if (estadoMenu == 2) {
      if (velocidadeCentrifugacao == 1400) {
        velocidadeCentrifugacao = 800;
      } else {
        velocidadeCentrifugacao += 200;
      }
      updateLCD();

    } else if (estadoMenu == 3) {
      secagemAtiva = (secagemAtiva == 0) ? 1 : 0;
      updateLCD();
    } else if (estadoMenu == 4) {
      if (tempoSecagem == 40) {
        tempoSecagem = 10;
      } else {
        tempoSecagem += 10;
      }
      updateLCD();

    }
    delay(200);
  }

  if (digitalRead(btnEnter) == LOW) {
    if (estadoMenu == 0) {
      estadoMenu = 1; 
      updateLCD();
    } else if (estadoMenu == 1) {
      estadoMenu = 2;  
      updateLCD();
    } else if (estadoMenu == 2) {
      estadoMenu = 3;
      updateLCD();
    } else if (estadoMenu == 3) {
      estadoMenu = 4;
      updateLCD();
    } else if (estadoMenu == 4) {
      executeProgram(opcaoAtual);
      delay(500);
      estadoMenu = 0;
      updateLCD();
    }
    delay(500);
  }
}

void updateLCD() {
  lcd.clear();

  if (estadoMenu == 0) {  // Menu de seleção de modo
    lcd.setCursor(0, 0);
    lcd.print("Selecione o modo:");
    lcd.setCursor(0, 1);
    lcd.print(opcaoAtual == 0 ? "> Rapido" : "  Rapido ");
    lcd.setCursor(0, 2);
    lcd.print(opcaoAtual == 1 ? "> Completo" : "  Completo ");
  }
  else if (estadoMenu == 1) {  // Menu de seleção de temperatura
    lcd.setCursor(0, 0);
    lcd.print("Selecione Temp:");
    lcd.setCursor(15, 0);
    lcd.print(temperaturaSelecionada == 30 ? "> 30C" : "  30C");
    lcd.setCursor(15, 1);
    lcd.print(temperaturaSelecionada == 40 ? "> 40C" : "  40C");
    lcd.setCursor(15, 2);
    lcd.print(temperaturaSelecionada == 60 ? "> 60C" : "  60C");
    lcd.setCursor(15, 3);
    lcd.print(temperaturaSelecionada == 90 ? "> 90C" : "  90C");
  }
  else if (estadoMenu == 2) {  // Menu de seleção de velocidade de centrifugação
    lcd.setCursor(0, 0);
    lcd.print("Selec Veloc:");
    lcd.setCursor(10, 0);
    lcd.print(velocidadeCentrifugacao == 800 ? "> 800 RPM" : "  800 RPM");
    lcd.setCursor(10, 1);
    lcd.print(velocidadeCentrifugacao == 1000 ? "> 1000 RPM" : "  1000 RPM");
    lcd.setCursor(10, 2);
    lcd.print(velocidadeCentrifugacao == 1200 ? "> 1200 RPM" : "  1200 RPM");
    lcd.setCursor(10, 3);
    lcd.print(velocidadeCentrifugacao == 1400 ? "> 1400 RPM" : "  1400 RPM");
  }

  if (opcaoAtual == 0) {  // Menu de confirmação de secagem no modo rápido

    if (estadoMenu == 3) {
      lcd.setCursor(0, 0);
      lcd.print("Ligar Secagem?");
      lcd.setCursor(0, 1);
      lcd.print(secagemAtiva == 0 ? "> Nao" : "  Nao");
      lcd.setCursor(0, 2);
      lcd.print(secagemAtiva == 1 ? "> Sim" : "  Sim");
    }

    if (estadoMenu == 4 && secagemAtiva == 1) {
      lcd.setCursor(0, 0);
      lcd.print("Tempo Secagem:");
      lcd.setCursor(15, 0);
      lcd.print(tempoSecagem == 10 ? "> 10s" : "  10s");
      lcd.setCursor(15, 1);
      lcd.print(tempoSecagem == 20 ? "> 20s" : "  20s");
      lcd.setCursor(15, 2);
      lcd.print(tempoSecagem == 30 ? "> 30s" : "  30s");
      lcd.setCursor(15, 3);
      lcd.print(tempoSecagem == 40 ? "> 40s" : "  40s");
    }

  } else if (opcaoAtual == 1 && estadoMenu == 4) {  // Menu de seleção do tempo de secagem
    lcd.setCursor(0, 0);
    lcd.print("Tempo Secagem:");
    lcd.setCursor(15, 0);
    lcd.print(tempoSecagem == 10 ? "> 10s" : "  10s");
    lcd.setCursor(15, 1);
    lcd.print(tempoSecagem == 20 ? "> 20s" : "  20s");
    lcd.setCursor(15, 2);
    lcd.print(tempoSecagem == 30 ? "> 30s" : "  30s");
    lcd.setCursor(15, 3);
    lcd.print(tempoSecagem == 40 ? "> 40s" : "  40s");
  }
}

void executeProgram(int option) {
  int tempoTotal = 0;

  float limitePeso = (option == 0) ? 5.0 : 10.0;
  int temperatura =  temperaturaSelecionada;
  int velocidade = velocidadeCentrifugacao;

  int resultadoPesagem = pesagem(limitePeso);
  if (resultadoPesagem == -1) {
    return;
  }

  lcd.setCursor(0, 0);
  lcd.print("Temperatura:");
  lcd.setCursor(13, 0);
  lcd.print(temperaturaSelecionada);
  lcd.print("C");

  lcd.setCursor(0, 1);
  lcd.print("Centrifug.:");
  lcd.setCursor(12, 1);
  lcd.print(velocidadeCentrifugacao);
  lcd.print("RPM");

  if (secagemAtiva == 1) {
    lcd.setCursor(0, 2);
    lcd.print("Secagem:");
    lcd.setCursor(10, 2);
    lcd.print(tempoSecagem);
    lcd.print("s");
  } else {
    lcd.setCursor(0, 2);
    lcd.print("Secagem:");
    lcd.setCursor(10, 2);
    lcd.print(" Desligado");
  }

  delay(5000);
  updateLCD();

  if (option == 0) { // Programa Rápido
    tempoTotal = resultadoPesagem + injecaoAgua() + aquecimentoAgua(temperatura) + preLavagem() +
                 lavagem("Rapido") + escoamentoAgua() + injecaoAgua() +
                 enxague() + escoamentoAgua() + centrifugacao(velocidade);
    if (secagemAtiva) {
      tempoTotal += secagem(tempoSecagem);
    }

  } else if (option == 1) { // Programa Completo
    tempoTotal = resultadoPesagem + injecaoAgua() + aquecimentoAgua(temperatura) +
                 preLavagem() + escoamentoAgua() + injecaoAgua() +
                 aquecimentoAgua(temperatura) + lavagem("Completo") + escoamentoAgua() +
                 injecaoAgua() + enxague() + escoamentoAgua() +
                 centrifugacao(velocidade) + secagem(tempoSecagem);
  }

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Tempo Total:");
  lcd.setCursor(13, 0);
  lcd.print(tempoTotal);
  lcd.print("s");
  delay(10000);
  updateLCD();
}

int pesagem(float limitePeso) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Pesagem...");

  motor.setSpeed(40);
  motor.step(STEPS_PER_REV * 2);
  motor.step(-STEPS_PER_REV * 2);

  float peso = balanca.get_units(10);

  lcd.setCursor(0, 3);
  lcd.print("Peso: ");
  lcd.print(peso, 2);
  lcd.print(" kg");

  if (peso > limitePeso) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Peso excedido!");
    delay(3000);

    lcd.clear();
    updateLCD();
    return -1; 
  }

  return 8;
}

int injecaoAgua() {
  //8 segundos
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Injecao Agua");

  motor.setSpeed(15);
  motor.step(-STEPS_PER_REV * 2);

  return 8;
}

int aquecimentoAgua(int temperatura) {
  int tempoAquecimento = (temperatura / 10) * 5;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Aquecendo agua");
  lcd.setCursor(0, 1);
  lcd.print("Temp: ");
  lcd.print(temperatura);
  lcd.print(" C");
  lcd.setCursor(0, 2);
  lcd.print("Tempo aquec:");
  lcd.print(tempoAquecimento);

  delay(tempoAquecimento * 1000);

  return tempoAquecimento;
}

int preLavagem() {
  // Tempo total de pré-lavagem (20 segundos)
  unsigned long TEMPO_TOTAL = 20000;
  unsigned long TEMPO_ALTERNANCIA = 10000; 
  unsigned long inicio = millis();

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Pre-Lavagem");

  motor.setSpeed(60);

  while (millis() - inicio < TEMPO_TOTAL) {
    unsigned long tempoDecorrido = millis() - inicio;

    if (tempoDecorrido < TEMPO_ALTERNANCIA) {
      // Primeiros 10 segundos: Sentido horário
      motor.step(STEPS_PER_REV * 2);
    } else {
      motor.step(-STEPS_PER_REV * 2);
    }
  }

  return 20; // Retorna o tempo total de operação
}

int escoamentoAgua() {
  //8 segundos
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Escoar Agua");

  delay(8000);

  return 8;
}

int lavagem(String modo) {
  unsigned long TEMPO_TOTAL;
  unsigned long TEMPO_ALTERNANCIA;
  int velocidade;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Lavagem: ");
  lcd.print(modo);

  if (modo == "Rapido") {
    TEMPO_TOTAL = 30000;        // 30 segundos
    TEMPO_ALTERNANCIA = 10000;  // Alterna a cada 10 segundos
    velocidade = 60;            // 60 RPM
  } else if (modo == "Completo") {
    TEMPO_TOTAL = 60000;        
    TEMPO_ALTERNANCIA = 15000;
    velocidade = 100;
  }

  motor.setSpeed(velocidade);

  unsigned long inicio = millis();

  while (millis() - inicio < TEMPO_TOTAL) {
    unsigned long tempoDecorrido = millis() - inicio;

    if ((tempoDecorrido / TEMPO_ALTERNANCIA) % 2 == 0) {
      // Primeira metade do intervalo: Sentido horário
      motor.step(STEPS_PER_REV * 2);
    } else {
      // Segunda metade do intervalo: Sentido anti-horário
      motor.step(-STEPS_PER_REV * 2);
    }
  }

  return TEMPO_TOTAL / 1000;
}

int enxague() {
  // Tempo total enxague (20 segundos)
  const unsigned long TEMPO_TOTAL = 20000;
  const unsigned long TEMPO_ALTERNANCIA = 10000;
  unsigned long inicio = millis();

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Pre-Lavagem");

  motor.setSpeed(60);

  while (millis() - inicio < TEMPO_TOTAL) {
    unsigned long tempoDecorrido = millis() - inicio;

    if (tempoDecorrido < TEMPO_ALTERNANCIA) {
      // Primeiros 10 segundos: Sentido horário
      motor.step(STEPS_PER_REV * 2);
    } else {
      // Últimos 10 segundos: Sentido anti-horário
      motor.step(-STEPS_PER_REV * 2);
    }
  }

  return 20;
}

int centrifugacao(int rpm) {
  // Configurações
  const unsigned long TEMPO_TOTAL = 30000;
  unsigned long inicio = millis();

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Centrifugacao");

  motor.setSpeed(rpm);

  while (millis() - inicio < TEMPO_TOTAL) {
    motor.step(STEPS_PER_REV * 2);
  }

  return TEMPO_TOTAL / 1000;
}

int secagem(int tempoSecagem) {

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Secagem ativa");
  lcd.setCursor(0, 1);
  lcd.print("Tempo: ");
  lcd.print(tempoSecagem);
  lcd.print("s");

  motor.setSpeed(60);

  unsigned long startTime = millis();

  while (millis() - startTime < tempoSecagem * 1000) {
    motor.step(STEPS_PER_REV);
  }

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Secagem concluida");
  delay(2000);

  return tempoSecagem;
}