#include "display.h"

#include <stddef.h>
#include <stdio.h>

#include "main.h"
#include "ssd1306.h"
#include "ssd1306_conf.h"

#define xstr(a) str(a)
#define str(a) #a

static const char *err_msg(ErrReason err) {
  switch (err) {
  case ERR_NONE:
    return "UNKNOWN ERR";
  case ERR_BAT_GT_CHARGE:
    return "VBAT > VPSU";
  case ERR_BAT_LT_DISCHARGE:
    return "VBAT < VLOAD";
  case ERR_VBAT_TOO_LOW:
    return "VBAT < " xstr(V_MIN_VOLT);
  case ERR_VCDC_TOO_LOW:
    if (state == STATE_CHARGE || state == STATE_ERR_CHARGE ||
        state == STATE_SETUP_CHARGE) {
      return "VPSU < " xstr(V_MIN_VOLT);
    } else {
      return "VLOAD < " xstr(V_MIN_VOLT);
    }
  case ERR_OVERVOLTAGE:
    return "OVERVOLTAGE";
  case ERR_UNDERVOLTAGE:
    return "UNDERVOLTAGE";
  }
  return "UNKNOWN ERROR";
}

void display_init() {
  ssd1306_Init();
  display_update_voltage(v_thresh);
  ssd1306_WriteChar('V', Font_16x26, White);
  display_update_state();
  ssd1306_UpdateScreen();
}

void display_update_voltage(int32_t v) {
  char voltage_buf[7];
  char *ptr = voltage_buf;
  ptr += snprintf(ptr, 4, "%3d", v / 100);
  snprintf(ptr, 4, ".%02d", v % 100);
  ssd1306_SetCursor(0, 38);
  ssd1306_WriteString(voltage_buf, Font_16x26, White);
}

void display_update_state() {
  switch (state) {
  case STATE_SETUP_CHARGE:
    display_update_voltage(v_thresh);
    ssd1306_FillRectangle(0, 0, SSD1306_WIDTH - 1, 30, Black);
    ssd1306_SetCursor(0, 0);
    ssd1306_WriteString("Charge", Font_6x8, White);
    ssd1306_SetCursor(0, 10);
    ssd1306_WriteString("Press to close relay", Font_6x8, White);
    break;
  case STATE_SETUP_DISCHARGE:
    display_update_voltage(v_thresh);
    ssd1306_FillRectangle(0, 0, SSD1306_WIDTH - 1, 30, Black);
    ssd1306_SetCursor(0, 0);
    ssd1306_WriteString("Discharge", Font_6x8, White);
    ssd1306_SetCursor(0, 10);
    ssd1306_WriteString("Press to close relay", Font_6x8, White);
    break;
  case STATE_CHARGE:
    display_update_voltage(v_bat);
    ssd1306_FillRectangle(0, 0, SSD1306_WIDTH - 1, 30, Black);
    ssd1306_SetCursor(0, 0);
    ssd1306_WriteString("CHARGING", Font_11x18, White);
    break;
  case STATE_DISCHARGE:
    display_update_voltage(v_bat);
    ssd1306_FillRectangle(0, 0, SSD1306_WIDTH - 1, 30, Black);
    ssd1306_SetCursor(0, 0);
    ssd1306_WriteString("DISCHARGING", Font_11x18, White);
    break;
  case STATE_ERR_CHARGE:
    display_update_voltage(v_bat);
    ssd1306_FillRectangle(0, 0, SSD1306_WIDTH - 1, 30, White);
    ssd1306_FillRectangle(3, 3, SSD1306_WIDTH - 1 - 3, 30 - 3, Black);
    ssd1306_SetCursor(10, 5);
    ssd1306_WriteString("ERROR:", Font_6x8, White);
    ssd1306_SetCursor(10, 15);
    ssd1306_WriteString(err_msg(error), Font_6x8, White);
    break;
  case STATE_ERR_DISCHARGE:
    display_update_voltage(v_bat);
    ssd1306_FillRectangle(0, 0, SSD1306_WIDTH - 1, 30, White);
    ssd1306_FillRectangle(3, 3, SSD1306_WIDTH - 1 - 3, 30 - 3, Black);
    ssd1306_SetCursor(10, 5);
    ssd1306_WriteString("ERROR:", Font_6x8, White);
    ssd1306_SetCursor(10, 15);
    ssd1306_WriteString(err_msg(error), Font_6x8, White);
    break;
  }
}

void display_update() {
  display_update_state();
  ssd1306_UpdateScreen();
}
