/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic startup code for a Juce application.

  ==============================================================================
*/
#include <synth/synth_defs.h>

#include <JuceHeader.h>
#include "JaySynthMidiCC.h"

//==============================================================================
JaySynthMidiCC::JaySynthMidiCC()
{
	int i;
	param_info_t *pConstraints;

	for (i=0; i < SYNTH_NUM_PARAMS; i++)
	{
		pConstraints = getParamInfo(i);

		// Constraints
		paramInfoInit(&midiCC_containers[i].constraints, i, pConstraints->pName);
		paramInfoSet(&midiCC_containers[i].constraints,
					/*base*/pConstraints->base,
					/*kexp*/pConstraints->kexp,
					/*scenter*/pConstraints->scenter,
					/*pmin*/pConstraints->pmin,
					/*pcenter*/pConstraints->pcenter,
					/*pmax*/pConstraints->pmax,
					/*pinterval*/pConstraints->pinterval);

		// Control
		paramInfoInit(&midiCC_containers[i].control, i, pConstraints->pName);
		paramInfoSet(&midiCC_containers[i].control,
					/*base*/1,
					/*kexp*/1,
					/*scenter*/0.5,
					/*pmin*/0,
					/*pcenter*/-1E18,
					/*pmax*/pConstraints->pmax,
					/*pinterval*/pConstraints->pinterval);

		// Parameters
		paramInfoInit(&midiCC_containers[i].param, i, pConstraints->pName);
		paramInfoSet(&midiCC_containers[i].param,
					/*base*/pConstraints->base,
					/*kexp*/pConstraints->kexp,
					/*scenter*/pConstraints->scenter,
					/*pmin*/pConstraints->pmin,
					/*pcenter*/-1E18,
					/*pmax*/pConstraints->pmax,
					/*pinterval*/pConstraints->pinterval);

		midiCC_containers[i].controllerID = 128 + i;
		midiCC_containers[i].isAssigned = false;
		midiCC_containers[i].doChangeParameter = false;
		midiCC_containers[i].doApplyOnPatchInit = false;

		if (pConstraints->midiControllerId >= 0)
		{
			if (pConstraints->midiControllerId > 0)
			{
				midiCC_containers[i].controllerID = pConstraints->midiControllerId;
			}
			midiCC_containers[i].doChangeParameter = true;
			midiCC_containers[i].isAssigned = true;
		}

		// Velocity
		paramInfoInit(&midiCC_containers[i].curve_vel, i, pConstraints->pName);
		paramInfoSet(&midiCC_containers[i].curve_vel,
					/*base*/exp(1.0),
					/*kexp*/0,
					/*scenter*/0.0,
					/*pmin*/0,
					/*pcenter*/-1E18,
					/*pmax*/1,
					/*pinterval*/0);

		// Key follow
		paramInfoInit(&midiCC_containers[i].curve_key, i, pConstraints->pName);
		paramInfoSet(&midiCC_containers[i].curve_key,
					/*base*/exp(1.0),
					/*kexp*/0,
					/*scenter*/0.0,
					/*pmin*/0,
					/*pcenter*/-1E18,
					/*pmax*/1,
					/*pinterval*/0);

		midiCC_containers[i].isVelAssigned = false;
		midiCC_containers[i].isKeyAssigned = false;
		midiCC_containers[i].velKeyCombineOP = VELKEY_OP_ADD;

		isModified = false;
		paramNamedValueSet.set(toParameterNameXML(String(pConstraints->pName)), var(i));
	}
}
JaySynthMidiCC::~JaySynthMidiCC()
{
	int i;

	for (i=0; i < SYNTH_NUM_PARAMS; i++)
	{
		paramInfoFree(&midiCC_containers[i].constraints);
		paramInfoFree(&midiCC_containers[i].control);
		paramInfoFree(&midiCC_containers[i].param);
		paramInfoFree(&midiCC_containers[i].curve_vel);
		paramInfoFree(&midiCC_containers[i].curve_key);
	}

	for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
		removeDestinations(i);
}

JaySynthMidiCC& JaySynthMidiCC::operator= (JaySynthMidiCC& other)
{
	if (this != &other)
    {
		this->copyMidiCC(&other);
    }
    return *this;
}

