#include "main_template_full.hh"
#include "utils.hh"
#include <functional>

namespace Heck {
    namespace { // anonymous namespace for internal linkage
        // =============================================================================================
        // INIT
        // =============================================================================================
        ld::DaisySeed seed{};

        static ld::MidiUartHandler midi{};

        Observer<int> pot1{};
        SWTimer scan_pots{};

        // function prototypes
        void audio_callback(
            ld::AudioHandle::InputBuffer in,
            ld::AudioHandle::OutputBuffer out,
            size_t size);
        void midi_realtime_handler(const ld::MidiEvent& msg);

        void init()
        {
            seed.Configure();
            seed.Init(Constants::CPU_BOOST480MHZ);
            seed.StartLog(Constants::Developer::LOG_BLOCKS_BOOT);

            {
                ld::AdcChannelConfig adc_cfg[1];
                adc_cfg[0].InitSingle(ld::DaisySeed::GetPin(Constants::Hardware::PIN_POT_1));

                seed.adc.Init(adc_cfg, 1);
                seed.adc.Start();
            }

            seed.PrintLine("Setting Blocksize: %i", Constants::AUDIO_BUFFERSIZE);
            seed.SetAudioBlockSize(Constants::AUDIO_BUFFERSIZE);

            seed.PrintLine("Setting Samplerate: %i", Constants::AUDIO_SAMPLERATE);
            seed.SetAudioSampleRate(Constants::AUDIO_SAMPLERATE);

            seed.PrintLine("Initializing MIDI");
            ld::MidiUartHandler::Config midi_config{};
            midi.Init(midi_config);

            seed.PrintLine("Starting MIDI Receive");
            midi.StartReceiveRt(&midi_realtime_handler);
            midi.Listen();

            seed.PrintLine("Starting Audio");
            seed.StartAudio(audio_callback);
        }


        // =============================================================================================
        // RUN
        // =============================================================================================

        void audio_callback(ld::AudioHandle::InputBuffer in, ld::AudioHandle::OutputBuffer out, size_t size)
        {
            // Channel 1
            for (size_t i = 0; i < size; i++) {
                out[0][i] = in[0][i];
            }

            // Channel 2
            for (size_t i = 0; i < size; i++) {
                out[1][i] = in[1][i];
            }
        }


        void midi_realtime_handler(const ld::MidiEvent& msg)
        {
            switch (msg.srt_type) {
                case ld::TimingClock: {
                } break;
                case ld::Start: {
                } break;
                case ld::Stop: {
                } break;
                case ld::Reset: {
                } break;
                case ld::Continue: {
                } break;
            }
        }

        void midi_async_handler(const ld::MidiEvent& msg)
        {
            char strbuf[128];
            GetMidiTypeAsString(msg, &strbuf[0]);
            seed.PrintLine("%s", strbuf);
            return;
        }

        void mainloop()
        {
            seed.PrintLine("Entering MainLoop");
            u32 time_boot_ms{};


            scan_pots.set_period(10);
            scan_pots.set_callback([](u32 time_now) {
                pot1.on_change_fuzzy(seed.adc.Get(0), 10, [](int val) {
                    seed.PrintLine("POT_1: %d", val);
                });
            });


            bool heartbeat_led_state{ false };

            SWTimer heartbeat{};
            heartbeat.set_period(100);
            heartbeat.set_callback([&heartbeat_led_state](u32 time_now) {
                heartbeat_led_state = !heartbeat_led_state;
                seed.SetLed(heartbeat_led_state);
                return;
            });

            while (true) {
                time_boot_ms = ld::System::GetNow();

                while (midi.HasEvents()) {
                    midi_async_handler(midi.PopEvent());
                }

                scan_pots.is_it_already_time_again(time_boot_ms);
                heartbeat.is_it_already_time_again(time_boot_ms);
            }
        }
    } // namespace
} // namespace Heck


int main()
{
    Heck::init();
    Heck::mainloop();
}