
#include "Chirp.hpp"
#include "vco_base.hpp"
#include "dsp/pitch.hpp"

void Chirp::Engine::reset() {
	trigger.reset();
	eocPulseGen.process(10.0f);
}

void Chirp::Engine::sampleRateChange(float sr) {
	chirp.setSampleRate(sr);
	ampSL.setParams(sr, Chirp::cycleSlewSeconds * 1000.0f, -Amplifier::minDecibels);
}

void Chirp::reset() {
	for (int c = 0; c < _channels; ++c) {
		_engines[c]->reset();
	}
}

void Chirp::sampleRateChange() {
	float sr = APP->engine->getSampleRate();
	_sampleTime = 1.0f / sr;
	for (int c = 0; c < _channels; ++c) {
		_engines[c]->sampleRateChange(sr);
	}
}

bool Chirp::active() {
	return outputs[OUT_OUTPUT].isConnected() || outputs[EOC_OUTPUT].isConnected();
}

int Chirp::channels() {
	return inputs[FREQUENCY1_INPUT].getChannels();
}

void Chirp::addChannel(int c) {
	_engines[c] = new Engine();
	_engines[c]->reset();
	_engines[c]->sampleRateChange(APP->engine->getSampleRate());
}

void Chirp::removeChannel(int c) {
	delete _engines[c];
	_engines[c] = NULL;
}

void Chirp::modulate() {
	_exponential = params[EXPONENTIAL_PARAM].getValue() > 0.5f;
	_loop = params[LOOP_PARAM].getValue() > 0.5f;
}

void Chirp::modulateChannel(int c) {
	Engine& e = *_engines[c];

	float time = clamp(params[TIME_PARAM].getValue(), 0.0f, 1.0f);
	if (inputs[TIME_INPUT].isConnected()) {
		time *= clamp(inputs[TIME_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
	}
	time *= time;
	time *= maxTimeSeconds - minTimeSeconds;
	time += minTimeSeconds;

	float maxF = roundf(maxFrequencyNyquistRatio * APP->engine->getSampleRate());
	float f1 = params[FREQUENCY1_PARAM].getValue();
	if (inputs[FREQUENCY1_INPUT].isConnected()) {
		f1 += clamp(inputs[FREQUENCY1_INPUT].getPolyVoltage(c), -5.0f, 5.0f);
	}
	f1 = cvToFrequency(f1);
	f1 = clamp(f1, minFrequency, maxF);

	float f2 = params[FREQUENCY2_PARAM].getValue();
	if (inputs[FREQUENCY2_INPUT].isConnected()) {
		f2 += clamp(inputs[FREQUENCY2_INPUT].getPolyVoltage(c), -5.0f, 5.0f);
	}
	f2 = cvToFrequency(f2);
	f2 = clamp(f2, minFrequency, maxF);

	e.chirp.setParams(f1, f2, time, !_exponential);
}

void Chirp::processChannel(const ProcessArgs& args, int c) {
	Engine& e = *_engines[c];

	bool triggered = e.trigger.process(params[TRIGGER_PARAM].getValue()*5.0f + inputs[TRIGGER_INPUT].getPolyVoltage(c));
	if (!_run) {
		if (triggered || _loop) {
			_run = true;
			e.chirp.reset();
		}
	}

	float out = 0.0f;
	if (_run) {
		out = e.chirp.next() * 5.0f;
		if (e.chirp.isCycleComplete()) {
			_run = false;
			e.eocPulseGen.trigger(0.001f);
			e.targetAmplitude = 0.0f;
		}
		else if (e.chirp.isCycleNearlyComplete(cycleSlewSeconds)) {
			e.targetAmplitude = Amplifier::minDecibels;
		}

		e.amp.setLevel(e.ampSL.next(e.targetAmplitude));
		out = e.amp.next(out);
	}

	outputs[OUT_OUTPUT].setChannels(_channels);
	outputs[OUT_OUTPUT].setVoltage(out, c);
	outputs[EOC_OUTPUT].setChannels(_channels);
	outputs[EOC_OUTPUT].setVoltage(e.eocPulseGen.process(_sampleTime) * 5.0f, c);
}

struct ChirpWidget : BGModuleWidget {
	static constexpr int hp = 5;

	ChirpWidget(Chirp* module) {
		setModule(module);
		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
		setPanel(box.size, "Chirp");
		createScrews();

		// generated by svg_widgets.rb
		auto timeParamPosition = Vec(18.5, 46.0);
		auto frequency1ParamPosition = Vec(24.5, 122.0);
		auto frequency2ParamPosition = Vec(24.5, 179.0);
		auto triggerParamPosition = Vec(10.0, 216.0);
		auto exponentialParamPosition = Vec(59.5, 218.0);
		auto loopParamPosition = Vec(59.5, 232.0);

		auto frequency1InputPosition = Vec(10.5, 251.0);
		auto frequency2InputPosition = Vec(40.5, 251.0);
		auto timeInputPosition = Vec(10.5, 288.0);
		auto triggerInputPosition = Vec(10.5, 325.0);

		auto eocOutputPosition = Vec(40.5, 288.0);
		auto outOutputPosition = Vec(40.5, 325.0);
		// end generated by svg_widgets.rb

		addParam(createParam<Knob38>(timeParamPosition, module, Chirp::TIME_PARAM));
		addParam(createParam<Knob26>(frequency1ParamPosition, module, Chirp::FREQUENCY1_PARAM));
		addParam(createParam<Knob26>(frequency2ParamPosition, module, Chirp::FREQUENCY2_PARAM));
		addParam(createParam<Button18>(triggerParamPosition, module, Chirp::TRIGGER_PARAM));
		addParam(createParam<IndicatorButtonGreen9>(exponentialParamPosition, module, Chirp::EXPONENTIAL_PARAM));
		addParam(createParam<IndicatorButtonGreen9>(loopParamPosition, module, Chirp::LOOP_PARAM));

		addInput(createInput<Port24>(frequency1InputPosition, module, Chirp::FREQUENCY1_INPUT));
		addInput(createInput<Port24>(frequency2InputPosition, module, Chirp::FREQUENCY2_INPUT));
		addInput(createInput<Port24>(timeInputPosition, module, Chirp::TIME_INPUT));
		addInput(createInput<Port24>(triggerInputPosition, module, Chirp::TRIGGER_INPUT));

		addOutput(createOutput<Port24>(eocOutputPosition, module, Chirp::EOC_OUTPUT));
		addOutput(createOutput<Port24>(outOutputPosition, module, Chirp::OUT_OUTPUT));
	}
};

Model* modelChirp = createModel<Chirp, ChirpWidget>("Bogaudio-Chirp", "CHIRP", "Swept-sine oscillator", "Oscillator", "Polyphonic");
