// Copyright 2015 Emilie Gillet.
// 
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// 
// See http://creativecommons.org/licenses/MIT/ for more information.

#include <stm32f4xx_conf.h>

#include "marbles/drivers/clock_inputs.h"
#include "marbles/drivers/dac.h"
#include "marbles/drivers/debug_pin.h"
#include "marbles/drivers/debug_port.h"
#include "marbles/drivers/gate_outputs.h"
#include "marbles/drivers/rng.h"
#include "marbles/drivers/system.h"

#include "marbles/ramp/ramp_extractor.h"
#include "marbles/random/random_generator.h"
#include "marbles/random/random_stream.h"
#include "marbles/random/t_generator.h"
#include "marbles/random/x_y_generator.h"

#include "marbles/clock_self_patching_detector.h"
#include "marbles/cv_reader.h"
#include "marbles/io_buffer.h"
#include "marbles/note_filter.h"
#include "marbles/resources.h"
#include "marbles/scale_recorder.h"
#include "marbles/settings.h"
#include "marbles/ui.h"

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/hysteresis_quantizer.h"
#include "stmlib/dsp/units.h"

#define PROFILE_INTERRUPT 0
#define PROFILE_RENDER 0

using namespace marbles;
using namespace std;
using namespace stmlib;

const bool test_adc_noise = false;

const int kSampleRate = 32000;
const int kGateDelay = 2;

ClockInputs clock_inputs;
ClockSelfPatchingDetector self_patching_detector[kNumGateOutputs];
CvReader cv_reader;
Dac dac;
DebugPort debug_port;
GateOutputs gate_outputs;
HysteresisQuantizer deja_vu_length_quantizer;
IOBuffer io_buffer;
NoteFilter note_filter;
Rng rng;
ScaleRecorder scale_recorder;
Settings settings;
Ui ui;

RandomGenerator random_generator;
RandomStream random_stream;
TGenerator t_generator;
XYGenerator xy_generator;

// Default interrupt handlers.
extern "C" {

int __errno;

void NMI_Handler() { }
void HardFault_Handler() { while (1); }
void MemManage_Handler() { while (1); }
void BusFault_Handler() { while (1); }
void UsageFault_Handler() { while (1); }
void SVC_Handler() { }
void DebugMon_Handler() { }
void PendSV_Handler() { }

void SysTick_Handler() {
  IWDG_ReloadCounter();
  ui.Poll();
  if (settings.freshly_baked()) {
    if (debug_port.readable()) {
      uint8_t command = debug_port.Read();
      uint8_t response = ui.HandleFactoryTestingRequest(command);
      debug_port.Write(response);
    }
  }
}

}

IOBuffer::Slice FillBuffer(size_t size) {
  if (PROFILE_INTERRUPT) {
    TIC;
  }
  IOBuffer::Slice s = io_buffer.NextSlice(size);
  
  gate_outputs.Write(s);
  clock_inputs.Read(s, size);

  if (io_buffer.new_block()) {
    cv_reader.Copy(&s.block->adc_value[0]);
    clock_inputs.ReadNormalization(s.block);
  }

  if (rng.readable()) {
    random_stream.Write(rng.data());
  }

  if (PROFILE_INTERRUPT) {
    TOC;
  }
  
  return s;
}

inline uint16_t DacCode(int index, float voltage) {
  CONSTRAIN(voltage, -5.0f, 5.0f);
  const float scale = settings.calibration_data().dac_scale[index];
  const float offset = settings.calibration_data().dac_offset[index];
  return ClipU16(static_cast<int32_t>(voltage * scale + offset));
}

void ProcessTest(IOBuffer::Block* block, size_t size) {
  float parameters[kNumParameters];
  static float phase;
  cv_reader.Process(&block->adc_value[0], parameters);
  for (size_t i = 0; i < size; ++i) {
    phase += 100.0f / static_cast<float>(kSampleRate);
    if (phase >= 1.0f) {
      phase -= 1.0f;
    }
    block->cv_output[0][i] = DacCode(
        0, 4.0 * Interpolate(lut_sine, phase, 256.0f));
    block->cv_output[1][i] = DacCode(
        1, -8.0f * phase + 4.0f);
    block->cv_output[2][i] = DacCode(
        2, (phase < 0.5f ? phase : 1.0f - phase) * 16.0f - 4.0f);
    block->cv_output[3][i] = DacCode(
        3, phase < 0.5f ? -4.0f : 4.0f);

    for (int j = 0; j < 4; ++j) {
      uint16_t dac_code = ui.output_test_forced_dac_code(j);
      if (dac_code) {
        block->cv_output[j][i] = dac_code;
      }
    }
    
    block->gate_output[0][i] = block->input_patched[0]
        ? block->input[0][i]
        : phase < 0.2f;
    block->gate_output[1][i] = phase < 0.5f;
    block->gate_output[2][i] = block->input_patched[1]
        ? block->input[1][i]
        : phase < 0.8f;
  }
}

