/*
 * Copyright (C) 2014 Guitarix project MOD project
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * --------------------------------------------------------------------------
 */


#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <cassert>
#include <unistd.h>

///////////////////////// MACRO SUPPORT ////////////////////////////////

#define __rt_func __attribute__((section(".rt.text")))
#define __rt_data __attribute__((section(".rt.data")))

///////////////////////// FAUST SUPPORT ////////////////////////////////

#define FAUSTFLOAT float
#ifndef N_
#define N_(String) (String)
#endif
#define max(x, y) (((x) > (y)) ? (x) : (y))
#define min(x, y) (((x) < (y)) ? (x) : (y))

#define always_inline inline __attribute__((always_inline))

template <int32_t N> inline float faustpower(float x)
{
  return powf(x, N);
}
template <int32_t N> inline double faustpower(double x)
{
  return pow(x, N);
}
template <int32_t N> inline int32_t faustpower(int32_t x)
{
  return faustpower<N/2>(x) * faustpower<N-N/2>(x);
}
template <>      inline int32_t faustpower<0>(int32_t x)
{
  return 1;
}
template <>      inline int32_t faustpower<1>(int32_t x)
{
  return x;
}

////////////////////////////// LOCAL INCLUDES //////////////////////////

#include "gx_ampegsvt.h"        // define struct PortIndex
#include "gx_pluginlv2.h"   // define struct PluginLV2
#include "ampegsvtp1.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp2.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp2_off.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp2_low.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp2_h.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp2_off_h.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp2_low_h.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp3.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp4_1.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp4_2.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp4_3.cc"    // dsp class generated by faust -> dsp2cc
#include "ampegsvtp5.cc"    // dsp class generated by faust -> dsp2cc
#include "valve.cc"    // dsp class generated by faust -> dsp2cc
#include "cabsim.cc"    // dsp class generated by faust -> dsp2cc
#include "resampler.cc"   // define struct PluginLV2
#include "resampler-table.cc"   // define struct PluginLV2
#include "zita-resampler/resampler.h"

////////////////////////////// PLUG-IN CLASS ///////////////////////////
#define declare(n) namespace n { PluginLV2 *plugin(); }

declare(ampegsvtp2);
declare(ampegsvtp2_off);
declare(ampegsvtp2_low);
declare(ampegsvtp2_h);
declare(ampegsvtp2_off_h);
declare(ampegsvtp2_low_h);

declare(ampegsvtp4_1);
declare(ampegsvtp4_2);
declare(ampegsvtp4_3);

static plug tone_model [] = {
    ampegsvtp2::plugin, //0
    ampegsvtp2_off::plugin, //1
    ampegsvtp2_low::plugin, //2
    ampegsvtp2_h::plugin, //3
    ampegsvtp2_off_h::plugin, //4
    ampegsvtp2_low_h::plugin, //5
};

static plug mid_model [] = {
    ampegsvtp4_1::plugin, //0
    ampegsvtp4_2::plugin, //1
    ampegsvtp4_3::plugin, //2
};

static const size_t TONE_COUNT = sizeof(tone_model) / sizeof(tone_model[0]);

static const size_t MID_COUNT = sizeof(mid_model) / sizeof(mid_model[0]);

