#include <hardware/pwm.h>
#include "Envelopes.h"
#include "Dsp.h"
#include "Hardware.h"
#include "Kick.h"
#include "Snare.h"
#include "HiHat.h"

// Hardware definitions
#define MODESWITCH_PIN_A 0
#define MODESWITCH_PIN_B 1
#define PUSHBUTTON_PIN 6
#define VOCT_PIN 26
#define FREQ_POT_PIN 27
#define INV_POT_PIN 28
#define CHORD_POT_PIN 29

// KICKDRUM SETTINGS
#define KICK_BASE_FREQ 0.22
#define KICK_FREQ_RANGE 10.0
#define KICK_SIGH_DELAY_MS 200
#define KICK_SIGH_AMOUNT 0.08
#define KICK_SIGH_SPEED 0.0005
#define KICK_SATURATION 2.0

// SNAREDRUM SETTINGS
#define SNARE_BASE_FREQ 0.5
#define SNARE_FREQ_RANGE 40.0
#define SNARE_SIGH_DELAY_MS 100
#define SNARE_SIGH_AMOUNT 0.1
#define SNARE_SIGH_SPEED 0.0004

// HIHAT SETTINGS
#define HIHAT_BASE_FREQ 0.5
#define HIHAT_FREQ_RANGE 20.0

// Create the Instruments
Kick kick;
Snare snare;
HiHat hihat;


// Switch
OnOffOnSwitch mode_switch(MODESWITCH_PIN_A, MODESWITCH_PIN_B);

// Variables that store the read values of the switches and potentiometers
bool gate_high, old_gate_high;
int freq_pot;
int chord_pot;
int inv_pot;
int voct_input;

// Slice of the Hardware Timer
int slice_num = 0;



void setup() {
  Serial.begin(9600);
  // Mode select switch is a On-Off-On switch with the outer pins connected
  // to the pins 0 and 1. 
  mode_switch.setup();

  // Push Button with internal pullup resistor
  pinMode(PUSHBUTTON_PIN, INPUT_PULLUP);
  
  // Setup the kick
  kick.setup();
  kick.base_frequency = KICK_BASE_FREQ;
  kick.frequency_range = KICK_FREQ_RANGE;
  kick.pitch_envelope_sigh.delay = KICK_SIGH_DELAY_MS;
  kick.pitch_envelope_sigh.amount = KICK_SIGH_AMOUNT;
  kick.pitch_envelope_sigh.speed = KICK_SIGH_SPEED;
  kick.setSaturation(KICK_SATURATION);

  // Setup the snare
  snare.setup();
  snare.base_frequency = SNARE_BASE_FREQ;
  snare.frequency_range = SNARE_FREQ_RANGE;
  snare.pitch_envelope_sigh.delay = SNARE_SIGH_DELAY_MS;
  snare.pitch_envelope_sigh.amount = SNARE_SIGH_AMOUNT;
  snare.pitch_envelope_sigh.speed = SNARE_SIGH_SPEED;

  // Setup the hihat
  hihat.setup();
  hihat.base_frequency = HIHAT_BASE_FREQ;
  hihat.frequency_range = HIHAT_FREQ_RANGE;

  // Setup the PWM timer
  gpio_set_function(2, GPIO_FUNC_PWM);// set GP2 function PWM
  slice_num = pwm_gpio_to_slice_num(2);// GP2 PWM slice

  pwm_clear_irq(slice_num);
  pwm_set_irq_enabled(slice_num, true);
  irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
  irq_set_enabled(PWM_IRQ_WRAP, true);

  // Set PWM frequency
  pwm_set_clkdiv(slice_num, 4);
  pwm_set_wrap(slice_num, 1023); // Resolution
  pwm_set_enabled(slice_num, true); // PWM output enable
  
}

void loop() {
  // Store the state of the previous gate for debouncing
  old_gate_high = gate_high;

  // Read the potentiometers and CV Inputs
  freq_pot = analogRead(FREQ_POT_PIN);
  chord_pot = map(analogRead(CHORD_POT_PIN), 0, 1023, 1023, 0);
  inv_pot = map(analogRead(INV_POT_PIN), 0, 1023, 1023, 0);
  voct_input = analogRead(VOCT_PIN);

  // Read Switches and Gates
  mode_switch.read();
  readGates();
  
  // Update Modulation for each instrument (depending on mode)
  if (mode_switch.mode == 0) {
    // Modulation updates for Kick
    kick.setFrequency((float)freq_pot/1023.0);
    kick.setVolumeDecay((float)chord_pot/1023.0);
    kick.setPitchDecay((float)inv_pot/1023.0);
    // Increase the amount of the pitch envelope if the pitch decay is longer
    kick.setPitchAmount(2.0 + (1023.0-(float)inv_pot)/1023.0*8.5);
    kick.update();
  } else if (mode_switch.mode == 1) {
    // Modulation updates for Snare
    snare.setFrequency((float)freq_pot/1023.0);
    snare.setVolumeDecay((float)chord_pot/1023.0);
    snare.setPitchDecay((float)inv_pot/1023.0);
    // Increase the amount of the pitch envelope if the pitch decay is longer
    snare.setPitchAmount((1023.0-(float)inv_pot)/1023.0);
    snare.setLpfFrequency(1023.0-(float)inv_pot);
    snare.update();
  } else if (mode_switch.mode == 2) {
    // Modulation updates for Hihat
    hihat.setFrequency((float)freq_pot/1023.0);
    hihat.setVolumeDecay((float)chord_pot/1023.0);
    hihat.setWavetableStart((1023.0-(float)inv_pot)/1023.0);
    hihat.setHpfFrequency((float)inv_pot/1023.0);
    hihat.update();
  }
}



// Timer that pushes out the actual audio with a fixed frequency
// Code inside this block needs to be _fast_ in order to meet the 
// deadlines, otherwise crackling will be heard
void on_pwm_wrap() {
  // Clear the PWM register
  pwm_clear_irq(slice_num);

  // Set the output to the actual calculated sample
  if (mode_switch.mode == 0){
    // Render the next kick sample and advance the oscillator
    pwm_set_chan_level(slice_num, PWM_CHAN_A, kick.render());
    kick.advanceOscillator();
  } else if (mode_switch.mode == 1){
    // Render the next snare sample and advance the oscillator
    pwm_set_chan_level(slice_num, PWM_CHAN_A, snare.render());
    snare.advanceOscillator();
  } else if (mode_switch.mode == 2){
    // Render the next hihat sample and advance the oscillator
    pwm_set_chan_level(slice_num, PWM_CHAN_A, hihat.render());
    hihat.advanceOscillator();
  }
}

// Reads the push button and V/Oct Input in order to trigger the gate
void readGates() {
  // Read the push button (invert the read value as we us a internal pullup resistor)
  gate_high = !digitalRead(PUSHBUTTON_PIN);

  // If there is a value bigger than 1 on the V/Oct Input, trigger the drum
  if (voct_input > 512) { gate_high = 1; }

  // If the module is first starting, supress any accidental triggering for 500 ms
  if (millis() < 500) { gate_high = 0; }

  // If there is a gate
  if (gate_high == 1) {
    // Trigger only on first rising flank
    if (old_gate_high == 0) {
      // (Re-)Trigger the Instruments
      if (mode_switch.mode == 0){
        kick.trigger();
      } else if (mode_switch.mode == 1){
        snare.trigger();
      } else if (mode_switch.mode == 2){
        hihat.trigger();
      }
    }
  }
}