Ratio y_divider_ratios[] = {
  { 1, 64 },
  { 1, 48 },
  { 1, 32 },
  { 1, 24 },
  { 1, 16 },
  { 1, 12 },
  { 1, 8 },
  { 1, 6 },
  { 1, 4 },
  { 1, 3 },
  { 1, 2 },
  { 1, 1 },
};

int loop_length[] = {
  1,
  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
  5, 5, 5, 5,
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  7, 7,
  8, 8, 8, 8, 8, 8, 8, 8, 8,
  10, 10, 10,
  12, 12, 12, 12, 12, 12, 12,
  14,
  16
};
float parameters[kNumParameters];
float ramp_buffer[kBlockSize * 4];
bool gates[kBlockSize * 2];
float voltages[kBlockSize * 4];
Ramps ramps;
GroupSettings x, y;
bool gate_delay_tail[kNumGateOutputs][kGateDelay];

float SineOscillator(float voltage) {
  static float phase = 0.0f;
  CONSTRAIN(voltage, -5.0f, 5.0f);
  float frequency = stmlib::SemitonesToRatio(voltage * 12.0f) * 220.0f / kSampleRate;
  phase += frequency;
  if (phase >= 1.0f) {
    phase -= 1.0f;
  }
  return 5.0f * Interpolate(lut_sine, phase, 256.0f);
}

void Process(IOBuffer::Block* block, size_t size) {
  if (PROFILE_RENDER) {
    TIC;
  }

  // Filter CV values (3.5%)
  cv_reader.Process(&block->adc_value[0], parameters);
  
  float deja_vu = parameters[ADC_CHANNEL_DEJA_VU_AMOUNT];
  
  //  Deadband near 12 o'clock for the deja vu parameter.
  const float d = fabsf(deja_vu - 0.5f);
  if (d > 0.03f) {
    ui.set_deja_vu_lock(false);
  } else if (d < 0.02f) {
    ui.set_deja_vu_lock(true);
  }
  if (deja_vu < 0.47f) {
    deja_vu *= 1.06382978723f;
  } else if (deja_vu > 0.53f) {
    deja_vu = 0.5f + (deja_vu - 0.53f) * 1.06382978723f;
  } else {
    deja_vu = 0.5f;
  }
  
  GateFlags* t_clock = block->input[0];
  GateFlags* xy_clock = block->input[1];
  
  // Determine the clock source for the XY section (2%)
  ClockSource xy_clock_source = CLOCK_SOURCE_INTERNAL_T1_T2_T3;
  if (block->input_patched[1]) {
    xy_clock_source = CLOCK_SOURCE_EXTERNAL;
    size_t best_score = 8;
    for (size_t i = 0; i < kNumGateOutputs; ++i) {
      size_t score = self_patching_detector[i].Process(block, size);
      if (score >= best_score) {
        xy_clock_source = ClockSource(CLOCK_SOURCE_INTERNAL_T1 + i);
        best_score = score;
      }
    }
  }

  // Generate gates for T-section (16%).
  ramps.master = &ramp_buffer[0];
  ramps.external = &ramp_buffer[kBlockSize];
  ramps.slave[0] = &ramp_buffer[kBlockSize * 2];
  ramps.slave[1] = &ramp_buffer[kBlockSize * 3];
  
  const State& state = settings.state();
  int deja_vu_length = deja_vu_length_quantizer.Lookup(
      loop_length,
      parameters[ADC_CHANNEL_DEJA_VU_LENGTH],
      sizeof(loop_length) / sizeof(int));
  
  t_generator.set_model(TGeneratorModel(state.t_model));
  t_generator.set_range(TGeneratorRange(state.t_range));
  t_generator.set_rate(parameters[ADC_CHANNEL_T_RATE]);
  t_generator.set_bias(parameters[ADC_CHANNEL_T_BIAS]);
  t_generator.set_jitter(parameters[ADC_CHANNEL_T_JITTER]);
  t_generator.set_deja_vu(
      state.t_deja_vu == DEJA_VU_LOCKED
          ? 0.5f
          : (state.t_deja_vu == DEJA_VU_ON ? deja_vu : 0.0f));
  t_generator.set_length(deja_vu_length);
  t_generator.set_pulse_width_mean(float(state.t_pulse_width_mean) / 256.0f);
  t_generator.set_pulse_width_std(float(state.t_pulse_width_std) / 256.0f);
  t_generator.Process(
      block->input_patched[0],
      t_clock,
      ramps,
      gates,
      size);
        
  // Generate voltages for X-section (40%).
  float note_cv_1 = cv_reader.channel(ADC_CHANNEL_X_SPREAD).scaled_raw_cv();
  float note_cv_2 = cv_reader.channel(ADC_CHANNEL_X_SPREAD_2).scaled_raw_cv();
  float note_cv = 0.5f * (note_cv_1 + note_cv_2);
  float u = note_filter.Process(0.5f * (note_cv + 1.0f));
  
  if (test_adc_noise) {
    static float note_lp = 0.0f;
    float note = note_cv_1;
    ONE_POLE(note_lp, note, 0.0001f);
    float cents = (note - note_lp) * 1200.0f * 5.0f;
    fill(&voltages[0], &voltages[4 * size], cents);
  } else if (ui.recording_scale()) {
    float voltage = (u - 0.5f) * 10.0f;
    for (size_t i = 0; i < size; ++i) {
      GateFlags gate = block->input_patched[1]
          ? block->input[1][i]
          : GATE_FLAG_LOW;
      if (gate & GATE_FLAG_RISING) {
        scale_recorder.NewNote(voltage);
      }
      if (gate & GATE_FLAG_HIGH) {
        scale_recorder.UpdateVoltage(voltage);
      }
      if (gate & GATE_FLAG_FALLING) {
        scale_recorder.AcceptNote();
      }
    }
    fill(&voltages[0], &voltages[4 * size], voltage);
  } else {
    x.control_mode = ControlMode(state.x_control_mode);
    x.voltage_range = VoltageRange(state.x_range % 3);
    x.register_mode = state.x_register_mode;
    x.register_value = u;
    cv_reader.set_attenuverter(
        ADC_CHANNEL_X_SPREAD, state.x_register_mode ? 0.5f : 1.0f);
  
    x.spread = parameters[ADC_CHANNEL_X_SPREAD];
    x.bias = parameters[ADC_CHANNEL_X_BIAS];
    x.steps = parameters[ADC_CHANNEL_X_STEPS];
    x.deja_vu = state.x_deja_vu == DEJA_VU_LOCKED
        ? 0.5f
        : (state.x_deja_vu == DEJA_VU_ON ? deja_vu : 0.0f);
    x.length = deja_vu_length;
    x.ratio.p = 1;
    x.ratio.q = 1;
  
    y.control_mode = CONTROL_MODE_IDENTICAL;
    y.voltage_range = VoltageRange(state.y_range);
    y.register_mode = false;
    y.register_value = 0.0f;
    y.spread = float(state.y_spread) / 256.0f;
    y.bias = float(state.y_bias) / 256.0f;
    y.steps = float(state.y_steps) / 256.0f;
    y.deja_vu = 0.0f;
    y.length = 1;
    y.ratio = y_divider_ratios[
        static_cast<uint16_t>(state.y_divider) * 12 >> 8];
    
    if (settings.dirty_scale_index() != -1) {
      int i = settings.dirty_scale_index();
      xy_generator.LoadScale(i, settings.persistent_data().scale[i]);
      settings.set_dirty_scale_index(-1);
    }
    
    y.scale_index = x.scale_index = state.x_scale;

    xy_generator.Process(
        xy_clock_source,
        x,
        y,
        xy_clock,
        ramps,
        voltages,
        size);
  }
  
  const float* v = voltages;
  const bool* g = gates;
  
  for (size_t i = 0; i < size; ++i) {
    block->cv_output[1][i] = DacCode(1, *v++);
    block->cv_output[2][i] = DacCode(2, *v++);
    block->cv_output[3][i] = DacCode(3, *v++);
    block->cv_output[0][i] = DacCode(0, *v++);
    block->gate_output[0][i + kGateDelay] = *g++;
    block->gate_output[1][i + kGateDelay] = ramps.master[i] < 0.5f;
    block->gate_output[2][i + kGateDelay] = *g++;
  }
  
  for (size_t i = 0; i < kNumGateOutputs; ++i) {
    for (size_t j = 0; j < kGateDelay; ++j) {
      block->gate_output[i][j] = gate_delay_tail[i][j];
      gate_delay_tail[i][j] = block->gate_output[i][size + j];
    }
  }
  
  if (PROFILE_RENDER) {
    TOC;
  }
}

