#include "FrozenWasteland.hpp"
#include "dsp-delay/delayLine.cpp"
#include <time.h>
//#include "frame.h"
#include <iostream>
#include "ui/knobs.hpp"


#define NUM_TAPS 64
#define CHANNELS 2
#define DIVISIONS 21
#define NUM_PATTERNS 16
#define NUM_FEEDBACK_TYPES 4


struct HairPick : Module {
	typedef float T;

	enum ParamIds {
		CLOCK_DIV_PARAM,
		SIZE_PARAM,
		PATTERN_TYPE_PARAM,
		NUMBER_TAPS_PARAM,
		EDGE_LEVEL_PARAM,
		TENT_LEVEL_PARAM,
		TENT_TAP_PARAM,
		FEEDBACK_TYPE_PARAM,
		FEEDBACK_AMOUNT_PARAM,
		NUM_PARAMS
	};
	enum InputIds {
		CLOCK_INPUT,
		CLOCK_DIVISION_CV_INPUT,
		VOLT_OCTAVE_INPUT,
		SIZE_CV_INPUT,
		PATTERN_TYPE_CV_INPUT,
		NUMBER_TAPS_CV_INPUT,
		EDGE_LEVEL_CV_INPUT,
		TENT_LEVEL_CV_INPUT,
		TENT_TAP_CV_INPUT,
		FEEDBACK_TYPE_CV_INPUT,
		FEEDBACK_CV_INPUT,
		IN_L_INPUT,
		IN_R_INPUT,
		NUM_INPUTS
	};
	enum OutputIds {
		OUT_L_OUTPUT,
		OUT_R_OUTPUT,
		DELAY_LENGTH_OUTPUT,
		NUM_OUTPUTS
	};
	enum LightIds {
		NUM_LIGHTS
	};
	enum FeedbackTypes {
		FEEDBACK_GUITAR,
		FEEDBACK_SITAR,
		FEEDBACK_CLARINET,
		FEEDBACK_RAW,
	};