void JaySynthMidiCC::copyMidiCC(JaySynthMidiCC const *pSrc)
{
	int i;
	midiCC_container_t const *pMidiContainerSrc;   
	midiCC_container_t *pMidiContainerDst;   

	for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
		removeDestinations(i);

	pMidiContainerSrc = pSrc->midiCC_containers;
	pMidiContainerDst = this->midiCC_containers;
	for (i=0; i < SYNTH_NUM_PARAMS; i++)
	{
		paramInfoCopy(&pMidiContainerDst[i].constraints, &pMidiContainerSrc[i].constraints);
		paramInfoCopy(&pMidiContainerDst[i].control, &pMidiContainerSrc[i].control);
		paramInfoCopy(&pMidiContainerDst[i].param, &pMidiContainerSrc[i].param);
		pMidiContainerDst[i].doChangeParameter = pMidiContainerSrc[i].doChangeParameter;
		pMidiContainerDst[i].doApplyOnPatchInit = pMidiContainerSrc[i].doApplyOnPatchInit;
		pMidiContainerDst[i].controllerID = pMidiContainerSrc[i].controllerID;
		pMidiContainerDst[i].isAssigned = 0;

		if (pMidiContainerSrc[i].isAssigned)
		{
			add(&pMidiContainerDst[i], pMidiContainerSrc[i].controllerID);
		}
	}

	for (i=0; i < SYNTH_NUM_PARAMS; i++)
	{
		paramInfoCopy(&pMidiContainerDst[i].curve_vel, &pMidiContainerSrc[i].curve_vel);
		paramInfoCopy(&pMidiContainerDst[i].curve_key, &pMidiContainerSrc[i].curve_key);
		pMidiContainerDst[i].isVelAssigned = pMidiContainerSrc[i].isVelAssigned;
		pMidiContainerDst[i].isKeyAssigned = pMidiContainerSrc[i].isKeyAssigned;
		pMidiContainerDst[i].velKeyCombineOP = pMidiContainerSrc[i].velKeyCombineOP;
	}

	isModified = false;
}

bool JaySynthMidiCC::isMidiCCmodified(void) const
{
	return isModified;
}

String JaySynthMidiCC::toParameterNameXML (String const &name) const
{
	String result;
	result = name.removeCharacters (String("."));
	result = result.replaceCharacter (' ','_');

    return result;
}

int JaySynthMidiCC::findParamID_byName(String name)
{
	return (int)paramNamedValueSet.getWithDefault (toParameterNameXML(name), var(-1));
}

void JaySynthMidiCC::export_midi_cc(XmlElement *pXML) const
{
	int paramID; 
	midiCC_container_t const *pMidiContainer;
	XmlElement *pXML_PARAM, *pXML_PARAM_INFO;

	// Store controller and slider infos
	for (paramID=0; paramID < SYNTH_NUM_PARAMS; paramID++)
	{
		pMidiContainer = &midiCC_containers[paramID];
		pXML_PARAM = pXML->createNewChildElement (String("PARAM_") + String(paramID));
		pXML_PARAM->setAttribute ("NAME", toParameterNameXML(pMidiContainer->constraints.pName));
		pXML_PARAM->setAttribute ("IS_ASSIGNED", pMidiContainer->isAssigned);
		pXML_PARAM->setAttribute ("IS_ABSOLUTE", pMidiContainer->doChangeParameter);
		pXML_PARAM->setAttribute ("DO_APPLY_ON_PATCH_INIT", pMidiContainer->doApplyOnPatchInit);
		pXML_PARAM->setAttribute ("CONTROLLER_ID", pMidiContainer->controllerID);
	}
}

