// --------------------------------------------------------------
// --------------------------------------------------------------
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "synth_defs.h"
#include "env.h"

// --------------------------------------------------------------
// Exported functions
// --------------------------------------------------------------
void ENV_Init(envgen_t *pObj, synth_float_t fs)
{
	pObj->fs = fs;
	ENV_Reset(pObj);
	ENV_Param2Set(pObj, ENV_PARAM2_A, 0.001);
	ENV_Param2Set(pObj, ENV_PARAM2_D, 1.000);
	ENV_Param2Set(pObj, ENV_PARAM2_S, 1.000);
	ENV_Param2Set(pObj, ENV_PARAM2_R, 1.000);
	pObj->bufsize = 0;
	pObj->pOut = NULL;
	ENV_SetBufsize(pObj, SYNTH_MAX_BUFSIZE);
}

void ENV_Free(envgen_t *pObj)
{
	ENV_SetBufsize(pObj, 0);
}

void ENV_SetBufsize(envgen_t *pObj, UINT32 size)
{
	if (pObj->bufsize == size)
		return;

	pObj->bufsize = size;

	if (pObj->pOut)
		free(pObj->pOut);

	pObj->pOut = NULL;

	if (!size)
		return;

	pObj->pOut = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
}

void ENV_SetFS(envgen_t *pObj, synth_float_t fs)
{
	pObj->fs = fs;
}

void ENV_Reset(envgen_t *pObj)
{
	pObj->state = env_state_idle;
	pObj->s = 0;
	pObj->k = (synth_float_t)1/ENV_KE;
}

void ENV_Event(envgen_t *pObj, UINT32 type)
{

	switch(type)
	{
		case env_event_trigger:
		case env_event_retrigger:
//			pObj->sr = 0;
			pObj->state = env_state_attack;
//			pObj->k = (synth_float_t)1/ENV_KE;
//			if (type == env_event_retrigger)
//			{
//				pObj->a_r = 1.f/(0.001*pObj->fs);
//				pObj->b_r = 1.f - pObj->a_r;
//				pObj->state = env_state_attack_retrigger;
//
//			}
		break;

		case env_event_release:
			if (pObj->state == env_state_idle)
				break;
	
//			if (pObj->state == env_state_attack)
//				pObj->sf = pObj->sr;

//			if (pObj->state == env_state_sustain)
//				pObj->sf = pObj->param.Vs/pObj->k;

			pObj->state = env_state_release;
			break;

		default:
		break;
	}

};

void ENV_Param2Set(envgen_t *pObj, UINT32 type, synth_float_t value)
{
	
	switch(type)
	{
		case ENV_PARAM2_A:
			pObj->param.Ta = jsy_max(ENV_MIN_A, value);
			pObj->a_a = 1.f/(pObj->param.Ta*pObj->fs);
			pObj->b_a = 1.f - pObj->a_a;
		break;

		case ENV_PARAM2_D:
			pObj->param.Td = jsy_max(ENV_MIN_D, value);
			pObj->a_d = 5.f/(pObj->param.Td*pObj->fs);
			pObj->b_d = 1.f - pObj->a_d;

		break;

		case ENV_PARAM2_S:
			pObj->param.Vs = value;
		break;

		case ENV_PARAM2_R:
			pObj->param.Tr = jsy_max(ENV_MIN_R, value);
			pObj->a_r = 5.f/(pObj->param.Tr*pObj->fs);
			pObj->b_r = 1.f - pObj->a_r;
		break;

		default:
			break;
	}
}

UINT32 ENV_ParamGet(envgen_t *pObj, UINT32 type)
{
	return 0;
}

INT32 ENV_IsIdle(envgen_t *pObj)
{
	return pObj->state == env_state_idle;
}

synth_float_t* ENV_ProcessDataV(envgen_t *pObj, UINT32 len)
{
	synth_float_t *pX, y;

	pX = pObj->pOut;
	while(len)
	{

		y = pObj->k*pObj->s;

		switch (pObj->state)
		{
			case env_state_idle:
			break;
			
			case env_state_attack:
//				y = pObj->k*pObj->sr;
				if (y >= 1.0)
				{
//					pObj->af = 5.f/(pObj->param.Td*pObj->fs);
//					pObj->bf = 1.f - pObj->af;
//					pObj->k = (synth_float_t)1/ENV_KE;
//					pObj->sf = pObj->sr;
					pObj->state = env_state_decay;
				}
				pObj->s = pObj->a_a + pObj->b_a*pObj->s;
				break;

			case env_state_attack_retrigger:
				pObj->sf = pObj->b_r*pObj->sf;
				if (pObj->sf <= ENV_TOL)
				{
					pObj->a_r = 5.f/(pObj->param.Tr*pObj->fs);
					pObj->b_r = 1.f - pObj->a_r;
					pObj->state = env_state_attack;
				}
				break;

			case env_state_decay:
				if (y <= pObj->param.Vs)
				{
					pObj->state = env_state_sustain;
				}
				pObj->s = pObj->b_d*pObj->s;
				break;

			case env_state_sustain:
				y = pObj->param.Vs;
				break;

			case env_state_release:
				if (y <= ENV_TOL)
				{
					pObj->state = env_state_idle;
				}
				pObj->s = pObj->b_r*pObj->s;
				break;

			default:
				break;
		}

		*(pX++) = y;
		len--;
	}
	return pObj->pOut;
}