	const char* combPatternNames[NUM_PATTERNS] = {"Uniform","Flat Middle","Early Comb","Fibonacci","Flat Comb","Late Comb","Rev. Fibonacci","Ess Comb","Rand Uniform","Rand Middle","Rand Early","Rand Fibonacci","Rand Flat","Rand Late","Rand Rev Fib","Rand Ess"};
	const float combPatterns[NUM_PATTERNS][NUM_TAPS] = {
		{1.0f,2.0f,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0}, // Uniform
		{1.0f,2.0f,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,24.5,25.0,25.5,26.0,26.5,27.0,27.5,28.0,28.5,29.0,29.5,30.0,30.5,31.5,32.0,32.5,33.0,33.5,34.0,34.5,35.0,35.5,36.0,36.5,37.0,37.5,38.0,38.5,39.0,39.5,40.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0}, // Flat Middle
		{0.25f,0.5f,0.75f,1.0f,1.25f,1.5f,1.75f,2.0f,2.25f,2.5f,2.75f,3.0f,3.25f,3.5f,3.75f,4.0f,4.5f,5.0f,5.5f,6.0f,6.5f,7.0f,7.5f,8.0f,8.5f,9.0f,9.5f,10.0f,10.5f,11.0f,11.5f,12.0f,13.0f,14.0f,15.0f,16.0f,17.0f,18.0f,19.0f,20.0f,21.0f,22.0f,23.0f,24.0f,25.0f,26.0f,27.0f,28.0f,30.0f,32.0f,34.0f,36.0f,38.0f,40.0f,42.0f,44.0f,46.0f,48.0f,50.0f,52.0f,55.0f,58.0f,61.0f,64.0f}, // Early Comb
		{0.031f,0.037f,0.043f,0.05f,0.056f,0.062f,0.068f,0.074f,0.087f,0.099f,0.111f,0.124f,0.142f,0.161f,0.18f,0.198f,0.229f,0.26f,0.291f,0.322f,0.372f,0.421f,0.471f,0.52f,0.601f,0.681f,0.762f,0.842f,0.972f,1.102f,1.232f,1.362f,1.573f,1.783f,1.994f,2.204f,2.545f,2.885f,3.226f,3.567f,4.118f,4.669f,5.22f,5.771f,6.663f,7.554f,8.446f,9.337f,10.78f,12.223f,13.666f,15.108f,17.443f,19.777f,22.111f,24.446f,28.223f,32.0f,35.777f,39.554f,45.666f,51.777f,57.889f,64.0f}, // Fibonacci
		{2.0f,4.0f,6.0,8.0,10.0,12.0,14.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,24.5,25.0,25.5,26.0,26.5,27.0,27.5,28.0,28.5,29.0,29.5,30.0,30.5,31.0,31.5,32.0,32.5,33.0,33.5,34.0,34.5,35.0,35.5,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,50.0,52.0,54.0,56.0,58.0,60.0,62.0,64.0}, // Flat Comb
		{3.0f,6.0f,9.0f,12.0f,14.0f,16.0f,18.0f,20.0f,22.0f,24.0f,26.0f,28.0f,30.0f,32.0f,34.0f,36.0f,37.0f,38.0f,39.0f,40.0f,41.0f,42.0f,43.0f,44.0f,45.0f,46.0f,47.0f,48.0f,49.0f,50.0f,51.0f,52.0f,52.5f,53.0f,53.5f,54.0f,54.5f,55.0f,55.5f,56.0f,56.5f,57.0f,57.5f,58.0f,58.5f,59.0f,59.5f,60.0f,60.25f,60.5f,60.75f,61.0f,61.25f,61.5f,61.75f,62.0f,62.25f,62.5f,62.75f,63.0f,63.25f,63.5f,63.75f,64.0f}, // Late Comb
		{0.031f,6.142f,12.254f,18.365f,24.477f,28.254f,32.031f,35.808f,39.585f,41.92f,44.254f,46.588f,48.923f,50.365f,51.808f,53.251f,54.693f,55.585f,56.477f,57.368f,58.26f,58.811f,59.362f,59.913f,60.464f,60.805f,61.146f,61.486f,61.827f,62.037f,62.248f,62.458f,62.669f,62.799f,62.929f,63.059f,63.189f,63.269f,63.35f,63.43f,63.511f,63.56f,63.61f,63.659f,63.709f,63.74f,63.771f,63.802f,63.833f,63.851f,63.87f,63.889f,63.907f,63.92f,63.932f,63.944f,63.957f,63.963f,63.969f,63.975f,63.981f,63.988f,63.994f,64.0f}, // Rev Fibonacci
		{0.5f, 1.0f, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 9.0, 10.0, 11.0, 12.0,13.0, 14.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0, 26.0, 30.0, 32.0, 34.0, 36.0, 38.0, 40.0, 42.0, 44.0,46.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 53.5, 54.0, 55.0, 55.5, 67.0, 56.5, 57.0, 57.5, 58.0, 58.5,59.0, 59.5, 60.0, 60.5, 61.0, 61.5, 62.0, 62.5, 63.5,64.0}, // Ess Comb
		{1.258f,2.867f,3.545f,4.461f,5.618f,6.916f,7.936f,8.923f,9.572f,10.993f,11.38f,12.026f,13.936f,14.892f,15.736f,16.014f,17.361f,18.787f,19.587f,20.646f,21.276f,22.949f,23.776f,24.212f,25.823f,26.169f,27.944f,28.69f,29.081f,30.472f,31.842f,32.739f,33.38f,34.703f,35.724f,36.334f,37.394f,38.256f,39.527f,40.965f,41.654f,42.668f,43.56f,44.402f,45.806f,46.53f,47.313f,48.504f,49.486f,50.401f,51.495f,52.71f,53.992f,54.407f,55.861f,56.309f,57.091f,58.333f,59.638f,60.449f,61.972f,62.246f,63.842f,64.0f}, // Random Uniform
		{1.255f,2.488f,3.252f,4.298f,5.917f,6.708f,7.255f,8.082f,9.828f,10.863f,11.181f,12.218f,13.025f,14.911f,15.891f,16.885f,25.203f,25.61f,25.888f,26.543f,27.024f,27.042f,28.272f,28.45f,29.018f,29.213f,30.248f,30.414f,30.619f,32.049f,32.299f,33.134f,33.184f,33.506f,34.738f,35.368f,35.86f,36.116f,36.729f,37.229f,37.243f,37.79f,38.802f,39.038f,39.413f,40.111f,40.574f,48.727f,49.52f,50.863f,51.137f,52.111f,53.015f,54.411f,55.078f,56.231f,57.537f,58.981f,59.585f,60.899f,61.794f,62.007f,63.964f,64.0f}, // Random Flat Middle
		{0.519f,0.907f,1.046f,1.428f,1.513f,1.761f,1.778f,2.46f,2.532f,2.632f,3.049f,3.806f,4.057f,4.229f,4.381f,4.643f,4.812f,6.0f,6.408f,6.961f,7.386f,7.437f,8.008f,8.767f,9.292f,9.534f,9.947f,10.775f,11.216f,11.256f,12.19f,12.835f,13.474f,14.255f,15.6f,16.858f,17.551f,18.903f,19.838f,20.843f,21.639f,22.448f,23.15f,24.456f,25.991f,26.898f,27.615f,28.734f,30.359f,32.205f,34.68f,36.531f,38.956f,40.711f,42.76f,44.832f,46.899f,48.996f,50.255f,52.644f,55.831f,58.236f,61.704f,64.0f}, // Random Early Comb
		{0.353f,0.4f,0.408f,0.435f,0.458f,0.516f,0.529f,0.545f,0.557f,0.624f,0.626f,0.631f,0.695f,0.701f,0.705f,0.812f,0.823f,0.843f,0.87f,0.88f,1.028f,1.034f,1.066f,1.115f,1.168f,1.177f,1.32f,1.355f,1.588f,1.629f,1.64f,1.667f,2.343f,2.579f,2.599f,3.182f,3.268f,3.395f,3.697f,4.066f,5.02f,5.276f,5.329f,6.487f,6.749f,7.659f,9.04f,9.645f,10.997f,12.85f,13.996f,15.983f,18.059f,20.243f,22.64f,24.964f,28.876f,32.715f,35.883f,40.513f,46.186f,51.974f,57.948f,64.0f}, // Random Fibonacci
		{2.748f,4.499f,6.144f,8.714f,10.868f,12.113f,14.522f,16.561f,17.24f,18.734f,19.323f,20.399f,21.06f,22.009f,23.648f,24.649f,25.162f,25.282f,25.916f,26.128f,26.515f,27.296f,27.521f,28.801f,29.097f,29.135f,29.803f,30.726f,31.407f,31.455f,32.31f,32.72f,33.135f,33.189f,33.761f,34.614f,35.094f,35.548f,36.104f,40.283f,40.338f,41.026f,41.325f,42.119f,42.227f,43.504f,43.758f,44.016f,44.263f,45.679f,45.827f,46.661f,46.991f,47.772f,47.962f,48.453f,50.478f,52.343f,54.117f,56.733f,58.81f,60.796f,62.606f,64.0f}, // Random Flat Comb
		{3.295f,6.695f,9.844f,12.738f,14.884f,16.065f,18.612f,20.819f,22.418f,24.964f,26.659f,28.592f,30.977f,32.615f,34.054f,36.412f,37.115f,38.85f,39.301f,40.246f,41.363f,42.518f,43.872f,44.889f,45.561f,46.958f,47.21f,48.644f,49.094f,50.385f,50.443f,51.001f,52.025f,52.201f,53.049f,53.684f,53.732f,54.015f,55.074f,55.077f,55.563f,56.796f,57.155f,57.254f,57.687f,58.124f,59.307f,59.479f,60.035f,60.294f,60.348f,60.517f,60.707f,61.066f,61.553f,61.778f,61.926f,62.073f,62.456f,62.926f,63.105f,63.468f,63.475f,64.0f}, // Random Late Comb
		{0.072f,7.105f,12.843f,18.42f,24.766f,28.496f,32.434f,36.489f,40.163f,42.361f,45.122f,46.641f,49.864f,51.319f,52.808f,53.11f,54.105f,55.209f,56.226f,56.628f,57.493f,58.371f,59.075f,59.561f,59.764f,60.242f,61.078f,61.163f,61.183f,61.63f,61.843f,62.011f,62.132f,62.201f,62.386f,62.553f,62.775f,62.797f,62.869f,62.894f,62.933f,63.032f,63.047f,63.17f,63.206f,63.233f,63.249f,63.291f,63.319f,63.425f,63.443f,63.484f,63.581f,63.59f,63.608f,63.686f,63.714f,63.725f,63.737f,63.828f,63.889f,63.903f,63.905f,64.0f}, // Random Rev Fibonacci
		{1.227f,1.69f,2.356f,2.925f,3.035f,3.53f,4.379f,4.581f,4.872f,5.614f,6.15f,6.629f,6.876f,7.199f,8.111f,8.855f,9.744f,10.338f,11.417f,12.745f,13.09f,14.512f,15.197f,16.068f,18.112f,20.342f,22.943f,24.92f,26.793f,30.707f,32.794f,34.89f,36.117f,38.542f,40.327f,42.95f,44.243f,46.082f,48.205f,49.592f,50.992f,51.408f,52.914f,53.784f,53.947f,54.299f,55.104f,55.65f,56.713f,57.184f,58.025f,58.193f,59.061f,59.281f,59.715f,60.494f,61.289f,61.693f,61.813f,62.587f,63.459f,63.58f,63.868f,64.0f} //Random Ess Comb
	};

