// https://programmersqtcpp.blogspot.com/2022/01/arduinolcd-testo-scorrevoleadvanced.html
//#include <IRremote.h>
#include <IRremote.hpp>
#include <Wire.h>
#include "U8glib.h"

#include "RTClib.h"
#include <U8x8lib.h>
#include "iapps.h"
#include <avr/sleep.h>

//RTC_DS1307 rtc;
//DateTime dateTime;

#define PIN_RECEIVER 2   // Signal Pin of IR receiver

U8X8_SSD1306_128X64_NONAME_HW_I2C oled(/* reset=*/U8X8_PIN_NONE);

uint32_t uiVolTimer;

struct TextField {
    uint8_t flags;
    uint8_t *font;
    char *buff; 
    uint8_t row;
    uint8_t col;
};

#define INVERT    (1 << 7)
#define IS_SHOW   (1 << 6)
#define DRAW_2x2  (1 << 5)

TextField muteField = {
    .flags = INVERT | 6,
    .font = u8x8_font_8x13_1x2_r,
    .buff = " Mute ",
    .row = 6,
    .col = 9
};

TextField heatingField = {
    .flags = 7,
    .font = u8x8_font_px437wyse700a_2x2_r,
    .buff = "HEATING",
    .row = 2,
    .col = 1
};

TextField volumeField = {
    .flags = 3 | DRAW_2x2,
    .font = u8x8_font_px437wyse700a_2x2_r,
    .buff = nullptr,
    .row = 1,
    .col = 1
};

TextField dBField = {
    .flags = 2,
    .font = u8x8_font_8x13_1x2_r,
    .buff = "dB",
    .row = 3,
    .col = 14
};

TextField sourceMiniField = {
    .flags = 5,
    .font = u8x8_font_8x13_1x2_r,
    .buff = nullptr,
    .row = 6,
    .col = 2
};

TextField workingTimeField = {
    .flags = 8,
    .font = u8x8_font_px437wyse700a_2x2_r,
    //.buff = "71582788",
    .buff = nullptr,
    .row = 2,
    .col = 0
};

GLB_STATE g_state = AT_PWR_OFF;
GLB_STATE g_prevState = AT_PWR_OFF;
GLB_PWR_STATE g_pwrState = PWR_OFF;

uint32_t heatingTimer;
uint32_t saveMillis;

void clearField(TextField &text) {
    uint8_t len = text.flags & 0x0f;
    char ibuff[len + 1];
    ibuff[len] = '\0';
    memset(ibuff, " ", len);
    bool is2x2 = text.flags & DRAW_2x2;
    oled.setFont(text.font);
             
    if (is2x2)
        oled.draw2x2String(text.col, text.row, ibuff);
    else
        oled.drawString(text.col, text.row, ibuff);
}

bool isShowField(TextField &text) {
    return text.flags & IS_SHOW;
}

void setShowField(TextField &text, bool tf) {
    if (tf)
      text.flags |= IS_SHOW;
    else
      text.flags &= ~IS_SHOW;
}

void printField(TextField &text) {
    if (text.buff == nullptr)
        return;

    bool is2x2 = text.flags & DRAW_2x2;
    oled.setFont(text.font);

    oled.setInverseFont(text.flags & INVERT);
    if (is2x2)
        oled.draw2x2String(text.col, text.row, text.buff);
    else
        oled.drawString(text.col, text.row, text.buff);
    oled.setInverseFont(false);
}

void setup() {
  Serial.begin(115200);
  // aggiungendo rtc si presentano problemi di memoria
  /*if (! rtc.begin()) {
      Serial.println("Couldn't find RTC");
      Serial.flush();
      abort();
  }*/
  oled.setBusClock(400000);
  appsInit();
  IrReceiver.begin(PIN_RECEIVER);
  
  oled.begin();
  oled.setPowerSave(0);
  if (appsPwrState()) {
      g_state = AT_PWR_ON;
  } else {
      g_state = AT_PWR_OFF;
  }
  
} // end void setup()

uint32_t workingTime = 0;

