// Copyright 2012 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "avrlibx/system/init.h"
#include "avrlibx/system/time.h"
#include "avrlibx/system/timer.h"
#include "avrlibx/utils/op.h"

#include "edges/adc_acquisition.h"
#include "edges/audio_buffer.h"
#include "edges/digital_oscillator.h"
#include "edges/hardware_config.h"
#include "edges/midi.h"
#include "edges/midi_handler.h"
#include "edges/settings.h"
#include "edges/timer_oscillator.h"
#include "edges/ui.h"

using namespace avrlibx;
using namespace edges;

MidiIO midi_io;
MidiBuffer midi_in_buffer;
midi::MidiStreamParser<MidiHandler> midi_parser;

GateInputs gate_inputs;

DACSS dac_ss;
AudioDACInterface audio_dac;
ADCSS adc_ss;
ADCInterface adc;

AudioRateTimer audio_rate_timer;

SyncSwitch sync_switch;

TimerOscillator channel_0;
TimerOscillator channel_1;
TimerOscillator channel_2;
TimerOscillator channel_3;

DigitalOscillator channel_4;

// Channel 1 overflow. If sync is enabled, reset the phase of the channel 2.
ISR(TCD0_OVF_vect) {
  Channel2Timer::Restart();
}

volatile uint16_t audio_interrupt_count = 0;

uint8_t channel_remapping[] = { 0, 1, 4, 5, 2, 3, 6, 7};

// Audio rate interrupt (48.048kHz).
ISR(TCE0_OVF_vect) {
  // Shove a sample to the DAC.
  uint16_t sample = audio_buffer.ImmediateRead();
  audio_dac.Strobe();
  Word sample_12bits;
  sample_12bits.value = sample | 0x3000;
  audio_dac.Overwrite(sample_12bits.bytes[1]);
  audio_dac.Overwrite(sample_12bits.bytes[0]);
  
  // Step through the ADC pipeline.
  int8_t result = adc_acquisition.Cycle();
  
  if (result != -1) {
    uint16_t value = adc_acquisition.channel(result);
#ifdef OCTAL_ADC
    value = 4095 - value;
    result = channel_remapping[result];
#endif  // OCTAL_ADC
    ui.set_cv(result, value);
  }
  
  // When a conversion is finished, update the corresponding channel
  // Note that the UI can take over the value read from the DAC, for example
  // When programming an arpeggio
  switch (result) {
    case 0:
      channel_1.UpdatePitch<Channel1Timer>(
          midi_handler.shift_pitch(0,
              settings.dac_to_pitch(0, ui.cv(0))) + 
          settings.dac_to_fm(0, ui.cv(4)),
          settings.pw(0));
      channel_0.SubFollow<Channel0Timer>(channel_1);
      break;
      
    case 1:
      channel_2.UpdatePitch<Channel2Timer>(
          midi_handler.shift_pitch(1,
              settings.dac_to_pitch(1, ui.cv(1))) +
          settings.dac_to_fm(1, ui.cv(5)),
          settings.pw(1));
      break;
    
    case 2:
      channel_3.UpdatePitch<Channel3Timer>(
          midi_handler.shift_pitch(2,
              settings.dac_to_pitch(2, ui.cv(2))) +
          settings.dac_to_fm(2, ui.cv(6)),
          settings.pw(2));
      break;
      
    case 3:
      {
        channel_4.UpdatePitch(
            midi_handler.shift_pitch(3,
                settings.dac_to_pitch(3, ui.cv(3))) +
            settings.dac_to_fm(3, ui.cv(7)),
            settings.shape(3));
#ifndef OCTAL_ADC
        uint8_t cv_controlled_pw_8bit = U16ShiftRight4(ui.cv(3));
        channel_1.set_cv_pw(cv_controlled_pw_8bit);
        channel_2.set_cv_pw(cv_controlled_pw_8bit);
        channel_3.set_cv_pw(cv_controlled_pw_8bit);
#endif  // #ifndef OCTAL_ADC
      }
      break;

#ifdef OCTAL_ADC      
    case 4:
      channel_1.set_cv_pw(U16ShiftRight4(ui.cv(4)));
      break;
    case 5:
      channel_2.set_cv_pw(U16ShiftRight4(ui.cv(5)));
      break;
    case 6:
      channel_3.set_cv_pw(U16ShiftRight4(ui.cv(6)));
      break;
    case 7:
      channel_4.set_cv_pw(U16ShiftRight4(ui.cv(7)));
      break;
#endif  // #ifdef OCTAL_ADC

    default:
      {
        // Otherwise, scan the gate.
        uint8_t gate = ~gate_inputs.value();
        uint8_t midi_gate = midi_handler.gate();
        bool g_1 = (gate & 1) || (midi_gate & 1) || settings.arpeggio(0);
        channel_0.Gate<Channel0>(g_1);
        channel_1.Gate<Channel1>(g_1);
        
        bool g_2 = (gate & 2) || (midi_gate & 2) || settings.arpeggio(1);
        channel_2.Gate<Channel2>(g_2);
        
        bool g_3 = (gate & 4) || (midi_gate & 4) || settings.arpeggio(2);
        channel_3.Gate<Channel3>(g_3);
        
        bool g_4 = (gate & 8) || (midi_gate & 8) || settings.arpeggio(3);
        channel_4.Gate(g_4);
    
        // Read some MIDI bytes, but process them later.
        if (midi_io.readable()) {
          midi_in_buffer.NonBlockingWrite(midi_io.ImmediateRead());
        }
      }
      break;
  }
  ++audio_interrupt_count;
}