void JaySynthMidiCC::export_midi_velkey(XmlElement *pXML) const
{
	int paramID; 
	midiCC_container_t const *pMidiContainer;
	XmlElement *pXML_PARAM, *pXML_PARAM_INFO;

	// Store controller and slider infos
	for (paramID=0; paramID < SYNTH_NUM_PARAMS; paramID++)
	{
		pMidiContainer = &midiCC_containers[paramID];
		pXML_PARAM = pXML->createNewChildElement (String("PARAM_") + String(paramID));
		pXML_PARAM->setAttribute ("NAME", toParameterNameXML(pMidiContainer->constraints.pName));
		pXML_PARAM->setAttribute ("IS_VELOCITY_ASSIGNED", pMidiContainer->isVelAssigned);
		pXML_PARAM->setAttribute ("IS_KEYFOLLOW_ASSIGNED", pMidiContainer->isKeyAssigned);
		pXML_PARAM->setAttribute ("VELKEY_COMBINE_OP", pMidiContainer->velKeyCombineOP);

		pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("RELATIVE"));
		pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->control.pmin, 10));
		pXML_PARAM_INFO->setAttribute ("PMAX", String(pMidiContainer->control.pmax, 10));

		pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("ABSOLUTE"));
		pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->param.pmin, 10));
		pXML_PARAM_INFO->setAttribute ("PMAX", String(pMidiContainer->param.pmax, 10));

		pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("VELOCITY_CURVE"));
		pXML_PARAM_INFO->setAttribute ("KEXP", String(pMidiContainer->curve_vel.kexp, 10));
		pXML_PARAM_INFO->setAttribute ("SCENTER", String(pMidiContainer->curve_vel.scenter, 10));
		pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->curve_vel.pmin, 10));
		pXML_PARAM_INFO->setAttribute ("PMAX", String(pMidiContainer->curve_vel.pmax, 10));
		pXML_PARAM_INFO->setAttribute ("PINTERVAL", String(pMidiContainer->curve_vel.pinterval, 10));

		pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("KEYFOLLOW_CURVE"));
		pXML_PARAM_INFO->setAttribute ("KEXP", String(pMidiContainer->curve_key.kexp, 10));
		pXML_PARAM_INFO->setAttribute ("SCENTER", String(pMidiContainer->curve_key.scenter, 10));
		pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->curve_key.pmin, 10));
		pXML_PARAM_INFO->setAttribute ("PMAX", String(pMidiContainer->curve_key.pmax, 10));
		pXML_PARAM_INFO->setAttribute ("PINTERVAL", String(pMidiContainer->curve_key.pinterval, 10));
	}
}

int JaySynthMidiCC::import_midi_velkey(XmlElement const *pXML)
{
	int i, paramID;
	midiCC_container_t *pMidiContainer;
	XmlElement *pXML_PARAM, *pXML_PARAM_INFO;

	for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
		removeDestinations(i);

	// make sure that it's actually our type of XML object..
	if (pXML)
	{
		pXML_PARAM = pXML->getFirstChildElement();
		while(pXML_PARAM)
		{
			paramID = findParamID_byName(pXML_PARAM->getStringAttribute("NAME"));
			if (paramID < 0)
			{
				pXML_PARAM = pXML_PARAM->getNextElement();
				continue;
			}

			pMidiContainer = &midiCC_containers[paramID];
			pMidiContainer->isVelAssigned = pXML_PARAM->getIntAttribute("IS_VELOCITY_ASSIGNED", 0) != 0;
			pMidiContainer->isKeyAssigned = pXML_PARAM->getIntAttribute("IS_KEYFOLLOW_ASSIGNED", 0) != 0;
			pMidiContainer->velKeyCombineOP = pXML_PARAM->getIntAttribute("VELKEY_COMBINE_OP", VELKEY_OP_ADD);

			pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("RELATIVE"));
			if (pXML_PARAM_INFO)
			{
				pMidiContainer->control.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
				pMidiContainer->control.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
			}

			pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("ABSOLUTE"));
			if (pXML_PARAM_INFO)
			{
				pMidiContainer->param.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
				pMidiContainer->param.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
			}

			pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("VELOCITY_CURVE"));
			if (pXML_PARAM_INFO)
			{
				pMidiContainer->curve_vel.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
				pMidiContainer->curve_vel.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
				pMidiContainer->curve_vel.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
				pMidiContainer->curve_vel.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
			}
			
			pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("KEYFOLLOW_CURVE"));
			if (pXML_PARAM_INFO)
			{
				pMidiContainer->curve_key.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
				pMidiContainer->curve_key.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
				pMidiContainer->curve_key.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
				pMidiContainer->curve_key.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
			}

			if (pXML_PARAM->getIntAttribute("IS_ASSIGNED", 0))
				add(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
			else
				remove(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));

			pXML_PARAM = pXML_PARAM->getNextElement();
		}
	}
	isModified = false;
	return 0;
}