void mainState(uint8_t command) {
  static bool saveMute = false;
  switch(g_state) {
    case AT_PWR_ON:
      g_pwrState = PWR_ON;
      setPwrOn();
      oled.setPowerSave(0);
      oled.clearDisplay();

      // !!! CHANGE STATE !!!
      g_state = HEATING;
      appsServiceMute(true);
      uiCurrentInput();
      uiMute();
     
      heatingTimer = millis();
      
      
      break;
    case HEATING:
        if (!isShowField(heatingField)) {
            printField(heatingField);
            setShowField(heatingField, true);
        } else         
        if (millis() - heatingTimer >= HEATING_TIME) {
            // !!! CHANGE STATE !!!
            g_state = RUN;
            Serial.println("Heating end");
            clearField(heatingField);
            setShowField(heatingField, false);
            appsPwrOn();
            uiCurrentInput();
            appsServiceMute(appsLastMute());
            uiMute();
        }
        break;

    case RUN:
        
        if (isShowField(volumeField) && millis() - uiVolTimer > 1000) {  
            uiClearVolume();
            uiCurrentInput();
        } 
        break;

    case AT_PWR_OFF:
        
        if (g_pwrState == PWR_ON) {
          oled.clearDisplay();
          oled.setPowerSave(1);

          // !!! CHANGE STATE !!!
          g_state = AT_SLEEP;
          appsPwrOff();
          appsSetLastWorkTime(workingTime);
          workingTime = 0;
        } else if (millis() - saveMillis > 500) {
           g_state = AT_SLEEP;        
        }
        break;

    case AT_SLEEP:
        g_pwrState = PWR_OFF;
        setPwrOff();
        //set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
        //noInterrupts ();
        //g_pwrState = PWR_OFF;
        // !!! CHANGE STATE !!!
        g_state = AT_PWR_OFF;
        //sleep_enable();
        //interrupts ();
        //sleep_cpu ();
        // questo while simula lo sleep cpu.
        while (digitalRead(2) == HIGH) {
            if (pwrBtn.read()) {
                break;
            }
        }
        saveMillis = millis();
        break;
  }
}

bool wasPressedFor;

void loop() {
    static uint32_t prevTime;
    pwrBtn.read();
    muteBtn.read();
    nextBtn.read();
    prevBtn.read();
    volPlusBtn.read();
    volMinusBtn.read();
    if (g_state == RUN 
            || g_state == AT_PWR_ON
            || g_state == HEATING) {
        // 1000(secondi) o 60000U(minuti)      
        if (millis() - prevTime > 60000U) {     
            // accumula tempo in secondi o minuti
            workingTime++;          
            prevTime = millis();
        }
    }

    uint8_t command = 0;
    // Checks received an IR signal
    if (IrReceiver.decode()) {
        command = IrReceiver.decodedIRData.command;
        //translateIR();
        Serial.println(command);
        IrReceiver.resume();  // Receive the next value
    } else {
        if (!wasPressedFor && pwrBtn.pressedFor(200)) {
            wasPressedFor = true;
            command = CMD_POWER;
        } 
        if (wasPressedFor && pwrBtn.releasedFor(200)) {
            wasPressedFor = false;
        }
        if (nextBtn.wasPressed()) {
            command = CMD_NEXT;
        } 
        if (prevBtn.wasPressed()) {
            command = CMD_PREV;
        } 
        if (volPlusBtn.isPressed(150)) {
            command = CMD_PLUS;
        } 
        if (volMinusBtn.isPressed(150)) {
            command = CMD_MINUS;
        } 
        if (muteBtn.wasPressed()) {
            command = CMD_PLAY;
        }
    }
    
    runCommand(command);
    mainState(command);
} // end void loop()

void clsRow0() {
  oled.setCursor(0, 0);
  oled.print("                ");  // 16 spazi
}

void lcdPrint(char* text)
{
  clsRow0();
  oled.setCursor(0, 0);

  oled.print(text);
  /*oled.print(" (");
  oled.print(receiver.decodedIRData.command);
  oled.print(")");*/
}

void uiClearVolume() {
    
    clearField(volumeField);
    clearField(dBField);
    setShowField(dBField, false);
    setShowField(volumeField, false);
}

void uiCurrentVolume() {
    if (!isShowField(dBField)) {
        printField(dBField);
        setShowField(dBField, true);
    }
    char str[4];
    str[3] = '\0';
    sprintf(str, "%#3d", appsCurrentVolume());
    volumeField.buff = str;
    printField(volumeField);
    setShowField(volumeField, true);
    if (isShowField(volumeField)) 
        uiVolTimer = millis();

}

void uiClearCurrentInput() {
    oled.setFont(u8x8_font_8x13_1x2_r);
    oled.draw2x2String(3, 1, "     ");
}

void uiCurrentInput() {
  clearField(sourceMiniField);
  
  sourceMiniField.buff = appsCurrentInputName();
  printField(sourceMiniField);
  
  if (g_state != RUN)
      return; 
  
  if (isShowField(volumeField)) {
      uiClearVolume();
      setShowField(volumeField, false);
  }
  oled.setFont(u8x8_font_8x13_1x2_r);
  switch (appsCurrentInput()) {
      case 0:

          oled.draw2x2String(3, 1, "PHONO");
          break;
      case 1:
          oled.draw2x2String(2, 1, "  CD ");
          break;
      case 2:
          oled.draw2x2String(2, 1, " AUX1");
          break;
      case 3:
          oled.draw2x2String(2, 1, " AUX2");
          break;
  }
}

