#include "./calibrate.h"
#include "./home.h"
#include "include/metrics.h"
#include "lib/settings.h"


void CalibrateScreen::onShow()
{
  this->display->setFont(Metrics::LargeFont);
  this->display->setTextSize(Metrics::LargeFontTextSize);

  this->display->fillScreen(Config::ColorHomeBackground);

  this->drawTitle();
  this->height[CalibrateStepCurrent] = Control.getCurrentHeight() + Settings.Height.Offset;
  this->height[CalibrateStepMax] = 1969; // TODO: read min/max from settings
  this->height[CalibrateStepMin] = 0;

  this->drawArrowUp(Metrics::ArrowMargin, Metrics::LargeTextLineHeight + Metrics::LargeTextLineVArrowYOffset, Config::ColorCalibrateIndicators);
  this->drawArrowRight(Metrics::ArrowMargin, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHArrowYOffset, Config::ColorCalibrateIndicators);

  this->display->setTextColor(Config::ColorCalibrateIndicators);
  this->display->setCursor(Metrics::ArrowMargin, Config::DisplayHeight - Metrics::LargeTextLineHeight + Metrics::LargeTextLineYOffset);
  this->display->print("OK");

  this->drawSetHeight();
}


// Credit: https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
uint16_t ipow(uint16_t base, uint8_t exp)
{
    int result = 1;
    for (;;)
    {
        if (exp & 1)
            result *= base;

        exp >>= 1;
        if (!exp)
            break;

        base *= base;
    }

    return result;
}


void CalibrateScreen::onButton(Button button)
{
  switch (button)
  {
    case Button::Top:
    {
      uint16_t height = this->height[this->step];
      uint16_t increment = ipow(10, 3 - this->editingDigit);
      uint16_t modulus = increment * 10;

      uint16_t remainder = height % modulus;
      uint16_t offset = height - remainder;

      height = offset + ((remainder + increment) % modulus);
      if (height > 1999)
        height %= 2000;

      this->height[this->step] = height;
      this->drawSetHeight();
      break;
    }

    case Button::Middle:
    {
      this->editingDigit++;
      if (this->editingDigit > 3)
        this->editingDigit = 0;

      this->drawSetHeight();
      break;
    }

    case Button::Bottom:
    {
      if (!this->isValidHeight())
        return;

      this->step = (CalibrateStep)(this->step + 1);
      if (this->step == CalibrateStepCount)
      {
        // TODO: store new settings
        this->screenManager->show<HomeScreen>();
      }
      else
      {
        this->drawTitle();
        this->drawSetHeight();
      }

      break;
    }
  }
}


void CalibrateScreen::onTick()
{
}


bool CalibrateScreen::isValidHeight()
{
  switch (this->step)
  {
    case CalibrateStepCurrent:
      return this->height[CalibrateStepCurrent] >= Control.getCurrentHeight();

    case CalibrateStepMin:
      return this->height[CalibrateStepMin] <= this->height[CalibrateStepCurrent];

    case CalibrateStepMax:
      return
        this->height[CalibrateStepMax] > this->height[CalibrateStepMin] &&
        this->height[CalibrateStepMax] >= this->height[CalibrateStepCurrent];

    default:
      return true;
  }
}


void CalibrateScreen::drawTitle()
{
  switch (this->step)
  {
    case CalibrateStepCurrent:
      this->drawLargeTextLineCentered("Calibrate", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
      break;

    case CalibrateStepMin:
      this->drawLargeTextLineCentered("Minimum", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
      break;

    case CalibrateStepMax:
      this->drawLargeTextLineCentered("Maximum", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
      break;

    default:
      break;
  }
}


void CalibrateScreen::drawSetHeight()
{
  char heightText[7];
  uint16_t height = this->height[this->step];

  if (height > 999)
    heightText[0] = '0' + ((height / 1000) % 10);
  else
    heightText[0] = '0';

  heightText[1] = '.';
  heightText[2] = '0' + ((height / 100) % 10);
  heightText[3] = '0' + ((height / 10) % 10);
  heightText[4] = '0' + (height % 10);
  heightText[5] = 'm';
  heightText[6] = 0;


  int16_t textX;
  int16_t textY;
  uint16_t textW;
  uint16_t textH;
  this->display->getTextBounds(&heightText[0], 0, 0, &textX, &textY, &textW, &textH);
  textX = (Config::DisplayWidth - textW) / 2;



  uint8_t editingChar = this->editingDigit;

  // Skip the dot
  if (editingChar > 0)
    editingChar++;


  if (this->lastTextWidth > 0)
    this->display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, Metrics::MiddleLargeTextLineY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorCalibrateBackground);

  this->lastTextWidth = textW;

  this->display->setTextColor(this->isValidHeight() ? Config::ColorCalibrateValue : Config::ColorCalibrateInvalidValue);
  this->display->setCursor(textX, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineYOffset);

  // Draw each character ourselves so we can keep track of where the line should be drawn
  for (uint8_t i = 0; i < sizeof(heightText); i++)
  {
    int16_t cursorStart = this->display->getCursorX();

    this->display->print(heightText[i]);

    if (i == editingChar)
      this->display->drawFastHLine(cursorStart, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHeight - 1, this->display->getCursorX() - cursorStart, Config::ColorCalibrateDigitMarker);
  }
}