namespace ampegsvt {

class SimpleResampler {
 private:
    Resampler r_up, r_down;
    int m_fact;
 public:
    SimpleResampler(): r_up(), r_down(), m_fact() {}
    void setup(int sampleRate, unsigned int fact);
    void up(int count, float *input, float *output);
    void down(int count, float *input, float *output);
};

void SimpleResampler::setup(int sampleRate, unsigned int fact)
{
	m_fact = fact;
	const int qual = 16; // resulting in a total delay of 2*qual (0.7ms @44100)
	// upsampler
	r_up.setup(sampleRate, sampleRate*fact, 1, qual);
	// k == inpsize() == 2 * qual
	// pre-fill with k-1 zeros
	r_up.inp_count = r_up.inpsize() - 1;
	r_up.out_count = 1;
	r_up.inp_data = r_up.out_data = 0;
	r_up.process();
	// downsampler
	r_down.setup(sampleRate*fact, sampleRate, 1, qual);
	// k == inpsize() == 2 * qual * fact
	// pre-fill with k-1 zeros
	r_down.inp_count = r_down.inpsize() - 1;
	r_down.out_count = 1;
	r_down.inp_data = r_down.out_data = 0;
	r_down.process();
}

void SimpleResampler::up(int count, float *input, float *output)
{
	r_up.inp_count = count;
	r_up.inp_data = input;
	r_up.out_count = count * m_fact;
	r_up.out_data = output;
	r_up.process();
	assert(r_up.inp_count == 0);
	assert(r_up.out_count == 0);
}

void SimpleResampler::down(int count, float *input, float *output)
{
	r_down.inp_count = count * m_fact;
	r_down.inp_data = input;
	r_down.out_count = count+1; // +1 == trick to drain input
	r_down.out_data = output;
	r_down.process();
	assert(r_down.inp_count == 0);
	assert(r_down.out_count == 1);
}

class Gx_ampegsvt_
{
private:
  // pointer to buffer
  float*          output;
  float*          input;
  // pointer to dsp class
  PluginLV2*      ampegsvtp1;
  PluginLV2*      ampegsvtone[TONE_COUNT];
  PluginLV2*      ampegsvtp3;
  PluginLV2*      ampegsvmid[MID_COUNT];
  PluginLV2*      ampegsvtp5;
  PluginLV2*      valve;
  PluginLV2*      cabsim;

  // bypass ramping
  float*          bypass;
  uint32_t        bypass_;
  // tone control
  float*          lowswitch;
  uint32_t        lowswitch_;
  float*          midswitch;
  uint32_t        midswitch_;
  float*          highswitch;
  uint32_t        highswitch_;
  float*          cabswitch;
  uint32_t        cabswitch_;
  uint32_t        model;
  uint32_t        model_t;
  uint32_t        midmodel;
  uint32_t        midmodel_t;
  uint32_t        cabmodel;
  uint32_t        cabmodel_t;
 
  bool            needs_ramp_down;
  bool            needs_ramp_up;
  float           ramp_down;
  float           ramp_up;
  float           ramp_up_step;
  float           ramp_down_step;
  bool            bypassed;

  uint32_t fSamplingFreq;
  SimpleResampler smp;
  unsigned int sru;
  unsigned int fact;

