#include "midi.h"
#include "launcher/launcher.h"
#include "util/logging.h"

namespace overlay::windows {

    static std::string midi_cmd_str(uint8_t cmd) {
        const char *name = "UNKNOWN";
        switch (cmd) {
            case 0x8:
                name = "NOTE OFF";
                break;
            case 0x9:
                name = "NOTE ON";
                break;
            case 0xA:
                name = "POLY.PRESS.";
                break;
            case 0xB:
                name = "CTRL CHANGE";
                break;
            case 0xC:
                name = "PRG CHANGE";
                break;
            case 0xD:
                name = "CHAN.PRESS.";
                break;
            case 0xE:
                name = "PITCH BEND";
                break;
            case 0xF:
                name = "SYSTEM";
                break;
        }
        return fmt::format("{} (0x{:2X})", name, cmd);
    }

    MIDIWindow::MIDIWindow(SpiceOverlay *overlay) : Window(overlay) {
        this->title = "MIDI Control";
        this->init_size = ImVec2(
                ImGui::GetIO().DisplaySize.x * 0.8f,
                ImGui::GetIO().DisplaySize.y * 0.8f);
        this->size_min = ImVec2(250, 200);
        this->init_pos = ImVec2(
                ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
                ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2);
        this->active = true;

        // add hook for receiving midi messages
        RI_MGR->add_callback_midi(this, MIDIWindow::midi_hook);
    }

    MIDIWindow::~MIDIWindow() {
        RI_MGR->remove_callback_midi(this, MIDIWindow::midi_hook);
    }

    void MIDIWindow::build_content() {

        // reset button
        if (ImGui::Button("Reset")) {
            this->midi_data.clear();
        }

        // autoscroll checkbox
        ImGui::SameLine();
        ImGui::Checkbox("Autoscroll", &this->autoscroll);

        // log section
        ImGui::BeginChild("MidiLog", ImVec2(), false);

        // header
        ImGui::Columns(5, "MidiLogColumns", true);
        ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Device"); ImGui::NextColumn();
        ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Command"); ImGui::NextColumn();
        ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Channel"); ImGui::NextColumn();
        ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Data 1"); ImGui::NextColumn();
        ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Data 2"); ImGui::NextColumn();

        // data
        ImGui::Separator();
        for (auto &data : this->midi_data) {

            // set color
            srand(data.device->id * 2111);
            float hue = ((float) rand()) / ((float) RAND_MAX);
            ImGui::PushStyleColor(ImGuiCol_Text, ImColor::HSV(hue, 0.8f, 0.8f, 1.f).Value);

            // data cells
            ImGui::Text("%i: %s", (int) data.device->id, data.device->desc.c_str());
            ImGui::NextColumn();
            ImGui::Text("%s", midi_cmd_str(data.cmd).c_str());
            ImGui::NextColumn();
            ImGui::Text("0x%02X - %i", data.ch, data.ch);
            ImGui::NextColumn();
            ImGui::Text("0x%02X", data.b1);
            ImGui::NextColumn();
            ImGui::Text("0x%02X", data.b2);
            ImGui::NextColumn();

            // clean up
            ImGui::PopStyleColor();
        }

        // autoscroll
        if (this->autoscroll_apply) {
            this->autoscroll_apply = false;
            ImGui::SetScrollHereY(1.f);
        }

        // clean up section
        ImGui::Columns();
        ImGui::EndChild();
    }

    void MIDIWindow::midi_hook(void *user, rawinput::Device *device,
            uint8_t cmd, uint8_t ch, uint8_t b1, uint8_t b2) {
        auto This = (MIDIWindow*) user;
        This->midi_data.emplace_back(MIDIData {
            .device = device,
            .cmd = cmd,
            .ch = ch,
            .b1 = b1,
            .b2 = b2,
        });
        if (This->autoscroll) {
            This->autoscroll_apply = true;
        }
    }
}
