#include <hardware/pwm.h>
#include "Envelopes.h"
#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
#define DEBUG_MODE false
#define SAMPLES 512

// KICKDRUM SETTINGS
#define KICK_BASE_FREQ 0.15
#define KICK_FREQ_RANGE 2.0
#define KICK_SIGH_DELAY_MS 300
#define KICK_SIGH_AMOUNT 0.02
#define KICK_SIGH_SPEED 0.0004


int wave[SAMPLES];
EnvelopeSimpleLinear kick_volume_envelope;

float pitch_envelope = 0.0;
float pitch_sigh = 0.0;

double last_trigger = 0;

bool gate_high, old_gate_high;//push sw
int freq_pot;
float osc_freq;

int chord_pot;
float volume_decay;

int inv_pot;
float pitch_decay;
float pitch_envelope_amount;
float pitch_sigh_amount = KICK_SIGH_AMOUNT;
float pitch_sigh_speed = KICK_SIGH_SPEED;
float sigh_incrementer = 0.0;

int voct_input;

float osc_progress = 0;
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. 
  pinMode(MODESWITCH_PIN_A, INPUT_PULLUP);
  pinMode(MODESWITCH_PIN_B, INPUT_PULLUP);

  // Push Button
  pinMode(PUSHBUTTON_PIN, INPUT_PULLUP);//push sw
  // delay(1000);

  for (int i = 0; i < SAMPLES; i++) {
    wave[i] = (sin(2 * M_PI * i  / SAMPLES)) * 511;
    // Serial.println(wave[i]);
  }

  //-------------------PWM setting-------------------------------
  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() {
  // put your main code here, to run repeatedly:
  old_gate_high = gate_high;
  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);
  // Serial.println(voct_input);

  osc_freq = KICK_BASE_FREQ + freq_pot/1023.0*KICK_FREQ_RANGE;
  // Bigger decay values make the envelope shorter
  kick_volume_envelope.setSpeed(0.000005 + (float)chord_pot/1023.0/3000.0);
  pitch_decay = 0.00001 + (float)inv_pot/1023.0/300.0;
  // Increase the amount of the pitch envelope if the pitch decay is longer
  pitch_envelope_amount = 0.5 + (1023.0-(float)inv_pot)/1023.0*1.5;

  //  -------------------push sw , play wave-------------------------------
  readGates();
  
  updateKickEnvelopes();
}

void updateKickEnvelopes() {
  // Increment the pitch sigh (a slight rise in pitch after 
  // the initial pitch envelope)
  if ((millis() - last_trigger) > KICK_SIGH_DELAY_MS) {
    sigh_incrementer += pitch_sigh_speed;
    if (sigh_incrementer < 3.1415) {
      pitch_sigh = max(0.0, sin(sigh_incrementer));
    } else {
      pitch_sigh = 0.0;
    }
  }

  // Decrement the pitch envelope
  pitch_envelope -= pitch_decay;
  if (pitch_envelope < 0.0001) {
    pitch_envelope = 0.0;
  }

  kick_volume_envelope.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
void on_pwm_wrap() {
  pwm_clear_irq(slice_num);
  
  // Decrement the volume envelope

  // Set the output to the actual level
  pwm_set_chan_level(slice_num, PWM_CHAN_A, kick_volume_envelope.value*wave[(int)osc_progress]/2+511);

  // Advance the oscillator and reset it if it reached the end
  osc_progress += osc_freq + pitch_envelope + pitch_sigh * pitch_sigh_amount;
  if (osc_progress > SAMPLES) {
    osc_progress = 0;
  }
}

// Reads the push button and V/Oct Input in order to trigger the gate
void readGates() {
  // Read the push button
  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
  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) {
      // Reset the oscillator and the envelopes
      osc_progress = 0;
      last_trigger = millis();
      resetEnvelopes();
    }
  }
}



void resetEnvelopes() {
  kick_volume_envelope.reset();
  pitch_envelope = pitch_envelope_amount;
  pitch_sigh = 0.0;
  sigh_incrementer = 0.0;
}