  // private functions
  inline void run_dsp_(uint32_t n_samples);
  inline void connect_(uint32_t port,void* data);
  inline void init_dsp_(uint32_t rate);
  inline void connect_all__ports(uint32_t port, void* data);
  inline void activate_f();
  inline void clean_up();
  inline void deactivate_f();
  inline uint32_t get_model();

public:
  // LV2 Descriptor
  static const LV2_Descriptor descriptor;
  // static wrapper to private functions
  static void deactivate(LV2_Handle instance);
  static void cleanup(LV2_Handle instance);
  static void run(LV2_Handle instance, uint32_t n_samples);
  static void activate(LV2_Handle instance);
  static void connect_port(LV2_Handle instance, uint32_t port, void* data);
  static LV2_Handle instantiate(const LV2_Descriptor* descriptor,
                                double rate, const char* bundle_path,
                                const LV2_Feature* const* features);
  Gx_ampegsvt_();
  ~Gx_ampegsvt_();
};

// constructor
Gx_ampegsvt_::Gx_ampegsvt_() :
  output(NULL),
  input(NULL),
  ampegsvtp1(ampegsvtp1::plugin()),
  ampegsvtp3(ampegsvtp3::plugin()),
  ampegsvtp5(ampegsvtp5::plugin()),
  valve(valve::plugin()),
  cabsim(cabsim::plugin()),
  bypass(0),
  bypass_(2),
  model(1),
  model_t(1),
  midmodel(0),
  midmodel_t(0),
  cabmodel(0),
  cabmodel_t(0),
  needs_ramp_down(false),
  needs_ramp_up(false),
  bypassed(false) {};

// destructor
Gx_ampegsvt_::~Gx_ampegsvt_()
{
  // just to be sure the plug have given free the allocated mem
  // it didn't hurd if the mem is already given free by clean_up()
  if (ampegsvtp1->activate_plugin !=0)
    ampegsvtp1->activate_plugin(false, ampegsvtp1);
  if (ampegsvtp3->activate_plugin !=0)
    ampegsvtp3->activate_plugin(false, ampegsvtp3);
  if (ampegsvtp5->activate_plugin !=0)
    ampegsvtp5->activate_plugin(false, ampegsvtp5);
  if (valve->activate_plugin !=0)
    valve->activate_plugin(false, valve);
  if (cabsim->activate_plugin !=0)
    cabsim->activate_plugin(false, cabsim);
  // delete DSP class
  ampegsvtp1->delete_instance(ampegsvtp1);
  ampegsvtp3->delete_instance(ampegsvtp3);
  ampegsvtp5->delete_instance(ampegsvtp5);
  valve->delete_instance(valve);
  cabsim->delete_instance(cabsim);
  for(uint32_t i=0; i<TONE_COUNT; i++) {
	if (ampegsvtone[i]->activate_plugin !=0)
      ampegsvtone[i]->activate_plugin(false, ampegsvtone[i]);  
    ampegsvtone[i]->delete_instance(ampegsvtone[i]);
  }
  for(uint32_t i=0; i<MID_COUNT; i++) {
	if (ampegsvmid[i]->activate_plugin !=0)
      ampegsvmid[i]->activate_plugin(false, ampegsvmid[i]);  
    ampegsvmid[i]->delete_instance(ampegsvmid[i]);
  }
};

///////////////////////// PRIVATE CLASS  FUNCTIONS /////////////////////

void Gx_ampegsvt_::init_dsp_(uint32_t rate)
{
  fSamplingFreq = rate;
  // samplerate check
  fact = fSamplingFreq/48000;
  if (fact>1) {
    smp.setup(fSamplingFreq, fact);
    fSamplingFreq = 48000;
  }

  // set values for internal ramping
  ramp_down_step = 32 * (256 * fSamplingFreq) / 48000; 
  ramp_up_step = ramp_down_step;
  ramp_down = ramp_down_step;
  ramp_up = 0.0;

  ampegsvtp1->set_samplerate(fSamplingFreq, ampegsvtp1); // init the DSP class
  ampegsvtp3->set_samplerate(fSamplingFreq, ampegsvtp3); // init the DSP class
  ampegsvtp5->set_samplerate(fSamplingFreq, ampegsvtp5); // init the DSP class
  valve->set_samplerate(fSamplingFreq, valve); // init the DSP class
  cabsim->set_samplerate(fSamplingFreq, cabsim); // init the DSP class
  for(uint32_t i=0; i<TONE_COUNT; i++) {
    ampegsvtone[i] = tone_model[i]();
    ampegsvtone[i]->set_samplerate(fSamplingFreq, ampegsvtone[i]);  
  }
  for(uint32_t i=0; i<MID_COUNT; i++) {
    ampegsvmid[i] = mid_model[i]();
    ampegsvmid[i]->set_samplerate(fSamplingFreq, ampegsvmid[i]);  
  }
}

// connect the Ports used by the plug-in class
void Gx_ampegsvt_::connect_(uint32_t port,void* data)
{
  switch ((PortIndex)port)
    {
    case EFFECTS_OUTPUT:
      output = static_cast<float*>(data);
      break;
    case EFFECTS_INPUT:
      input = static_cast<float*>(data);
      break;
    case BYPASS: 
      bypass = static_cast<float*>(data); // , 0.0, 0.0, 1.0, 1.0 
      break;
    case LOWSWITCH: 
      lowswitch = static_cast<float*>(data); // , 0.0, 0.0, 2.0, 1.0 
      break;
    case MIDSWITCH: 
      midswitch = static_cast<float*>(data); // , 0.0, 0.0, 2.0, 1.0 
      break;
    case HIGHSWITCH: 
      highswitch = static_cast<float*>(data); // , 0.0, 0.0, 1.0, 0.0 
      break;
    case CABSWITCH: 
      cabswitch = static_cast<float*>(data); // , 0.0, 0.0, 1.0, 0.0 
      break;
    default:
      break;
    }
}

void Gx_ampegsvt_::activate_f()
{
  // allocate the internal DSP mem
  if (ampegsvtp1->activate_plugin !=0)
    ampegsvtp1->activate_plugin(true, ampegsvtp1);
  if (ampegsvtp3->activate_plugin !=0)
    ampegsvtp3->activate_plugin(true, ampegsvtp3);
  if (ampegsvtp5->activate_plugin !=0)
    ampegsvtp5->activate_plugin(true, ampegsvtp5);
  if (valve->activate_plugin !=0)
    valve->activate_plugin(true, valve);
  if (cabsim->activate_plugin !=0)
    cabsim->activate_plugin(true, cabsim);
  for(uint32_t i=0; i<TONE_COUNT; i++) {
    if (ampegsvtone[i]->activate_plugin !=0)
      ampegsvtone[i]->activate_plugin(true, ampegsvtone[i]);  
  }
  for(uint32_t i=0; i<MID_COUNT; i++) {
    if (ampegsvmid[i]->activate_plugin !=0)
      ampegsvmid[i]->activate_plugin(true, ampegsvmid[i]);  
  }
}

void Gx_ampegsvt_::clean_up()
{
  // delete the internal DSP mem
  if (ampegsvtp1->activate_plugin !=0)
    ampegsvtp1->activate_plugin(false, ampegsvtp1);
  if (ampegsvtp3->activate_plugin !=0)
    ampegsvtp3->activate_plugin(false, ampegsvtp3);
  if (ampegsvtp5->activate_plugin !=0)
    ampegsvtp5->activate_plugin(false, ampegsvtp5);
  if (valve->activate_plugin !=0)
    valve->activate_plugin(false, valve);
  if (cabsim->activate_plugin !=0)
    cabsim->activate_plugin(false, cabsim);
  for(uint32_t i=0; i<TONE_COUNT; i++) {
    if (ampegsvtone[i]->activate_plugin !=0)
      ampegsvtone[i]->activate_plugin(false, ampegsvtone[i]);  
  }
  for(uint32_t i=0; i<MID_COUNT; i++) {
    if (ampegsvmid[i]->activate_plugin !=0)
      ampegsvmid[i]->activate_plugin(false, ampegsvmid[i]);  
  }
}

void Gx_ampegsvt_::deactivate_f()
{
  // delete the internal DSP mem
  if (ampegsvtp1->activate_plugin !=0)
    ampegsvtp1->activate_plugin(false, ampegsvtp1);
  if (ampegsvtp3->activate_plugin !=0)
    ampegsvtp3->activate_plugin(false, ampegsvtp3);
  if (ampegsvtp5->activate_plugin !=0)
    ampegsvtp5->activate_plugin(false, ampegsvtp5);
  if (valve->activate_plugin !=0)
    valve->activate_plugin(false, valve);
  if (cabsim->activate_plugin !=0)
    cabsim->activate_plugin(false, cabsim);
  for(uint32_t i=0; i<TONE_COUNT; i++) {
	if (ampegsvtone[i]->activate_plugin !=0)
      ampegsvtone[i]->activate_plugin(false, ampegsvtone[i]);  
  }
  for(uint32_t i=0; i<MID_COUNT; i++) {
    if (ampegsvmid[i]->activate_plugin !=0)
      ampegsvmid[i]->activate_plugin(false, ampegsvmid[i]);  
  }
}

uint32_t Gx_ampegsvt_::get_model() {
  if (!highswitch_) {
    return lowswitch_;
  } else {
    return lowswitch_ + 3;
  }
}

void Gx_ampegsvt_::run_dsp_(uint32_t n_samples)
{
	uint32_t ReCount = n_samples;
	if (fact>1) {
		ReCount = n_samples/fact ;
	}
	FAUSTFLOAT buf[ReCount];
	if (fact>1) {
		smp.down(ReCount, input, buf);
	} else {
		memcpy(buf, input, n_samples*sizeof(float));
	}
  // do inplace processing at default
  // memcpy(output, input, n_samples*sizeof(float));
  // get switch values
  lowswitch_ = static_cast<uint32_t>(*(lowswitch));
  midswitch_ = static_cast<uint32_t>(*(midswitch));
  highswitch_ = static_cast<uint32_t>(*(highswitch));
  cabswitch_ = static_cast<uint32_t>(*(cabswitch));

  // check if model has changed
  if (model_t != get_model()) {
    model_t = get_model();
    ramp_down = ramp_down_step;
    ramp_up = 0.0;    
    if (bypass_) needs_ramp_down = true;
  }
  if (midmodel_t != static_cast<uint32_t>(midswitch_)) {
    midmodel_t = static_cast<uint32_t>(midswitch_);
    ramp_down = ramp_down_step;
    ramp_up = 0.0;    
    if (bypass_) needs_ramp_down = true;
  }
  if (cabmodel_t != static_cast<uint32_t>(cabswitch_)) {
    cabmodel_t = static_cast<uint32_t>(cabswitch_);
    ramp_down = ramp_down_step;
    ramp_up = 0.0;    
    if (bypass_) needs_ramp_down = true;
  }
  // check if bypass is pressed
  if (bypass_ != static_cast<uint32_t>(*(bypass))) {
    bypass_ = static_cast<uint32_t>(*(bypass));
    ramp_down = ramp_down_step;
    ramp_up = 0.0;    
    if (!bypass_) needs_ramp_down = true;
    else needs_ramp_up = true;
  }
  // check if raming is needed
  if (needs_ramp_down) {
    for (uint32_t i=0; i<ReCount; i++) {
      if (ramp_down >= 0.0) {
        --ramp_down;
      }
      output[i] = (output[i] * ramp_down) /ramp_down_step ;
    }

    if (ramp_down <= 0.0) {
      // when ramped down, clear buffers
      ampegsvtp1->clear_state(ampegsvtp1);
      ampegsvtone[model]->clear_state(ampegsvtone[model]);
      ampegsvtp3->clear_state(ampegsvtp3);
      ampegsvmid[midmodel]->clear_state(ampegsvmid[midmodel]);
      ampegsvtp5->clear_state(ampegsvtp5);
      cabsim->clear_state(cabsim);
      needs_ramp_down = false;
      // set new models to use
      if (model != get_model()) {
        if (bypass_) needs_ramp_up = true;
        model = get_model();
      }
      if (midmodel != midswitch_) {
        if (bypass_) needs_ramp_up = true;
        midmodel = midswitch_;
      }
      if (cabmodel != cabswitch_) {
        if (bypass_) needs_ramp_up = true;
        cabmodel = cabswitch_;
      }
      bypassed = true;
      //ramp_down = ramp_down_step;
    }
  } else if (needs_ramp_up) {
    bypassed = false;
    for (uint32_t i=0; i<ReCount; i++) {
      if (ramp_up <= ramp_up_step) {
        ++ramp_up;
      }
      output[i] = (output[i] * ramp_up) /ramp_up_step;
    }
    if (ramp_up >= ramp_up_step) {
      needs_ramp_up = false;
     //ramp_up = 0.0;
    }
  }
  if (!bypassed) {
	  ampegsvtp1->mono_audio(static_cast<int>(ReCount), buf, buf, ampegsvtp1);
	  ampegsvtone[model]->mono_audio(static_cast<int>(ReCount), buf, buf, ampegsvtone[model]);
	  ampegsvtp3->mono_audio(static_cast<int>(ReCount), buf, buf, ampegsvtp3);
	  ampegsvmid[midmodel]->mono_audio(static_cast<int>(ReCount), buf, buf, ampegsvmid[midmodel]);
	  ampegsvtp5->mono_audio(static_cast<int>(ReCount), buf, buf, ampegsvtp5);
	  valve->mono_audio(static_cast<int>(ReCount), buf, buf, valve);
	  if (cabmodel)cabsim->mono_audio(static_cast<int>(ReCount), buf, buf, cabsim);
  }
	if (fact>1) {
		smp.up(ReCount, buf, output);
	} else {
		memcpy(output, buf, n_samples*sizeof(float));
	}
}

void Gx_ampegsvt_::connect_all__ports(uint32_t port, void* data)
{
  // connect the Ports used by the plug-in class
  connect_(port,data); 
  // connect the Ports used by the DSP class
  ampegsvtp1->connect_ports(port,  data, ampegsvtp1);
  ampegsvtp3->connect_ports(port,  data, ampegsvtp3);
  ampegsvtp5->connect_ports(port,  data, ampegsvtp5);
  valve->connect_ports(port,  data, valve);
  cabsim->connect_ports(port,  data, cabsim);
  for(uint32_t i=0; i<TONE_COUNT; i++) {
    ampegsvtone[i]->connect_ports(port,  data, ampegsvtone[i]);  
  }
  for(uint32_t i=0; i<MID_COUNT; i++) {
    ampegsvmid[i]->connect_ports(port,  data, ampegsvmid[i]);  
  }
}

////////////////////// STATIC CLASS  FUNCTIONS  ////////////////////////

LV2_Handle 
Gx_ampegsvt_::instantiate(const LV2_Descriptor* descriptor,
                            double rate, const char* bundle_path,
                            const LV2_Feature* const* features)
{
  // init the plug-in class
  Gx_ampegsvt_ *self = new Gx_ampegsvt_();
  if (!self) {
    return NULL;
  }

  self->init_dsp_((uint32_t)rate);

  return (LV2_Handle)self;
}

void Gx_ampegsvt_::connect_port(LV2_Handle instance, 
                                   uint32_t port, void* data)
{
  // connect all ports
  static_cast<Gx_ampegsvt_*>(instance)->connect_all__ports(port, data);
}

void Gx_ampegsvt_::activate(LV2_Handle instance)
{
  // allocate needed mem
  static_cast<Gx_ampegsvt_*>(instance)->activate_f();
}

void Gx_ampegsvt_::run(LV2_Handle instance, uint32_t n_samples)
{
  // run dsp
  static_cast<Gx_ampegsvt_*>(instance)->run_dsp_(n_samples);
}

void Gx_ampegsvt_::deactivate(LV2_Handle instance)
{
  // free allocated mem
  static_cast<Gx_ampegsvt_*>(instance)->deactivate_f();
}

void Gx_ampegsvt_::cleanup(LV2_Handle instance)
{
  // well, clean up after us
  Gx_ampegsvt_* self = static_cast<Gx_ampegsvt_*>(instance);
  self->clean_up();
  delete self;
}

const LV2_Descriptor Gx_ampegsvt_::descriptor =
{
  GXPLUGIN_URI "#_ampegsvt_",
  Gx_ampegsvt_::instantiate,
  Gx_ampegsvt_::connect_port,
  Gx_ampegsvt_::activate,
  Gx_ampegsvt_::run,
  Gx_ampegsvt_::deactivate,
  Gx_ampegsvt_::cleanup,
  NULL
};


} // end namespace ampegsvtp1

////////////////////////// LV2 SYMBOL EXPORT ///////////////////////////

extern "C"
LV2_SYMBOL_EXPORT
const LV2_Descriptor*
lv2_descriptor(uint32_t index)
{
  switch (index)
    {
    case 0:
      return &ampegsvt::Gx_ampegsvt_::descriptor;
    default:
      return NULL;
    }
}

///////////////////////////// FIN //////////////////////////////////////
