#include "TBeamDisplay.h"

#ifndef OLED_SDA
#define OLED_SDA 17
#endif

#ifndef OLED_SCL
#define OLED_SCL 18
#endif

#ifndef OLED_ADDR
#define OLED_ADDR 0x3C
#endif

namespace tbeam {

TBeamDisplay::TBeamDisplay(TwoWire& wire) : wire_(wire) {}

bool TBeamDisplay::begin(const DisplayConfig& config) {
  config_ = config;
  clearError();

  if (config_.sda < 0) {
    config_.sda = OLED_SDA;
  }
  if (config_.scl < 0) {
    config_.scl = OLED_SCL;
  }
  if (config_.address == 0) {
    config_.address = OLED_ADDR;
  }

  if (config_.beginWire) {
    wire_.begin(config_.sda, config_.scl);
  }

  oled_.setI2CAddress(config_.address << 1);
  if (!oled_.begin()) {
    ready_ = false;
    setError("OLED begin failed");
    return false;
  }

  ready_ = true;
  setPowerSave(config_.powerSave);
  setFont(DisplayFont::NORMAL);
  clear();
  return true;
}

void TBeamDisplay::update() {
}

void TBeamDisplay::clear() {
  if (!ready_) {
    return;
  }
  oled_.clearBuffer();
  oled_.sendBuffer();
}

void TBeamDisplay::clearBuffer() {
  for (uint8_t i = 0; i < kMaxLines; ++i) {
    lines_[i][0] = '\0';
  }
}

void TBeamDisplay::setPowerSave(bool enabled) {
  if (!ready_) {
    return;
  }
  oled_.setPowerSave(enabled ? 1 : 0);
  powerSave_ = enabled;
}

void TBeamDisplay::setFont(DisplayFont font) {
  font_ = font;
  if (ready_) {
    oled_.setFont(fontFor(font_));
  }
}

void TBeamDisplay::showLines(const char* l1,
                             const char* l2,
                             const char* l3,
                             const char* l4,
                             const char* l5,
                             const char* l6) {
  setLine(0, l1);
  setLine(1, l2);
  setLine(2, l3);
  setLine(3, l4);
  setLine(4, l5);
  setLine(5, l6);
  renderLines();
}

void TBeamDisplay::setLine(uint8_t index, const char* text) {
  if (index >= kMaxLines) {
    return;
  }
  strlcpy(lines_[index], text ? text : "", sizeof(lines_[index]));
}

void TBeamDisplay::renderLines(uint8_t lineCount) {
  if (!ready_) {
    return;
  }

  if (lineCount > kMaxLines) {
    lineCount = kMaxLines;
  }

  oled_.clearBuffer();
  oled_.setFont(fontFor(font_));

  uint8_t yStart = 12;
  uint8_t yStep = 12;
  if (font_ == DisplayFont::SMALL) {
    yStart = 10;
    yStep = 10;
  } else if (font_ == DisplayFont::LARGE) {
    yStart = 15;
    yStep = 16;
    if (lineCount > 4) {
      lineCount = 4;
    }
  }

  for (uint8_t i = 0; i < lineCount; ++i) {
    if (lines_[i][0] == '\0') {
      continue;
    }
    oled_.drawUTF8(0, yStart + (i * yStep), lines_[i]);
  }
  oled_.sendBuffer();
}

void TBeamDisplay::appendLine(const char* text) {
  for (uint8_t i = 0; i < kMaxLines - 1; ++i) {
    strlcpy(lines_[i], lines_[i + 1], sizeof(lines_[i]));
  }
  setLine(kMaxLines - 1, text);
  renderLines();
}

void TBeamDisplay::showBoot(const char* title, const char* subtitle, const char* detail) {
  setFont(DisplayFont::NORMAL);
  showLines(title ? title : "T-Beam", subtitle, detail);
}

void TBeamDisplay::showStatus(const char* title, const char* left, const char* right, const char* footer) {
  if (!ready_) {
    return;
  }

  oled_.clearBuffer();
  oled_.setFont(u8g2_font_6x10_tf);
  if (title) {
    oled_.drawUTF8(0, 10, title);
  }
  oled_.drawHLine(0, 13, 128);

  oled_.setFont(u8g2_font_7x14B_tf);
  if (left) {
    oled_.drawUTF8(0, 34, left);
  }
  if (right) {
    const int width = oled_.getUTF8Width(right);
    int x = 128 - width;
    if (x < 0) {
      x = 0;
    }
    oled_.drawUTF8(x, 34, right);
  }

  oled_.setFont(u8g2_font_6x10_tf);
  if (footer) {
    oled_.drawUTF8(0, 60, footer);
  }
  oled_.sendBuffer();
}

void TBeamDisplay::setError(const char* message) {
  strlcpy(lastError_, message ? message : "", sizeof(lastError_));
}

void TBeamDisplay::clearError() {
  lastError_[0] = '\0';
}

const uint8_t* TBeamDisplay::fontFor(DisplayFont font) const {
  switch (font) {
    case DisplayFont::SMALL:
      return u8g2_font_5x8_tf;
    case DisplayFont::LARGE:
      return u8g2_font_7x14B_tf;
    case DisplayFont::NORMAL:
    default:
      return u8g2_font_6x10_tf;
  }
}

}  // namespace tbeam