	const char* feedbackTypeNames[NUM_FEEDBACK_TYPES] = {"Guitar","Sitar","Clarinet","Raw"};


	int combPattern = 0;
	int feedbackType = 0;	
	float edgeLevel = 0.0f;
	float tentLevel = 1.0f;
	int tentTap = 32;
	
	dsp::SchmittTrigger clockTrigger;
	float divisions[DIVISIONS] = {1/256.0,1/192.0,1/128.0,1/96.0,1/64.0,1/48.0,1/32.0,1/24.0,1/16.0,1/13.0,1/12.0,1/11.0,1/8.0,1/7.0,1/6.0,1/5.0,1/4.0,1/3.0,1/2.0,1/1.5,1};
	const char* divisionNames[DIVISIONS] = {"/256","/192","/128","/96","/64","/48","/32","/24","/16","/13","/12","/11","/8","/7","/6","/5","/4","/3","/2","/1.5","x 1"};
	int division;
	float timeElapsed= 0.0;
	float duration = 0;
	float baseDelay;
	bool firstClockReceived = false;
	bool secondClockReceived = false;


	bool combActive[NUM_TAPS];
	float combLevel[NUM_TAPS];
	float lastDelayTime[NUM_TAPS+1];

	float lastTapCount = -1;
	float tapCountSqrt = 0;


