#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeSerifBold24pt7b.h>
#include "eink.hpp"
#include "logger.h"

extern logging::Logger logger;

Eink::Eink(int csPin, int dcPin, int rstPin, int busyPin)
    : eink(GxEPD2_213_BN(csPin, dcPin, rstPin, busyPin)), u8g2Fonts()
{
}

void Eink::setup_eink()
{

  SPI.begin(EPD_SCLK, EPD_MISO, EPD_MOSI);
  eink.init(115200, true, 2, false);
  eink.setRotation(1);
  eink.fillScreen(GxEPD_WHITE);
  eink.setTextColor(GxEPD_BLACK);
  eink.setFullWindow();
  u8g2Fonts.begin(eink);
  u8g2Fonts.setFont(u8g2_font_7x13_tf);
  u8g2Fonts.setForegroundColor(GxEPD_BLACK); // apply Adafruit GFX color
  u8g2Fonts.setBackgroundColor(GxEPD_WHITE);

  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "Display init done!");
}

void Eink::show_display(String header, int wait)
{
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "One line: %s", header.c_str());
  eink.setFont(&FreeMonoBold9pt7b);
  eink.setCursor(0, 10);
  eink.println(header);
  eink.println("Line 2");
  eink.println("Line 3");
  eink.println("Line 4");
  eink.display();
  delay(wait);

  int16_t x1, y1;
  uint16_t w, h;
  eink.getTextBounds(header, 0, 0, &x1, &y1, &w, &h);
}

void Eink::show_temp(float temperature)
{
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "Show temp: %f", temperature);

  int textX = 50;
  //u8g2Fonts.setFont(u8g2_font_fur42_tf);
  u8g2Fonts.setFont(u8g2_font_inb46_mf);

  drawString(textX, 70, String(temperature, 1) + "°", LEFT);
  drawMercury(temperature);
}

void Eink::show_internal_temp(int x, int y, float temperature)
{
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "Show internal temp: %f", temperature);
  u8g2Fonts.setFont(u8g2_font_7x13_tf);
  drawString(x + 60, y - 11, String(temperature, 1) + "°", LEFT);
}
void Eink::show_count(int x, int y, float cnt, String unit)
{
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "Show cnt: %f", cnt);
  u8g2Fonts.setFont(u8g2_font_7x13_tf);
  drawString(x + 60, y - 11, unit + String(cnt, 1), LEFT);
}
void Eink::drawSignalBars(int x, int y, int percentage)
{
  // Define stapler properties
  const int staplerWidth = 4;
  const int staplerHeight = 14;
  const int staplerSpacing = 2;
  int numBars = percentage / 20;

  // Limit numBars to the maximum (5 staplers)
  numBars = min(numBars, 5); // Ensure no more than 5 staplers are drawn
  int staplerX;
  int currentHeight;
  int staplerY;
  // Loop to draw staplers with variable heights and bottom alignment
  for (int i = 0; i < numBars; i++)
  {
    // Calculate x-position for each stapler
    staplerX = x + i * (staplerWidth + staplerSpacing);

    // Calculate stapler height based on the number of bars (1 to 5)
    currentHeight = staplerHeight * (i + 1) / 6; // Scales from 1/5 to full height

    // Calculate y-position for bottom alignment
    staplerY = y - currentHeight; // Subtract height for bottom placement
    // Draw the stapler body (rectangle)
    eink.fillRect(staplerX, staplerY, staplerWidth, currentHeight, GxEPD_BLACK);
  }

  drawString(staplerX + staplerWidth + 2, staplerY, String(percentage) + "%", LEFT);
}

void Eink::display(bool partialupgrade)
{
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "Update display");

  eink.display(partialupgrade);
}

void Eink::drawMercury(float temperature)
{
  int x = 20;
  int y = 30;
  int height = 60;
  int width = 10;
  int mercuryLevel = map(temperature, 5.0, 35.0, 10, height);
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "mercuryLevel: %d", mercuryLevel);

  int radius = 14;
  eink.drawRect(x, y - radius + 2, width, height, GxEPD_BLACK);

  eink.drawCircle(x + (width / 2), y + height, radius, GxEPD_BLACK);
  eink.fillCircle(x + (width / 2), y + height, radius - 2, GxEPD_BLACK);

  eink.fillRect(
      x + 2,
      y + height - mercuryLevel - radius + 2,
      width - 4,
      mercuryLevel,
      GxEPD_BLACK);
}

void Eink::drawBattery(int x, int y, int percentage)
{
  logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "EINK", "drawBattery: %d %", percentage);
  eink.drawRect(x + 15, y - 12, 19, 10, GxEPD_BLACK);
  eink.fillRect(x + 34, y - 10, 2, 5, GxEPD_BLACK);
  eink.fillRect(x + 17, y - 10, 15 * percentage / 100.0, 6, GxEPD_BLACK);
  drawString(x + 60, y - 11, String(percentage) + "%", RIGHT);
}

void Eink::drawString(int x, int y, String text, alignmentType alignment)
{
  int16_t x1, y1; // the bounds of x,y and w and h of the variable 'text' in pixels.
  uint16_t w, h;
  eink.setTextWrap(false);
  eink.getTextBounds(text, x, y, &x1, &y1, &w, &h);
  if (alignment == RIGHT)
    x = x - w;
  if (alignment == CENTER)
    x = x - w / 2;
  u8g2Fonts.setCursor(x + 2, y + h);
  u8g2Fonts.print(text);
}