#include "lcd.h"

#include "thermo.h"
#include "controlloop.h"
#include "button.h"
#include <vector>

#include <TFT_eSPI.h> // Include the graphics library
#include "ft6206_touch.h"

FT6206 ts = FT6206();

TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
TFT_eSprite looptime_spr = TFT_eSprite(&tft);

//chart data
TFT_eSprite chartXaxis_spr = TFT_eSprite(&tft);
TFT_eSprite chartYaxis_spr = TFT_eSprite(&tft);
TFT_eSprite chartArea_spr = TFT_eSprite(&tft);
TFT_eSprite reflowstate_spr = TFT_eSprite(&tft);
TFT_eSprite output_spr = TFT_eSprite(&tft);

//buttons
String buttonNames[] = {"START", "STOP", "SET"};
char buttonlabels[BUTTONS_N][10] = {"", "", ""};
TFT_eSPI_Button key[BUTTONS_N];
uint16_t calData[5] = {549, 3080, 343, 3159, 4};

uint32_t lastButtonTime = 0;

void updateTemperature(void)
{
  //draw actualTemperature
  chartArea_spr.fillRoundRect(TEMP_X, 0, 60 - TEMP_R, TEMP_H, TEMP_R, TEMP_BG_COLOR);
  chartArea_spr.drawString("A:", TEMP_X + TEMP_LABEL_X, TEMP_H / 4);
  chartArea_spr.drawString("T:", TEMP_X + TEMP_LABEL_X, TEMP_H / 4 * 3);

  chartArea_spr.drawFloat(getTemperature(), 1, TEMP_X + TEMP_VALUE_X, TEMP_H / 4);
  if (getReflowStatus() == REFLOW_STATUS_OFF)
  {
    chartArea_spr.drawString("--", TEMP_X + TEMP_VALUE_X, TEMP_H / 4 * 3);
  }
  else
  {
    chartArea_spr.drawNumber(getReflowTargetTemp(), TEMP_X + TEMP_VALUE_X, TEMP_H / 4 * 3);
  }
}

uint32_t calcTemp(double temp)
{
  //map(long x, long in_min, long in_max, long out_min, long out_max)
  return CHART_H - map(temp, CHART_TEMP_MIN, CHART_TEMP_MAX, CHART_X_AXIS_OFFSET, CHART_H) + CHART_X_AXIS_OFFSET;
}

uint32_t calcTempY(uint32_t inputY)
{
  return map(inputY, CHART_H, CHART_X_AXIS_OFFSET, CHART_TEMP_MIN, CHART_TEMP_MAX);
}

uint32_t calcTime(uint32_t timeMs)
{
  return map((timeMs / 1000), 0, CHART_TIME_MAX, 0, CHART_W - CHART_Y_AXIS_OFFSET);
}

std::vector<float> temperatureReading;
uint32_t lastReading = 0;
#define TEMPINTERVAL 1000

void updateRealtimeGraph(void)
{
  if (getReflowStatus() == REFLOW_STATUS_OFF)
  {
    temperatureReading.clear();
    return;
  }
  //record temperature
  uint32_t timeNow = millis();
  if (timeNow - lastReading > TEMPINTERVAL)
  {
    temperatureReading.push_back(getTemperature());
    lastReading = timeNow;
  }

  //draw graph
  uint32_t timeIndex = 0;
  uint32_t lastX = 0;
  uint32_t lastY = 0;
  for (auto &&sample : temperatureReading)
  {
    uint32_t nowX = calcTime(timeIndex * TEMPINTERVAL);
    uint32_t nowY = calcTemp(sample);
    chartArea_spr.drawLine(lastX, lastY, nowX, nowY, CHART_ACTUAL_COLOR);
    lastX = nowX;
    lastY = nowY;
    timeIndex++;
  }
}