inline void Init() {
  SysInit();
  
  sync_switch.set_direction(INPUT);
  sync_switch.set_mode(PORT_MODE_PULL_UP);

  gate_inputs.set_direction(INPUT);
  gate_inputs.set_mode(PORT_MODE_PULL_UP);
  
  ui.Init();
  
  // Reset to factory default if any switch is pressed during boot.
  settings.Init(!(Switches::Read() & 8));
  midi_io.Init();
  
  midi_handler.Init();
  
  audio_dac.Init();
  audio_dac.Strobe();
  audio_dac.Overwrite(0x1f);
  audio_dac.Overwrite(0xff);
  
  channel_0.Init<Channel0, Channel0Timer>();
  channel_1.Init<Channel1, Channel1Timer>();
  channel_2.Init<Channel2, Channel2Timer>();
  channel_3.Init<Channel3, Channel3Timer>();
  channel_4.Init();
  
  adc_acquisition.Init();
  
  audio_rate_timer.set_prescaler(TIMER_PRESCALER_CLK);
  audio_rate_timer.set_period(665); // 48kHz
  audio_rate_timer.set_mode(TIMER_MODE_NORMAL);
  audio_rate_timer.EnableOverflowInterrupt(2);
}

int main(void) {
  Init();
  while (1) {
    // Render audio.
    channel_4.Render();
    
    // Scan MIDI in.
    while (midi_in_buffer.readable()) {
      midi_parser.PushByte(midi_in_buffer.ImmediateRead());
    }
    
    // Do some UI stuff.
    if (audio_interrupt_count >= 48) {
      audio_interrupt_count -= 48;
      uint8_t gate = ~gate_inputs.value();
      // Detect new notes and forward to UI subsystem for calibration functions.
      uint8_t mask = 1;
      for (uint8_t channel = 0; channel < kNumChannels; ++channel) {
        if ((gate & mask) && !(ui.gate() & mask)) {
          settings.StepArpeggio(channel);
        }
        mask <<= 1;
      }
      uint8_t midi_gate = midi_handler.gate();
      ui.set_gate(gate|midi_gate);
      ui.Poll();      
    }
    
    // Check the status of the sync switch
    if (sync_switch.value()) {
      Channel1Timer::EnableOverflowInterrupt(3);
    } else {
      Channel1Timer::DisableOverflowInterrupt();
    }
  }
}