void Init() {
  System sys;
  sys.Init(true);
  settings.Init();
  
  clock_inputs.Init();
  dac.Init(kSampleRate, 1);
  rng.Init();
  note_filter.Init();
  gate_outputs.Init();
  io_buffer.Init();
    
  deja_vu_length_quantizer.Init();
  cv_reader.Init(settings.mutable_calibration_data());
  scale_recorder.Init();
  ui.Init(&settings, &cv_reader, &scale_recorder, &clock_inputs);
  
  if (settings.freshly_baked()) {
    settings.ProgramOptionBytes();
    if (PROFILE_INTERRUPT || PROFILE_RENDER) {
      DebugPin::Init();
    } else {
      debug_port.Init();
    }
  }
  
  random_generator.Init(1);
  random_stream.Init(&random_generator);
  t_generator.Init(&random_stream, static_cast<float>(kSampleRate));
  xy_generator.Init(&random_stream, static_cast<float>(kSampleRate));

  for (size_t i = 0; i < kNumScales; ++i) {
    xy_generator.LoadScale(i, settings.persistent_data().scale[i]);
  }
  
  for (size_t i = 0; i < kNumGateOutputs; ++i) {
    self_patching_detector[i].Init(i);
  }
  
  sys.StartTimers();
  dac.Start(&FillBuffer);
}

int main(void) {
  Init();
  while (1) {
    ui.DoEvents();
    io_buffer.Process(ui.output_test_mode() ? &ProcessTest : &Process);
  }
}