void updateTargetChart(void)
{

  chartArea_spr.fillSprite(CHART_BG_COLOR);

  //draw preheat
  uint32_t stepTimeStart = 0;
  uint32_t stepTimeStop = calcTime(PREHEAT_PERIOD);
  chartArea_spr.drawLine(stepTimeStart,
                         CHART_H - CHART_X_AXIS_OFFSET,
                         stepTimeStop,
                         calcTemp(TEMPERATURE_SOAK_MIN),
                         CHART_TARGET_COLOR);
  //draw soak
  stepTimeStart = stepTimeStop;
  stepTimeStop += calcTime(SOAK_MICRO_PERIOD * (((TEMPERATURE_SOAK_MAX - TEMPERATURE_SOAK_MIN) / SOAK_TEMPERATURE_STEP) - 1));
  chartArea_spr.drawLine(stepTimeStart,
                         calcTemp(TEMPERATURE_SOAK_MIN),
                         stepTimeStop,
                         calcTemp(TEMPERATURE_SOAK_MAX),
                         CHART_TARGET_COLOR);

  //draw reflow
  stepTimeStart = stepTimeStop;
  stepTimeStop += calcTime(REFLOW_PERIOD);
  chartArea_spr.drawLine(stepTimeStart,
                         calcTemp(TEMPERATURE_SOAK_MAX),
                         stepTimeStop,
                         calcTemp(TEMPERATURE_REFLOW_MAX),
                         CHART_TARGET_COLOR);

  //draw cool
  stepTimeStart = stepTimeStop;
  stepTimeStop += calcTime(COOLDOWN_PERIOD);
  chartArea_spr.drawLine(stepTimeStart,
                         calcTemp(TEMPERATURE_REFLOW_MAX),
                         stepTimeStop,
                         calcTemp(TEMPERATURE_ROOM),
                         CHART_TARGET_COLOR);

  updateRealtimeGraph();

  updateTemperature();
  chartArea_spr.pushSprite(CHART_X + CHART_Y_AXIS_OFFSET, CHART_Y);
}

void prepTargetChart(void)
{
  chartArea_spr.createSprite(CHART_W - CHART_Y_AXIS_OFFSET, CHART_H - CHART_X_AXIS_OFFSET);
  chartArea_spr.setTextFont(TEMP_FONT);
  chartArea_spr.setTextColor(TEMP_F_COLOR);
  chartArea_spr.setTextDatum(CL_DATUM);
  updateTargetChart();
}

void updateReflowState(void)
{
  uint32_t statusColor = STATE_BG_COLOR;
  if (getOverheating())
  {
    statusColor = TFT_RED;
  }
  reflowstate_spr.fillRoundRect(STATE_OFFSET, 0, (STATE_W / 2) - (2 * STATE_OFFSET), STATE_H, STATE_R, statusColor);
  reflowstate_spr.fillRoundRect(STATE_W / 2 + STATE_OFFSET, 0, (STATE_W / 2) - (2 * STATE_OFFSET), STATE_H, STATE_R, STATE_BG_COLOR);
  reflowstate_spr.drawString(getReflowState_str(), STATE_W / 4, STATE_H / 2);
  reflowstate_spr.drawString(getReflowStatus_str(), STATE_W / 4 * 3, STATE_H / 2);
  reflowstate_spr.pushSprite(STATE_X, STATE_Y);
}

void prepReflowstate(void)
{
  reflowstate_spr.createSprite(STATE_W, STATE_H);
  reflowstate_spr.setTextFont(STATE_FONT);
  reflowstate_spr.setTextColor(STATE_F_COLOR);
  reflowstate_spr.setTextDatum(MC_DATUM);
  updateReflowState();
}

void updateOutput(void)
{
  output_spr.fillSprite(OUTPUT_BG);
  if (getOutputState())
  {
    output_spr.fillCircle(OUTPUT_W / 2, OUTPUT_H / 2, OUTPUT_R, OUTPUT_ON_COLOR);
  }
  else
  {
    output_spr.drawCircle(OUTPUT_W / 2, OUTPUT_H / 2, OUTPUT_R, OUTPUT_OFF_COLOR);
  }
  output_spr.pushSprite(OUTPUT_X, OUTPUT_Y);
}