	MultiTapDelayLine<FloatFrame, NUM_TAPS+1> delayLine;
	FloatFrame lastFeedback = {0.0f,0.0f};

	//percentages
	float clockDivPercentage = 0;
	float sizePercentage = 0;
	float patternTypePercentage = 0;
	float numberTapsPercentage = 0;
	float edgeLevelPercentage = 0;
	float tentLevelPercentage = 0;
	float tentTapPercentage = 0;
	float feedbackAmountPercentage = 0;
	float feedbackTypePercentage = 0;

	int muteTap(int tapIndex)
    {
        int tapNumber = 0;
        for (int levelIndex = 0; levelIndex < 6; levelIndex += 1)
        {
            tapNumber += (tapIndex & (1 << levelIndex)) > 0 ? (1 << (5-levelIndex)) : 0;
        }
        return NUM_TAPS-tapNumber;     
    }

	float envelope(int tapNumber, float edgeLevel, float tentLevel, int tentNumber) {
        float t;
        if (tapNumber <= tentNumber)
        {
            t = (float)tapNumber / tentNumber;
            return (1 - t) * edgeLevel + t * tentLevel;
        }
        else
        {
            t = (float)(tapNumber-tentNumber) / (63-tentNumber);
            return (1 - t) * tentLevel+ t * edgeLevel;
        }
    }

	float cubicSoftClip(float x, float drive) {
		float cx = std::max(std::min(drive * x, 1.0f), -1.0f);
		float y = std::max(std::min(cx - cx*cx*cx /  3.0f, 2.0f/3.0f), -2.0f/3.0f) / drive;
		return y;
	}


	HairPick() {
		config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);

		configParam(CLOCK_DIV_PARAM, 0, DIVISIONS-1, 0,"Divisions");
		configParam(SIZE_PARAM, 0.001f, 10.0f, 0.350f,"Size");
		//configParam(createParam<RoundLargeBlackKnob>(Vec(257, 40), module, HairPick::GRID_PARAM, 0.001f, 10.0f, 0.350f));

		configParam(PATTERN_TYPE_PARAM, 0.0f, 15.0f, 0.0f,"Pattern Type");
		configParam(NUMBER_TAPS_PARAM, 1.0f, 64.0f, 64.0f,"# of Taps");

		configParam(EDGE_LEVEL_PARAM, 0.0f, 1.0f, 0.5f,"Edge Level","%",0,100);
		configParam(TENT_LEVEL_PARAM, 0.0f, 1.0f, 0.5f,"Tent Level","%",0,100);
		configParam(TENT_TAP_PARAM, 0.0f, 63.0f, 32.0f,"Tent Tap #");


		configParam(FEEDBACK_TYPE_PARAM, 0.0f, 3, 0.0f);
		configParam(FEEDBACK_AMOUNT_PARAM, 0.0f, 1.0f, 0.0f);
		

		configInput(CLOCK_INPUT, "Clock");
		configInput(CLOCK_DIVISION_CV_INPUT, "Clock Division");
		configInput(VOLT_OCTAVE_INPUT, "v/O");
		configInput(SIZE_CV_INPUT, "Size CV");
		configInput(PATTERN_TYPE_CV_INPUT, "Pattern CV");
		configInput(NUMBER_TAPS_CV_INPUT, "# Taps CV");
		configInput(TENT_LEVEL_CV_INPUT, "Tent Level");
		configInput(TENT_TAP_CV_INPUT, "Tent Tap");
		configInput(FEEDBACK_TYPE_CV_INPUT, "Feedback Type");
		configInput(FEEDBACK_CV_INPUT, "Feedback CV");
		configInput(IN_L_INPUT, "Left");
		configInput(IN_R_INPUT, "Right");