void uiMute() {
   
    if (appsMuteState() == true) {
        printField(muteField);
    } else {
        clearField(muteField);
    }
}

void uiWorkingTime() {
    if (!isShowField(workingTimeField)) {
        uint32_t hour = (appsLastWorkTime() + workingTime) / 60;
        uint8_t len = workingTimeField.flags & 0x0f;
        char ibuff[len + 1];
        sprintf(ibuff, "%#8u", hour);
        workingTimeField.buff = ibuff;
        uiClearCurrentInput();
        printField(workingTimeField);
        setShowField(workingTimeField, true);
        
    } else {
        clearField(workingTimeField);
        setShowField(workingTimeField, false);
        uiCurrentInput();
    }
}

void runCommand(uint8_t command) {
  // Takes command based on IR code received
  switch (command) {
    case CMD_POWER:
      if (g_pwrState == PWR_ON) {
          // !!! CHANGE STATE !!!
          g_state = AT_PWR_OFF;
      } else {
          // !!! CHANGE STATE !!!
          g_state = AT_PWR_ON;
      }
      //lcdPrint("POWER");
      break;
    case CMD_MENU:
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("MENU");
      break;
    case CMD_TEST:
      if (isShowField(volumeField))
          return;
      //lcdPrint("TEST");
      uiWorkingTime();
      Serial.println(appsLastWorkTime() + workingTime);
   
      break;
    case CMD_PLUS:
      if (isShowField(workingTimeField))
          return;
      /*if (g_pwrState == PWR_OFF)
          return;*/
      //lcdPrint("PLUS");
      if (isShowField(volumeField))
          appsIncVol();
      uiCurrentVolume();
   
      break;
    case 194:
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("BACK");
      break;
    case CMD_PREV:
      if (isShowField(workingTimeField))
          return;
      /*if (g_pwrState == PWR_OFF)
          return;*/
      //lcdPrint("PREV.");
      if (!isShowField(volumeField))
          appsPrevInput();

      uiCurrentInput();
      break;
    case CMD_PLAY:
      //lcdPrint("PLAY");
      if (g_state == RUN) {
          appsToggleMute();
          uiMute();
      }
      break;
    case CMD_NEXT:
      if (isShowField(workingTimeField))
          return;
      /*if (g_pwrState == PWR_OFF)
          return;*/
      //lcdPrint("NEXT");
      if (!isShowField(volumeField))
          appsNextInput();
      uiCurrentInput();
      
      break;
    case CMD_MINUS:
      if (isShowField(workingTimeField))
          return;
      /*if (g_pwrState == PWR_OFF)
          return;*/
      //lcdPrint("MINUS");
      if (isShowField(volumeField))
          appsDecVol();
      uiCurrentVolume();
            
      break;
    case 176:
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("key: C");
      break;
    case CMD_ZERO:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 0");
      appsSetCurrentInput(0);
      uiCurrentInput();
      break;  
    case CMD_UNO:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 1");
      appsSetCurrentInput(1);
      uiCurrentInput();
      break;
    case CMD_DUE:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 2");
      appsSetCurrentInput(2);
      uiCurrentInput();
      break;
    case CMD_TRE:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 3");
      appsSetCurrentInput(3);
      uiCurrentInput();
      break;
    case CMD_QUATTRO:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 4");
      appsSetCurrentInput(4);
      uiCurrentInput();
      break;
    case CMD_CINQUE:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 5");
      appsSetCurrentInput(5);
      uiCurrentInput();
      break;
    case CMD_SEI:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 6");
      appsSetCurrentInput(6);
      uiCurrentInput();
      break;
    case CMD_SETTE:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 7");
      appsSetCurrentInput(7);
      uiCurrentInput();
      break;
    case 74:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 8");
      appsSetCurrentInput(8);
      uiCurrentInput();
      break;
    case 82:
      if (isShowField(workingTimeField))
          return;
      if (g_pwrState == PWR_OFF)
          return;
      //lcdPrint("num: 9");
      appsSetCurrentInput(9);
      uiCurrentInput();
      break;
    default:
      /*clsRow0();
      oled.setCursor(0, 0);
      oled.print(receiver.decodedIRData.command);
      oled.print(" other button");*/
      break;
  }
}
GND5VSDASCLSQWRTCDS1307+