int JaySynthMidiCC::import_midi_cc(XmlElement const *pXML)
{
	int i, paramID;
	midiCC_container_t *pMidiContainer;
	XmlElement *pXML_PARAM, *pXML_PARAM_INFO;

	for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
		removeDestinations(i);

	// make sure that it's actually our type of XML object..
	if (pXML)
	{
		pXML_PARAM = pXML->getFirstChildElement();
		while(pXML_PARAM)
		{
			paramID = findParamID_byName(pXML_PARAM->getStringAttribute("NAME"));
			if (paramID < 0)
			{
				pXML_PARAM = pXML_PARAM->getNextElement();
				continue;
			}

			pMidiContainer = &midiCC_containers[paramID];
			pMidiContainer->doChangeParameter = pXML_PARAM->getIntAttribute("IS_ABSOLUTE", pXML_PARAM->getIntAttribute("DO_CHANGE_PARAMETER", 0)) != 0;
			pMidiContainer->doApplyOnPatchInit = pXML_PARAM->getIntAttribute("DO_APPLY_ON_PATCH_INIT", 0) != 0;

			if (pXML_PARAM->getIntAttribute("IS_ASSIGNED", 0))
				add(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
			else
				remove(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));

			pXML_PARAM = pXML_PARAM->getNextElement();
		}
	}
	isModified = false;
	return 0;
}

int JaySynthMidiCC::import_midi_cc_legacy(XmlElement const *pXML_MidiCc)
{
	int i, paramID;
	midiCC_container_t *pMidiContainer;
	XmlElement *pXML, *pXML_PARAM, *pXML_PARAM_INFO;

	for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
		removeDestinations(i);

	// make sure that it's actually our type of XML object..
	if (pXML_MidiCc)
	{
		pXML = pXML_MidiCc->getFirstChildElement();
		if(pXML)
		{
			pXML_PARAM = pXML->getFirstChildElement();
			while(pXML_PARAM)
			{
				paramID = findParamID_byName(pXML_PARAM->getStringAttribute("NAME"));
				if (paramID < 0)
				{
					pXML_PARAM = pXML_PARAM->getNextElement();
					continue;
				}

				pMidiContainer = &midiCC_containers[paramID];
				pMidiContainer->doChangeParameter = pXML_PARAM->getIntAttribute("IS_ABSOLUTE", pXML_PARAM->getIntAttribute("DO_CHANGE_PARAMETER", 0)) != 0;
				pMidiContainer->doApplyOnPatchInit = pXML_PARAM->getIntAttribute("DO_APPLY_ON_PATCH_INIT", 0) != 0;

				pMidiContainer->isVelAssigned = pXML_PARAM->getIntAttribute("IS_VELOCITY_ASSIGNED", 0) != 0;
				pMidiContainer->isKeyAssigned = pXML_PARAM->getIntAttribute("IS_KEYFOLLOW_ASSIGNED", 0) != 0;
				pMidiContainer->velKeyCombineOP = pXML_PARAM->getIntAttribute("VELKEY_COMBINE_OP", VELKEY_OP_ADD);

				pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("RELATIVE"));
				if (pXML_PARAM_INFO)
				{
					pMidiContainer->control.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
					pMidiContainer->control.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
				}

				pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("ABSOLUTE"));
				if (pXML_PARAM_INFO)
				{
					pMidiContainer->param.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
					pMidiContainer->param.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
				}

				pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("VELOCITY_CURVE"));
				if (pXML_PARAM_INFO)
				{
					pMidiContainer->curve_vel.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
					pMidiContainer->curve_vel.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
					pMidiContainer->curve_vel.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
					pMidiContainer->curve_vel.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
				}
				
				pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("KEYFOLLOW_CURVE"));
				if (pXML_PARAM_INFO)
				{
					pMidiContainer->curve_key.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
					pMidiContainer->curve_key.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
					pMidiContainer->curve_key.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
					pMidiContainer->curve_key.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
				}

				if (pXML_PARAM->getIntAttribute("IS_ASSIGNED", 0))
					add(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
				else
					remove(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
				
				pXML_PARAM = pXML_PARAM->getNextElement();
			}
		}
	}
	isModified = false;
	return 0;
}

void JaySynthMidiCC::add(midiCC_container_t *pObj, int controllerID)
{
	if (controllerID >= NUM_MIDI_CONTROLLERS)
		return;

	remove(pObj, pObj->controllerID);

	if (midiCC_dest[controllerID].contains(pObj))
		return;

	pObj->isAssigned = 1;
	pObj->controllerID = controllerID;
	midiCC_dest[controllerID].append(pObj);
	isModified = true;
}