		configOutput(OUT_L_OUTPUT, "Left");
		configOutput(OUT_R_OUTPUT, "Right");
		configOutput(DELAY_LENGTH_OUTPUT, "Delay Length");

		srand(time(NULL));
	}

	



	void process(const ProcessArgs &args) override {

		combPattern = (int)clamp(params[PATTERN_TYPE_PARAM].getValue() + (inputs[PATTERN_TYPE_CV_INPUT].getVoltage() * 1.5f),0.0f,15.0);
		feedbackType = (int)clamp(params[FEEDBACK_TYPE_PARAM].getValue() + (inputs[FEEDBACK_TYPE_CV_INPUT].getVoltage() / 10.0f),0.0f,3.0);
		patternTypePercentage = combPattern / 15.0;
		feedbackTypePercentage = feedbackType / 3.0;

		int tapCount = (int)clamp(params[NUMBER_TAPS_PARAM].getValue() + (inputs[NUMBER_TAPS_CV_INPUT].getVoltage() * 6.4f),1.0f,64.0);		
		if(tapCount !=lastTapCount) {
			lastTapCount = tapCount;
			tapCountSqrt = sqrt((float)tapCount);
			numberTapsPercentage = tapCount / 64.0;

			//Initialize muting - set all active first
			for(int tapNumber = 0;tapNumber<NUM_TAPS;tapNumber++) {
				combActive[tapNumber] = true;	
			}
			//Turn off as needed
			for(int tapIndex = NUM_TAPS-1;tapIndex >= tapCount;tapIndex--) {
				int tapNumber = muteTap(tapIndex);
				combActive[tapNumber] = false;
			}
		}


		edgeLevel = clamp(params[EDGE_LEVEL_PARAM].getValue() + (inputs[EDGE_LEVEL_CV_INPUT].getVoltage() / 10.0f),0.0f,1.0);
		tentLevel = clamp(params[TENT_LEVEL_PARAM].getValue() + (inputs[TENT_LEVEL_CV_INPUT].getVoltage() / 10.0f),0.0f,1.0);

		tentTap = (int)clamp(params[TENT_TAP_PARAM].getValue() + (inputs[TENT_TAP_CV_INPUT].getVoltage() * 6.3f),1.0f,63.0f);

		edgeLevelPercentage = edgeLevel;
		tentLevelPercentage = tentLevel;
		tentTapPercentage = tentTap / 63.0;

		float divisionf = params[CLOCK_DIV_PARAM].getValue();
		if(inputs[CLOCK_DIVISION_CV_INPUT].isConnected()) {
			divisionf +=(inputs[CLOCK_DIVISION_CV_INPUT].getVoltage() * (DIVISIONS / 10.0));
		}
		divisionf = clamp(divisionf,0.0f,20.0f);
		clockDivPercentage = divisionf / 20.0;
		division = (DIVISIONS-1) - int(divisionf); //TODO: Reverse Division Order

		timeElapsed+= 1.0 / args.sampleRate;
		if(inputs[CLOCK_INPUT].isConnected()) {
			if(clockTrigger.process(inputs[CLOCK_INPUT].getVoltage())) {
				if(firstClockReceived) {
					duration = timeElapsed;
					secondClockReceived = true;
				}
				timeElapsed= 0;
				firstClockReceived = true;								
			} else if(secondClockReceived && timeElapsed > duration) {  //allow absense of second clock to affect duration
				duration = timeElapsed;				
			}	
			baseDelay = clamp(duration / divisions[division],0.001f,10.0f);		
			sizePercentage = 0;
		} else {
			baseDelay = clamp(params[SIZE_PARAM].getValue() + inputs[SIZE_CV_INPUT].getVoltage(), 0.001f, 10.0f);
			sizePercentage = baseDelay / 10.0;
			duration = 0.0f;
			firstClockReceived = false;		
			secondClockReceived = false;	
		}

		float pitchShift = powf(2.0f,inputs[VOLT_OCTAVE_INPUT].getVoltage());
		baseDelay = baseDelay / pitchShift;
		outputs[DELAY_LENGTH_OUTPUT].setVoltage(baseDelay);  
		
		FloatFrame dryFrame;
		float feedbackAmount = clamp(params[FEEDBACK_AMOUNT_PARAM].getValue() + (inputs[FEEDBACK_CV_INPUT].getVoltage() / 10.0f), 0.0f, 1.0f);
		feedbackAmountPercentage = feedbackAmount;
		float in = 0.0f;				
		for(int channel = 0;channel < CHANNELS;channel++) {	
			if(channel == 0) {
				in = inputs[IN_L_INPUT].getVoltage();	
				dryFrame.l = in + lastFeedback.l * feedbackAmount;
			} else {
				in = inputs[IN_R_INPUT].isConnected() ? inputs[IN_R_INPUT].getVoltage() : inputs[IN_L_INPUT].getVoltage();	
				dryFrame.r = in + lastFeedback.r * feedbackAmount;
			}	
		}

		// Push dry sample into history buffer
		delayLine.write(dryFrame);

		float delayNonlinearity = 1.0f;
		float percentChange = 10.0f;
		//Apply non-linearity
		if(feedbackType == FEEDBACK_SITAR) {
			float rectIn = std::abs(in);
			delayNonlinearity = 1 + ((rectIn/10.0f) * (percentChange/100.0f)); //Test sitar will change length by up tp 10%
		}


		FloatFrame wet = {0.0f, 0.0f}; // This is the mix of delays and input that is outputed
		FloatFrame feedbackValue = {0.0f,0.0f}; // This is the output of a tap that gets sent back to input
		for(int tap = 0; tap <= NUM_TAPS;tap++) { 
			
			float delay	= 0.0f;
			// Compute delay time in seconds
			if(tap <NUM_TAPS) {
				delay = baseDelay * combPatterns[combPattern][tap] / NUM_TAPS; 
			} else { // delay tap
				delay = baseDelay * delayNonlinearity;
			}
			// Number of delay samples
			float index = delay * args.sampleRate;
			if(lastDelayTime[tap] != index) {
				delayLine.setDelayTime(tap,index);
				lastDelayTime[tap] = index;
			}

		
			FloatFrame wetTap = {0.0f,0.0f};
			if(tap == NUM_TAPS) {
				feedbackValue = delayLine.getTap(tap);
			} else {
				if(combActive[tap]) {
					wetTap = delayLine.getTap(tap);
					wetTap.l = wetTap.l * envelope(tap,edgeLevel,tentLevel,tentTap);
					wetTap.r = wetTap.r * envelope(tap,edgeLevel,tentLevel,tentTap);
				}
			}

			wet.l += wetTap.l;
			wet.r += wetTap.r;
		}

		// wet.l = wet.l / ((float)tapCount) * tapCountSqrt;
		// wet.r = wet.r / ((float)tapCount) * tapCountSqrt;
		// wet.l = wet.l / tapCountSqrt;
		// wet.r = wet.r / tapCountSqrt;

		float feedbackWeight = 0.5;
		switch(feedbackType) {
			case FEEDBACK_GUITAR :
				feedbackValue.l = (feedbackWeight * feedbackValue.l) + ((1-feedbackWeight) * lastFeedback.l);
				feedbackValue.r = (feedbackWeight * feedbackValue.r) + ((1-feedbackWeight) * lastFeedback.r);
				break;
			case FEEDBACK_SITAR :
				feedbackValue.l = (feedbackWeight * feedbackValue.l) + ((1-feedbackWeight) * lastFeedback.l);
				feedbackValue.r = (feedbackWeight * feedbackValue.r) + ((1-feedbackWeight) * lastFeedback.r);

				// T cx = std::max(std::min(drive * x, (T) 1.0), (T) -1.0);
				// T y = std::max(std::min(cx - cx*cx*cx / (T) 3, (T) 2.0/3.0), (T) -2.0/3.0) / drive;

				break;
			case FEEDBACK_CLARINET :
				feedbackValue.l = cubicSoftClip(feedbackValue.l / 10.0,1.0) * 13;
				feedbackValue.r = cubicSoftClip(feedbackValue.r / 10.0,1.0) * 13;
				feedbackValue.l = (feedbackWeight * feedbackValue.l) + ((1-feedbackWeight) * lastFeedback.l);
				feedbackValue.r = (feedbackWeight * feedbackValue.r) + ((1-feedbackWeight) * lastFeedback.r);
				break;
			case FEEDBACK_RAW :
				break;
		}
		
		//feedbackValue = clamp(feedbackValue,-10.0f,10.0f);


		lastFeedback = feedbackValue;

		FloatFrame out = wet; 
		
		outputs[OUT_L_OUTPUT].setVoltage(out.l);
		outputs[OUT_R_OUTPUT].setVoltage(out.r);

	}
};


