#include <ctype.h>
#include <cstring>

#include "hardware/i2c.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "hardware/gpio.h"

#include "display.h"
#include <new>


using namespace pico_ssd1306;

// Implement the methods declared in `display.h`.

// Use in-place storage for the SSD1306 object to avoid heap allocation on
// constrained embedded systems. The storage is sized using the complete
// `SSD1306` type (this file includes the concrete header), and the object
// is constructed with placement-new during `init()` and explicitly destroyed
// in the destructor.
namespace {
    alignas(SSD1306) unsigned char display_storage[sizeof(SSD1306)];
    bool display_constructed = false;
}
DisplayManager::DisplayManager()
    : display_(nullptr)
{
    last_echo_[0] = '\0';
}

DisplayManager::~DisplayManager() {
    if (display_constructed && display_) {
        // Call destructor explicitly since we used placement-new.
        display_->~SSD1306();
        display_constructed = false;
    }
    display_ = nullptr;
}

// (No singleton accessor — the DisplayManager is constructed explicitly by callers.)

void DisplayManager::init(uint8_t i2c_addr) {
    if (display_) return; // already initialized

    // Initialize I2C for the SSD1306 display (common pins: SDA=GPIO4, SCL=GPIO5)
    i2c_init(i2c0, 400 * 1000);
    gpio_set_function(4, GPIO_FUNC_I2C);
    gpio_set_function(5, GPIO_FUNC_I2C);
    gpio_pull_up(4);
    gpio_pull_up(5);

    // Construct the display object in pre-allocated storage to avoid heap use.
    display_ = reinterpret_cast<SSD1306 *>(display_storage);
    new (display_) SSD1306(i2c0, i2c_addr, Size::W128xH64);
    display_constructed = true;
    display_->setOrientation(false);
    display_->clear();
    display_->sendBuffer();

    last_echo_[0] = '\0';
    refresh("> ", nullptr);
}

void DisplayManager::refresh(const char *current_input, const char *last_echo) {
    if (!display_) return;
    display_->clear();

    size_t input_len = current_input ? strlen(current_input) : 0;
    const unsigned char *input_font = (input_len > LAST_ECHO_VISIBLE_CHARS) ? font_5x8 : font_12x16;

    pico_ssd1306::drawText(display_, input_font, current_input ? current_input : "", 0, 0);

    const char *echo_to_draw = last_echo ? last_echo : (last_echo_[0] ? last_echo_ : "");
    pico_ssd1306::drawText(display_, font_12x16, echo_to_draw, 0, 20);

    display_->sendBuffer();
}

void DisplayManager::set_last_echo(const char *text) {
    if (!text) return;
    strncpy(last_echo_, text, sizeof(last_echo_) - 1);
    last_echo_[sizeof(last_echo_) - 1] = '\0';
}

// No C-compatible wrappers: prefer `DisplayManager::instance()` API directly.