void JaySynthMidiCC::remove(midiCC_container_t *pObj, int controllerID)
{
	if (controllerID >= NUM_MIDI_CONTROLLERS)
		return;
	
	pObj->isAssigned = 0;
	if (midiCC_dest[controllerID].contains(pObj))
	{
		midiCC_dest[controllerID].remove(pObj);
	}
	isModified = true;
}

struct midiCC_container_t* JaySynthMidiCC::getAtParamID(int paramID)
{
	if (paramID >= SYNTH_NUM_PARAMS)
		return NULL;

	return &midiCC_containers[paramID];
}

int JaySynthMidiCC::getNumDestinations(int controllerID)
{
	if (controllerID >= NUM_MIDI_CONTROLLERS)
		return 0;

	return midiCC_dest[controllerID].size();
}

void JaySynthMidiCC::removeDestinations(int controllerID)
{
	int i;

	if (controllerID >= NUM_MIDI_CONTROLLERS)
		return;

	int numDst = getNumDestinations(controllerID);
	midiCC_container_t *pDst;

	for (i=0; i < numDst; i++)
	{
		pDst = getDestination(controllerID, 0);
		remove(pDst, controllerID);
	}
	midiCC_dest[controllerID].deleteAll();
	isModified = true;
}

midiCC_container_t* JaySynthMidiCC::getDestination(int controllerID, int index)
{
	if (controllerID >= NUM_MIDI_CONTROLLERS)
		return NULL;

	midiCC_container_t *pDst;
	if (index >= getNumDestinations(controllerID))
		return NULL;

	pDst =  midiCC_dest[controllerID][index].get();
	return pDst;
}

int JaySynthMidiCC::getParamID(midiCC_container_t *pObj)
{
	return pObj->constraints.id;
}

int JaySynthMidiCC::getControllerID(midiCC_container_t *pObj)
{
	return pObj->controllerID;
}

void JaySynthMidiCC::setControllerID(midiCC_container_t *pObj, int controllerID)
{
	if (controllerID >= NUM_MIDI_CONTROLLERS)
		return;

	if(isAssigned(pObj))
	{
		remove(pObj, pObj->controllerID);
		add(pObj, controllerID);
	}
	pObj->controllerID = controllerID;
	isModified = true;
}

bool JaySynthMidiCC::isAssigned(midiCC_container_t *pObj)
{
	return pObj->isAssigned;
}

void JaySynthMidiCC::setDoChangeParameter(midiCC_container_t *pObj, bool doChangeParameter)
{
	pObj->doChangeParameter = doChangeParameter;
	isModified = true;
}

bool JaySynthMidiCC::doChangeParameter(midiCC_container_t *pObj)
{
	return pObj->doChangeParameter;
}

void JaySynthMidiCC::setDoApplyOnPatchInit(midiCC_container_t *pObj, bool doApplyOnPatchInit)
{
	pObj->doApplyOnPatchInit = doApplyOnPatchInit;
	isModified = true;
}

bool JaySynthMidiCC::doApplyOnPatchInit(midiCC_container_t *pObj)
{
	return pObj->doApplyOnPatchInit;
}

synth_float_t JaySynthMidiCC::getConstraintsMin(midiCC_container_t *pObj)
{
	return pObj->constraints.pmin;
}

synth_float_t JaySynthMidiCC::getConstraintsMax(midiCC_container_t *pObj)
{
	return pObj->constraints.pmax;
}

param_info_t* JaySynthMidiCC::getConstraints(midiCC_container_t *pObj)
{
	return &pObj->constraints;
}

synth_float_t JaySynthMidiCC::getControlMin(midiCC_container_t *pObj)
{
	return pObj->control.pmin;
}

void JaySynthMidiCC::setControlMin(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->control.pmin = value;
	isModified = true;
}

synth_float_t JaySynthMidiCC::getControlMax(midiCC_container_t *pObj)
{
	return pObj->control.pmax;
}

void JaySynthMidiCC::setControlMax(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->control.pmax = value;
	isModified = true;
}

param_info_t* JaySynthMidiCC::getControl(midiCC_container_t *pObj)
{
	return &pObj->control;
}