struct HPStatusDisplay : TransparentWidget {
	HairPick *module;
	int frame = 0;
	std::shared_ptr<Font> fontNumbers,fontText;
	std::string numberFontPath,textFontPath;

	

	HPStatusDisplay() {
		numberFontPath = asset::plugin(pluginInstance, "res/fonts/SUBWT___.ttf");
		textFontPath = asset::plugin(pluginInstance, "res/fonts/DejaVuSansMono.ttf");
	}

	

	void drawDivision(const DrawArgs &args, Vec pos, int division) {
		nvgFontSize(args.vg, 20);
		nvgFontFaceId(args.vg, fontNumbers->handle);
		nvgTextLetterSpacing(args.vg, -1);

		//nvgFillColor(args.vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
		nvgFillColor(args.vg, nvgRGBA(0x4a, 0xc3, 0x27, 0xff));
		char text[128];
		snprintf(text, sizeof(text), "%s", module->divisionNames[division]);
		nvgText(args.vg, pos.x, pos.y, text, NULL);
	}

	void drawDelayTime(const DrawArgs &args, Vec pos, float delayTime) {
		nvgFontSize(args.vg, 28);
		nvgFontFaceId(args.vg, fontNumbers->handle);
		nvgTextLetterSpacing(args.vg, -1);

		//nvgFillColor(args.vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
		nvgFillColor(args.vg, nvgRGBA(0x4a, 0xc3, 0x27, 0xff));
		char text[128];
		snprintf(text, sizeof(text), "%6.0f", delayTime*1000);	
		nvgText(args.vg, pos.x, pos.y, text, NULL);
	}

