#pragma once

#include "hardware/pio.h"
#include "pico_graphics.hpp"
#include "../pico_synth/pico_synth.hpp"

namespace pimoroni {

  class GalacticUnicorn {
  public:
    static const int WIDTH  = 53;
    static const int HEIGHT = 11;

    // pin assignments
    static const uint8_t COLUMN_CLOCK           = 13;
    static const uint8_t COLUMN_DATA            = 14;
    static const uint8_t COLUMN_LATCH           = 15;
    static const uint8_t COLUMN_BLANK           = 16;

    static const uint8_t ROW_BIT_0              = 17;
    static const uint8_t ROW_BIT_1              = 18;
    static const uint8_t ROW_BIT_2              = 19;
    static const uint8_t ROW_BIT_3              = 20;
  
    static const uint8_t LIGHT_SENSOR           = 28;

    static const uint8_t MUTE                   = 22;

    static const uint8_t I2S_DATA               =  9;
    static const uint8_t I2S_BCLK               = 10;
    static const uint8_t I2S_LRCLK              = 11;

    static const uint8_t I2C_SDA                =  4;
    static const uint8_t I2C_SCL                =  5;

    static const uint8_t SWITCH_A               =  0;
    static const uint8_t SWITCH_B               =  1;
    static const uint8_t SWITCH_C               =  3;
    static const uint8_t SWITCH_D               =  6;

    static const uint8_t SWITCH_SLEEP           = 27;

    static const uint8_t SWITCH_VOLUME_UP       =  7;
    static const uint8_t SWITCH_VOLUME_DOWN     =  8;
    static const uint8_t SWITCH_BRIGHTNESS_UP   = 21;
    static const uint8_t SWITCH_BRIGHTNESS_DOWN = 26;

  private:
    static const uint32_t ROW_COUNT = 11;
    static const uint32_t BCD_FRAME_COUNT = 14;
    static const uint32_t BCD_FRAME_BYTES = 60;
    static const uint32_t ROW_BYTES = BCD_FRAME_COUNT * BCD_FRAME_BYTES;
    static const uint32_t BITSTREAM_LENGTH = (ROW_COUNT * ROW_BYTES);
    static const uint SYSTEM_FREQ = 22050;

  private:
    static PIO bitstream_pio;
    static uint bitstream_sm;
    static uint bitstream_sm_offset;

    static PIO audio_pio;
    static uint audio_sm;
    static uint audio_sm_offset;

    uint16_t brightness = 256;
    uint16_t volume = 127;

    // must be aligned for 32bit dma transfer
    alignas(4) uint8_t bitstream[BITSTREAM_LENGTH] = {0};
    const uint32_t bitstream_addr = (uint32_t)bitstream;
    static GalacticUnicorn* unicorn;
    static void dma_complete();

    static const uint NUM_TONE_BUFFERS = 2;
    static const uint TONE_BUFFER_SIZE = 4;
    int16_t tone_buffers[NUM_TONE_BUFFERS][TONE_BUFFER_SIZE] = {0};
    uint current_buffer = 0;

    PicoSynth synth;

    enum PlayMode {
      PLAYING_BUFFER,
      //PLAYING_TONE,
      PLAYING_SYNTH,
      NOT_PLAYING
    };
    PlayMode play_mode = NOT_PLAYING;

  public:
    ~GalacticUnicorn();

    void init();
    static inline void pio_program_init(PIO pio, uint sm, uint offset);

    void clear();

    void update(PicoGraphics *graphics);

    void set_brightness(float value);
    float get_brightness();
    void adjust_brightness(float delta);

    void set_volume(float value);
    float get_volume();
    void adjust_volume(float delta);

    void set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b);

    uint16_t light();

    bool is_pressed(uint8_t button);

    void play_sample(uint8_t *data, uint32_t length);
    void play_synth();
    void stop_playing();
    AudioChannel& synth_channel(uint channel);

  private:
    void partial_teardown();
    void dma_safe_abort(uint channel);
    void next_audio_sequence();
    void populate_next_synth();
  };

}