void prepOutput(void)
{
  output_spr.createSprite(OUTPUT_W, OUTPUT_H);
  updateOutput();
}

void prepChart(void)
{
  chartXaxis_spr.createSprite(CHART_W, CHART_X_AXIS_OFFSET);
  chartYaxis_spr.createSprite(CHART_Y_AXIS_OFFSET, CHART_H);
  //draw X-axis
  chartXaxis_spr.drawLine(0, 0, CHART_W, 0, CHART_LINE_COLOR);
  //draw Y-axis
  chartYaxis_spr.drawLine(CHART_Y_AXIS_OFFSET - 1, 0, CHART_Y_AXIS_OFFSET - 1, CHART_H, CHART_LINE_COLOR);

  //draw Y axis labels
  uint16_t tickIndex = (CHART_H - CHART_X_AXIS_OFFSET) / CHART_Y_TICKS;
  chartXaxis_spr.setTextColor(CHART_TEXT_COLOR);
  chartYaxis_spr.setTextColor(CHART_TEXT_COLOR);

  for (int i = 0; i < CHART_Y_TICKS; i++)
  {
    //tick value
    uint16_t y_tick_step = calcTempY(tickIndex * (i + 1)); //CHART_TEMP_MAX - ((CHART_TEMP_MAX - CHART_TEMP_MIN) / CHART_Y_TICKS * i + 1) + CHART_TEMP_MIN;
    chartYaxis_spr.drawLine(CHART_Y_AXIS_OFFSET - 8, tickIndex * (i + 1), CHART_Y_AXIS_OFFSET, tickIndex * (i + 1), CHART_LINE_COLOR);
    chartYaxis_spr.setTextDatum(BR_DATUM);
    chartYaxis_spr.drawString(String(y_tick_step), CHART_Y_AXIS_OFFSET - 3, tickIndex * (i + 1), CHART_FONT);
  }

  //draw X axis labels
  tickIndex = (CHART_W - CHART_Y_AXIS_OFFSET) / CHART_X_TICKS;
  for (int i = 0; i < CHART_X_TICKS + 1; i++)
  {
    uint16_t x_tick_step = (CHART_TIME_MAX / CHART_X_TICKS * (i));
    chartXaxis_spr.drawLine(tickIndex * i + CHART_Y_AXIS_OFFSET - 1, 0, tickIndex * i + CHART_Y_AXIS_OFFSET - 1, 8, CHART_LINE_COLOR);
    chartXaxis_spr.setTextDatum(TR_DATUM);
    chartXaxis_spr.drawString(String(x_tick_step), tickIndex * i + CHART_Y_AXIS_OFFSET - 1, 3, CHART_FONT);
  }
  chartXaxis_spr.pushSprite(CHART_X, CHART_Y + CHART_H - CHART_X_AXIS_OFFSET);
  chartYaxis_spr.pushSprite(CHART_X, CHART_Y);
}

void prepStatus(void)
{
  looptime_spr.createSprite(80, tft.fontHeight(1) + 1);
}

void updateStatus(void)
{
  looptime_spr.fillSprite(TFT_BLACK);
  looptime_spr.setTextDatum(TL_DATUM);
  looptime_spr.setTextColor(TFT_WHITE);
  looptime_spr.drawNumber(getLooptime(), 1, 1, 1);
  looptime_spr.drawFloat(getProcessTime(), 2, 20, 1, 1);

  looptime_spr.pushSprite(1, 1);
}