	void drawPatternType(const DrawArgs &args, Vec pos, int patternType) {
		nvgFontSize(args.vg, 14);
		nvgFontFaceId(args.vg, fontText->handle);
		nvgTextLetterSpacing(args.vg, -2);

		//nvgFillColor(args.vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
		nvgFillColor(args.vg, nvgRGBA(0x4a, 0xc3, 0x27, 0xff));
		char text[128];
		snprintf(text, sizeof(text), "%s", module->combPatternNames[patternType]);
		nvgText(args.vg, pos.x, pos.y, text, NULL);
	}

	void drawEnvelope(const DrawArgs &args, Vec pos,float edgeLevel, float tentLevel, int tentTap) {
		nvgStrokeWidth(args.vg, 1);
		nvgStrokeColor(args.vg, nvgRGBA(0xff, 0xff, 0x20, 0xff));	
		nvgBeginPath(args.vg);
		float xOffset = (float)(tentTap-1)/64*100;
		nvgMoveTo(args.vg, pos.x+0, pos.y+20*(1-edgeLevel));
		nvgLineTo(args.vg, pos.x+xOffset, pos.y+20*(1-tentLevel));
		nvgLineTo(args.vg, pos.x+99, pos.y+20*(1-edgeLevel));
			
		nvgStroke(args.vg);
	}


	void drawFeedbackType(const DrawArgs &args, Vec pos, int feedbackType) {
		nvgFontSize(args.vg, 14);
		nvgFontFaceId(args.vg, fontText->handle);
		nvgTextLetterSpacing(args.vg, -2);

		//nvgFillColor(args.vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
		nvgFillColor(args.vg, nvgRGBA(0x4a, 0xc3, 0x27, 0xff));
		char text[128];
		snprintf(text, sizeof(text), "%s", module->feedbackTypeNames[feedbackType]);
		nvgText(args.vg, pos.x, pos.y, text, NULL);
	}

	void draw(const DrawArgs &args) override {
		fontNumbers = APP->window->loadFont(numberFontPath);
		fontText = APP->window->loadFont(textFontPath);

		if (!module)
			return;
		
		drawDivision(args, Vec(91,60), module->division);
		drawDelayTime(args, Vec(350,65), module->baseDelay);
		drawPatternType(args, Vec(64,135), module->combPattern);
		drawEnvelope(args,Vec(55,163),module->edgeLevel,module->tentLevel,module->tentTap);
		drawFeedbackType(args, Vec(62,305), module->feedbackType);
	}
};