synth_float_t JaySynthMidiCC::getParamMin(midiCC_container_t *pObj)
{
	return pObj->param.pmin;
}

void JaySynthMidiCC::setParamMin(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->param.pmin = value;
	isModified = true;
}

synth_float_t JaySynthMidiCC::getParamMax(midiCC_container_t *pObj)
{
	return pObj->param.pmax;
}

void JaySynthMidiCC::setParamMax(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->param.pmax = value;
	isModified = true;
}

param_info_t* JaySynthMidiCC::getParam(midiCC_container_t *pObj)
{
	return &pObj->param;
}

param_info_t* JaySynthMidiCC::getVelCurve(midiCC_container_t *pObj)
{
	return &pObj->curve_vel;
}

void JaySynthMidiCC::setVelCurveMin(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_vel.pmin = value;
	isModified = true;
}

void JaySynthMidiCC::setVelCurveMax(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_vel.pmax = value;
	isModified = true;
}

void JaySynthMidiCC::setVelCurveSkew(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_vel.scenter = value;
	isModified = true;
}

void JaySynthMidiCC::setVelCurveShape(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_vel.kexp = value;
	isModified = true;
}

param_info_t* JaySynthMidiCC::getKeyCurve(midiCC_container_t *pObj)
{
	return &pObj->curve_key;
}

void JaySynthMidiCC::setKeyCurveMin(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_key.pmin = value;
	isModified = true;
}

void JaySynthMidiCC::setKeyCurveMax(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_key.pmax = value;
	isModified = true;
}

void JaySynthMidiCC::setKeyCurveSkew(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_key.scenter = value;
	isModified = true;
}

void JaySynthMidiCC::setKeyCurveShape(midiCC_container_t *pObj, synth_float_t value)
{
	pObj->curve_key.kexp = value;
	isModified = true;
}

bool JaySynthMidiCC::isAssignedVel(midiCC_container_t *pObj)
{
	return pObj->isVelAssigned;
}

bool JaySynthMidiCC::isAssignedKey(midiCC_container_t *pObj)
{
	return pObj->isKeyAssigned;
}

void JaySynthMidiCC::setAssignedVel(midiCC_container_t *pObj, bool active)
{
	pObj->isVelAssigned = active;
	isModified = true;
}

void JaySynthMidiCC::setAssignedKey(midiCC_container_t *pObj, bool active)
{
	pObj->isKeyAssigned = active;
	isModified = true;
}

int JaySynthMidiCC::getVelKeyCombineOP(midiCC_container_t *pObj)
{
	return pObj->velKeyCombineOP;
}

void JaySynthMidiCC::setVelKeyCombineOP(midiCC_container_t *pObj, int combineOP)
{
	if (combineOP >= VELKEY_NUM_OPS)
		return;

	pObj->velKeyCombineOP = combineOP;
	isModified = true;
}

bool JaySynthMidiCC::isAssignedVelKeyControl(int paramID)
{
	return isAssignedVel(&midiCC_containers[paramID]) || isAssignedKey(&midiCC_containers[paramID]);
}

synth_float_t JaySynthMidiCC::getVelKeyControl(int paramID, synth_float_t vel, synth_float_t key)
{
	synth_float_t result, result_vel, result_key;

	result_vel = 0;
	result_key = 0;
	result = 0;

	if (isAssignedVel(&midiCC_containers[paramID]))
		result_vel = toParam(&midiCC_containers[paramID].curve_vel, vel);

	if (isAssignedKey(&midiCC_containers[paramID]))
		result_key = toParam(&midiCC_containers[paramID].curve_key, key/127);

	switch (midiCC_containers[paramID].velKeyCombineOP)
	{
		case VELKEY_OP_ADD:
			result = jsy_min(1, jsy_max(-1, result_vel + result_key));
			break;

		case VELKEY_OP_SUB:
			result = jsy_min(1, jsy_max(-1, result_vel - result_key));
			break;

		case VELKEY_OP_MUL:
			if (!isAssignedVel(&midiCC_containers[paramID]))
			{
				result = result_key;
				break;
			}
			if (!isAssignedKey(&midiCC_containers[paramID]))
			{
				result = result_vel;
				break;
			}
			result = result_vel * result_key;
			break;

		default:
			break;
	}

	return midiCC_containers[paramID].constraints.pmax*result;
}