void prepButtons(void)
{
  const uint32_t button_width = tft.width() / BUTTONS_N;

  for (int i = 0; i < BUTTONS_N; i++)
  {
    key[i].initButtonUL(&tft,
                        (button_width * i) + BUTTON_PADDING,
                        tft.height() - BUTTON_H,
                        button_width - (BUTTON_PADDING * 2),
                        BUTTON_H,
                        TFT_BLUE,        // Outline
                        TFT_SKYBLUE,     // Fill
                        TFT_BLACK,       // Text
                        buttonlabels[i], // 10 Byte Label
                        KEY_TEXTSIZE);

    key[i].setLabelDatum(0, 2, MC_DATUM);
    key[i].drawButton(false, buttonNames[i]);
  }
}

#if defined(TOUCH_CS)
void touch_calibrate()
{
  uint16_t calData[5];
  uint8_t calDataOK = 0;

  // Calibrate
  tft.fillScreen(TFT_BLACK);
  tft.setCursor(20, 0);
  tft.setTextFont(2);
  tft.setTextSize(1);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);

  tft.println("Touch corners as indicated");

  tft.setTextFont(1);
  tft.println();

  tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);

  // Use this calibration code in setup():");
  // uint16_t calData[5] = {   0,0,0,0,0 };
  // tft.setTouch(calData);

  //String calDatastr = "";
  for (uint8_t i = 0; i < 5; i++)
  {
    tft.printf("%d,", calData[i]);
  }

  while (1)
    ;
}
#endif

void updateGUIButtons(void)
{
  uint32_t timeNow = millis();
  if (timeNow - lastButtonTime > BUTTON_INTERVAL)
  {
    uint16_t t_x = 0, t_y = 0;   // To store the touch coordinates
    bool pressed = ts.touched(); //.getTouch(&t_x, &t_y);
    if (pressed)
    {
      TS_Point p = ts.getPoint();
      t_x = p.x;
      t_y = p.y;
    }

    for (uint8_t b = 0; b < BUTTONS_N; b++)
    {
      if (pressed && key[b].contains(t_x, t_y))
      {
        //Serial.println("key pressed");
        key[b].press(true); // tell the button it is pressed
        setButton(b, true);
      }
      else
      {
        //Serial.println("key released");
        key[b].press(false); // tell the button it is NOT pressed
        setButton(b, false);
      }
    }

    // Check if any key has changed state
    for (uint8_t b = 0; b < BUTTONS_N; b++)
    {
      // If button was just pressed, redraw inverted button
      if (key[b].justPressed())
      {
        key[b].drawButton(true, buttonNames[b]);
      }

      // If button was just released, redraw normal color button
      if (key[b].justReleased())
      {
        key[b].drawButton(false, buttonNames[b]);
      }
    }
    lastButtonTime = timeNow;
  }
}

// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
void initTouchScreen(void)
{
  //ts.begin();
  if (!ts.begin(tft.width(), tft.height(), true, 40))
  {
    Serial.println("Unable to start touchscreen.");
  }
  else
  {
    Serial.println("Touchscreen started.");
  }
}

void initLCD(void)
{
  tft.init();

  tft.setRotation(TFT_ROTATION);
  tft.setTextFont(2);
  tft.fillScreen(TFT_BLACK);
  tft.invertDisplay(false);
  #if defined(TOUCH_CS)
  //touch_calibrate();
  tft.setTouch(calData);
  #endif

  initTouchScreen();

  prepStatus();
  prepButtons();
  prepChart();
  prepReflowstate();
  prepTargetChart();
  prepOutput();
}

// -------------------------------------------------------------------------
// Main loop
// -------------------------------------------------------------------------


uint32_t lastLCDupdate = 0;

void handleLCD()
{
  updateStatus();
  uint32_t timeNow = millis();
  if (timeNow - lastLCDupdate > LCD_INTERVAL)
  {
    updateGUIButtons();
    updateReflowState();
    updateTargetChart();
    updateOutput();
    lastLCDupdate = timeNow;
  }
}

void loopScreen(void)
{
}