struct HairPickWidget : ModuleWidget {
	HairPickWidget(HairPick *module) {
		setModule(module);

		setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HairPick.svg")));
		

		addChild(createWidget<ScrewSilver>(Vec(15, 0)));
		addChild(createWidget<ScrewSilver>(Vec(box.size.x-30, 0)));
		addChild(createWidget<ScrewSilver>(Vec(15, 745)));
		addChild(createWidget<ScrewSilver>(Vec(box.size.x-30, 745)));

		{
			HPStatusDisplay *display = new HPStatusDisplay();
			display->module = module;
			display->box.pos = Vec(0, 0);
			display->box.size = Vec(box.size.x, 200);
			addChild(display);
		}

		ParamWidget* clockDivParam = createParam<RoundLargeFWSnapKnob>(Vec(45, 33), module, HairPick::CLOCK_DIV_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWSnapKnob*>(clockDivParam)->percentage = &module->clockDivPercentage;
		}
		addParam(clockDivParam);							
		ParamWidget* sizeParam = createParam<RoundLargeFWKnob>(Vec(157, 33), module, HairPick::SIZE_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWKnob*>(sizeParam)->percentage = &module->sizePercentage;
		}
		addParam(sizeParam);							

		ParamWidget* patternTypeParam = createParam<RoundLargeFWSnapKnob>(Vec(17, 115), module, HairPick::PATTERN_TYPE_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWSnapKnob*>(patternTypeParam)->percentage = &module->patternTypePercentage;
		}
		addParam(patternTypeParam);							
		ParamWidget* numberTapsParam = createParam<RoundLargeFWSnapKnob>(Vec(157, 115), module, HairPick::NUMBER_TAPS_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWSnapKnob*>(numberTapsParam)->percentage = &module->numberTapsPercentage;
		}
		addParam(numberTapsParam);							

		ParamWidget* edgeLevelParam = createParam<RoundLargeFWKnob>(Vec(17, 200), module, HairPick::EDGE_LEVEL_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWKnob*>(edgeLevelParam)->percentage = &module->edgeLevelPercentage;
		}
		addParam(edgeLevelParam);							
		ParamWidget* tentLevelParam = createParam<RoundLargeFWKnob>(Vec(87, 200), module, HairPick::TENT_LEVEL_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWKnob*>(tentLevelParam)->percentage = &module->tentLevelPercentage;
		}
		addParam(tentLevelParam);							
		ParamWidget* tentTapParam = createParam<RoundLargeFWSnapKnob>(Vec(157, 200), module, HairPick::TENT_TAP_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWSnapKnob*>(tentTapParam)->percentage = &module->tentTapPercentage;
		}
		addParam(tentTapParam);							


		ParamWidget* feedbackTypeParam = createParam<RoundLargeFWSnapKnob>(Vec(17, 283), module, HairPick::FEEDBACK_TYPE_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWSnapKnob*>(feedbackTypeParam)->percentage = &module->feedbackTypePercentage;
		}
		addParam(feedbackTypeParam);							
		ParamWidget* feedbackAmountParam = createParam<RoundLargeFWKnob>(Vec(157, 283), module, HairPick::FEEDBACK_AMOUNT_PARAM);
		if (module) {
			dynamic_cast<RoundLargeFWKnob*>(feedbackAmountParam)->percentage = &module->feedbackAmountPercentage;
		}
		addParam(feedbackAmountParam);							


	//	addChild(createLight<MediumLight<BlueLight>>(Vec(474, 189), module, HairPick::PING_PONG_LIGHT));


		

		addInput(createInput<PJ301MPort>(Vec(12, 32), module, HairPick::CLOCK_INPUT));
		addInput(createInput<PJ301MPort>(Vec(12, 74), module, HairPick::VOLT_OCTAVE_INPUT));

		addInput(createInput<PJ301MPort>(Vec(50, 74), module, HairPick::CLOCK_DIVISION_CV_INPUT));
		addInput(createInput<PJ301MPort>(Vec(162, 74), module, HairPick::SIZE_CV_INPUT));
		//addInput(createInput<PJ301MPort>(Vec(300, 45), module, HairPick::GRID_CV_INPUT));

		addInput(createInput<PJ301MPort>(Vec(22, 156), module, HairPick::PATTERN_TYPE_CV_INPUT));
		addInput(createInput<PJ301MPort>(Vec(162, 156), module, HairPick::NUMBER_TAPS_CV_INPUT));

		addInput(createInput<PJ301MPort>(Vec(22, 241), module, HairPick::EDGE_LEVEL_CV_INPUT));
		addInput(createInput<PJ301MPort>(Vec(92, 241), module, HairPick::TENT_LEVEL_CV_INPUT));
		addInput(createInput<PJ301MPort>(Vec(162, 241), module, HairPick::TENT_TAP_CV_INPUT));


		addInput(createInput<PJ301MPort>(Vec(22, 324), module, HairPick::FEEDBACK_TYPE_CV_INPUT));
		addInput(createInput<PJ301MPort>(Vec(162, 324), module, HairPick::FEEDBACK_CV_INPUT));


		addInput(createInput<PJ301MPort>(Vec(50, 336), module, HairPick::IN_L_INPUT));
		addInput(createInput<PJ301MPort>(Vec(75, 336), module, HairPick::IN_R_INPUT));
		addOutput(createOutput<PJ301MPort>(Vec(110, 336), module, HairPick::OUT_L_OUTPUT));
		addOutput(createOutput<PJ301MPort>(Vec(135, 336), module, HairPick::OUT_R_OUTPUT));

		addOutput(createOutput<PJ301MPort>(Vec(130, 74), module, HairPick::DELAY_LENGTH_OUTPUT));
	}
};


Model *modelHairPick = createModel<HairPick, HairPickWidget>("HairPick");
