/******************************************************************************

    Test program for the lcd16.c module. Displays a floating point number
    on line one along with a fixed string. Displays a long integer (32-bits)
    and an integer (16-bits) on the second line. Increments the numbers by one
    forever.
  
  Ron Kreymborg
  
******************************************************************************/

#include <stdlib.h>
#include <interrupt.h>
#include <io.h>
#include <string.h>
#include <math.h>
#include <util/delay_basic.h>
#include <avr/eeprom.h>

#include "lcd.h"
#include "mc1.h"

#define PARAM_CHANNELS		(MAX_ANALOG_PARAM+MAX_ENCODER_PARAM)
#define PARAM_JITTER		2
#define PARAM_LOCKOUT		30 // ms
#define PARAM_AVERAGE		3
#define ENCODER_ACCEL		60

#if 1
#define PARAM_VALID			Global.ParamValid
#define PARAM_DAMPING		Global.ParamDamping
#else
#define PARAM_VALID			8 // out of 1024
#define PARAM_DAMPING		25 // ms
#endif

// Clockwise 3102
#define ENCODER_CW          ((1 << 4) | (2))
// Anti-clockwise 3201
#define ENCODER_ANTICW      ((2 << 4) | (1))


#if MC2
int				Param[PARAM_CHANNELS] = {0};
#else
unsigned short	RawMax[PARAM_CHANNELS] = {0},
				RawMin[PARAM_CHANNELS] = {0},
				RawSamples[PARAM_CHANNELS][PARAM_AVERAGE],
				RawParam[PARAM_CHANNELS] = {0};
uint8			RawLockout[PARAM_CHANNELS] = {0}, // locks out a2d reads shortly after its passed into a valid range
				RawPos[PARAM_CHANNELS] = {0};
#endif

// When this counts down to zero turn the tempo LED off
static uint8 TempoCount = 0;

/// Convert analog to digital
int a2d(int Channel)
{
	int v;
	
	/*
		ADMUX defn of bits:
		7		6		5		4		3		2		1		0
		REFS1	REFS0	ADLAR	MUX4	MUX3	MUX2	MUX1	MUX0

		REFS - Voltage Reference Selections for ADC:
			0 0 = AREF, Internal Vref turned off
			0 1 = AVCC
			1 0 = Reserved
			1 1 = Internal 2.56V Voltage Reference with external capacitor at AREF pin
		ADLAR - ADC Left Adjust Result
			Adjust result to be left aligned in ADC
		MUX4:3 - Input Mode Selection Bits
			0 0 = Single ended input
			0 1 &
			1 0 &
			1 1 = Differential input
		MUX2:0 - Analog channel selection			
	 */
	ADMUX = _BV(REFS0) | Channel;
	/*
		ADCSRA defn of bits:
		7		6		5		4		3		2		1		0
		ADEN	ADSC	ADFR	ADIF	ADIE	ADPS2	ADPS1	ADPS0
		
		ADEN - ADC Enable
		ADSC - ADC Start Conversion
		ADFR - ADC Free Running Select
		ADIF - ADC Interrupt Flag
		ADIE - ADC Interrupt Enable
		ADPS2:0 - ADC Prescaler Select Bits
	*/	
	ADCSRA |= _BV(ADSC); // Start the conversion
	while (ADCSRA & _BV(ADSC)); // Loop until the conversion finishes
	
	v = ADC;
	return v & 0x3ff;
}

void USART_Init(uint16_t ubrr)
{
	UBRR0H = (uint8_t) (ubrr >> 8);
	UBRR0L = ubrr & 0xFF;

	// Enable receiver and transmitter and Rx complete interrupt (must provide routine)
	UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);
	// UCSR0B |= _BV(TXEN0);

	// Set frame format: asynchronous, no parity, 8 bit data, 1 stop bit
	UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
}

// This flag is set when we are receiving a patch from the Axefx
static uint8 MidiIsPatch = 0;

// This buffer contains the midi bytes coming in
static uint8 MidiInBuf[512];

// This is how many bytes of 'MidiInBuf' are valid
static uint16 MidiInUsed = 0;

ISR(USART0_RX_vect)
{
	// Read the byte in from the USART
	uint8 b = UDR0;

	// Check we have buffer space available
	if (MidiInUsed < sizeof(MidiInBuf))
	{
		if (MidiIsPatch)
		{
			// We are receiving a patch. The patch is larger then the buffer we have available so
			// we capture the first 'n' bytes, skipping the bulk of the patch and then storing the
			// sysex end byte. The patch name (which we want) is in the first part of the patch.
			if (MidiInUsed < sizeof(MidiInBuf) - 1)
			{
				// Havn't stored enough patch yet, fill the buffer
				MidiInBuf[MidiInUsed++] = b;
			}
			else if (b == MIDI_SYSEX_END)
			{
				// End of sysex, store the byte and leave Patch mode
				MidiInBuf[MidiInUsed++] = b;
				MidiIsPatch = false;
			}
		}
		else
		{
			MidiInBuf[MidiInUsed++] = b;
			
			// Check for the start of the sysex Patch dump command
			if (MidiInUsed > 6 &&
				MidiInBuf[0] == MIDI_SYSEX_START &&
				MidiInBuf[1] == AxefxId[0] &&
				MidiInBuf[2] == AxefxId[1] &&
				MidiInBuf[3] == AxefxId[2] &&
				MidiInBuf[5] == MIDI_PATCH_DUMP)
			{
				MidiIsPatch = true;
			}
		}
	}
}

// System uptime in milliseconds
static uint32 UpTime = 0;

ISR(SIG_OUTPUT_COMPARE1A)
{
	// Our millisecond timer
	UpTime++;
}

uint32 Device_GetTick(void)
{
	uint32 t;
	cli();
	t = UpTime;
	sei();
	return t;
}

void Device_Sleep(int ms)
{
	uint32 start = Device_GetTick();
	while (1)
	{
		uint32 t = Device_GetTick() - start;
		
		if (t > ms)
			break;

		SendInt(t/100, 2, 8);
	}
}

void Device_SendMidi(unsigned char *ptr, int len)
{
	for (int i=0; i<len; i++)
	{
		while (!(UCSR0A & _BV(UDRE0)))
			;
		UDR0 = ptr[i];
	}
}

void Device_SetLed(enum Led led, int on)
{
	if (led >= LedEffect1)
	{
		led >>= 8;
		if (on)
			PORTG |= led;
		else
			PORTG &= ~led;
	}
	else
	{
		if (on)
			PORTE |= led;
		else
			PORTE &= ~led;
	}

	if (led == LedTempo && on)
	{
		TempoCount = TEMPO_LIGHT_MS;
	}
}

void Device_CustomChar(uint8 location, uint8 *def)
{
	DefineCustomCharacter(location, def);
}

int Device_Eeprom(uint16 Pos, uint8 *Ptr, int Len, uint8 Write)
{
	if (Write)
		eeprom_write_block(Ptr, (void *)Pos, Len); 		
	else
		eeprom_read_block(Ptr, (const void*)Pos, Len);

	return Len;
}

/// In this function all the ports used for buttons are initialized for input. If you
/// are using more ports for buttons then add the initialization here.
void Device_InitializeButtonPorts(void)
{
	#if MC2
	#else
	// Port B		Switch input:	0 - Effect1
	//								1 - Effect2
	//								2 - Effect3
	//								3 - Bank Up
	//								4 - Bank Down
	//								5 - Preset1
	//								6 - Preset2
	//								7 - Preset3
	DDRB = 0x00;	// all input pins
	PORTB = BUTTONS_MASK & 0xff;	// turn on all the pull up resistors

	// Port D		Switch input:	0 - Preset4
	//								1 - Preset5
	//								2 - 
	//								3 - 
	//								4 - Tuner
	//								5 - Tempo
	//								6 - 
	//								7 - 
	DDRD = 0x00;	// all input pins
	PORTD = BUTTONS_MASK >> 8;	// turn on all the pull up resistors for pins we are using
	#endif
}

ButtonBits Device_ReadButtonState(void)
{
	#if MC2
	return 0;
	#else
	// This function reads the instant button state from the IO ports, joins it into
	// one variable that can be queried by AND'd it with one of the Button enum's values.
	//
	// BUTTONS_MASK is all the valid bits
	//
	// ButtonBits is an unsigned type large enough to hold all the button bits. If you are
	// using more than 16 buttons then you'll have to change ButtonBits to a larger type, like
	// uint32
	return (((ButtonBits)PINB | ((ButtonBits)(PIND & 0xf3) << 8)) ^ BUTTONS_MASK) & BUTTONS_MASK;
	#endif
}

#include <util/delay.h>
#define delay_microsec _delay_us

#define SET_PIN(port, bit) \
	PORT##port |= (1 << bit)
#define CLR_PIN(port, bit) \
	PORT##port &= ~(1 << bit)

// SPI Input
#define SI_DATA_OUT			0
#define SI_CLOCK			1
#define SI_PLOAD			2
#define SI_CLOCK_ENABLE		3

// SPI Output
#define SO_DATA				4 // The input data bit to pass to the chip
#define SO_CLOCK			5 // Strobe HIGH to move the data bit onto the chip
#define SO_INPUT_ENABLE		6 // Set LOW to enable serial input
#define SO_OUTPUT_ENABLE	7 // Set LOW to enable output update
#define SO_PULSE_WIDTH_US   5 // Microsec

void spiWrite16(uint16 v)
{
	uint8 Data;
	
	CLR_PIN(B, SO_CLOCK);
	CLR_PIN(B, SO_INPUT_ENABLE);

	for (int i=0; i<sizeof(v)<<3; i++)
	{
		Data = (v & (1 << i)) != 0;
		if (Data)
			SET_PIN(B, SO_DATA);
		else
			CLR_PIN(B, SO_DATA);
		SET_PIN(B, SO_CLOCK);
		CLR_PIN(B, SO_CLOCK);
	}

	SET_PIN(B, SO_INPUT_ENABLE);
}

uint8 spiRead8(void)
{
	uint8 Data = 0;

	SET_PIN(B, SI_CLOCK_ENABLE);
	CLR_PIN(B, SI_PLOAD);
	delay_microsec(SO_PULSE_WIDTH_US);
	SET_PIN(B, SI_PLOAD);
	CLR_PIN(B, SI_CLOCK_ENABLE);

	for (int i=0; i<sizeof(Data)<<3; i++)
	{
		uint8 Bit = PINB & (1 << SI_DATA_OUT) ? 1 : 0;
		if (Bit)
			Data |= 1 << i;
		
		// Start the clock cycle
		SET_PIN(B, SI_CLOCK);
		delay_microsec(SO_PULSE_WIDTH_US);
		CLR_PIN(B, SI_CLOCK);		
	}
	
	return Data;
}

uint16 spiRead16(void)
{
	uint16 Data = 0;

	SET_PIN(B, SI_CLOCK_ENABLE);
	CLR_PIN(B, SI_PLOAD);
	delay_microsec(SO_PULSE_WIDTH_US);
	SET_PIN(B, SI_PLOAD);
	CLR_PIN(B, SI_CLOCK_ENABLE);

	for (int i=0; i<sizeof(Data)<<3; i++)
	{
		uint8 Bit = PINB & (1 << SI_DATA_OUT) ? 1 : 0;
		if (Bit)
			Data |= 1 << i;
		
		// Start the clock cycle
		SET_PIN(B, SI_CLOCK);
		delay_microsec(SO_PULSE_WIDTH_US);
		CLR_PIN(B, SI_CLOCK);		
	}
	
	return Data;
}

void ReadParams(void)
{
	// read adc
	for (int i=0; i<PARAM_CHANNELS; i++)
	{
		// Read param into average buffer
		RawSamples[i][RawPos[i]] = a2d(i);
		
		// Update the position in the average buffer
		RawPos[i]++;
		if (RawPos[i] >= PARAM_AVERAGE)
			RawPos[i] = 0;
	}
}

void ProcessParams(void)
{
	// read adc
	for (int i=0; i<PARAM_CHANNELS; i++)
	{
		uint8 n, valid;
		uint16 v = 0;
		
		// Create an average over 'PARAM_AVERAGE' samples
		for (n=0; n<PARAM_AVERAGE; n++)
		{
			v += RawSamples[i][n];
		}
		v /= PARAM_AVERAGE;

		// Check if its in the valid range
		valid = v >= PARAM_VALID && v < 1024 - PARAM_VALID;						
		
		#if 0
		if (i == 0) // ch 7
		{
			SendString("raw:            ", 2, 0);
			SendInt(v, 2, 4);
			SendInt(RawLockout[i], 2, 8);
			SendInt(RawParam[i], 2, 12);
		}
		#endif

		if (!valid)
		{
			// Outside of valid range for analog inputs, e.g. when the user has disconnected the input
			RawMin[i] = RawMax[i] = 400;
			RawLockout[i] = PARAM_LOCKOUT;

			#if 0
			if (i == 6) // ch 7
			{
				SendString("v:              ", 2, 0);
				SendInt(v, 2, 4);
			}
			#endif

			if (RawParam[i])
			{
				RawParam[i] = 0;
				Mc1_OnParam(i + 1, 0);
			}
		}
		else if (RawLockout[i] > 0)
		{
			// The user has connected the analog input but it's still unstable.
			RawLockout[i]--;
		}
		else
		{
			if (RawMax[i] < v)
				RawMax[i] = v;
			if (RawMin[i] > v)
				RawMin[i] = v;

			int diff = RawParam[i] - v;
			if (diff < 0)
				diff = -diff;

			if (RawParam[i] == 0 || diff > PARAM_JITTER)
			{
				RawParam[i] = v;
				int RawRange = RawMax[i] - RawMin[i];
				long Base = v - RawMin[i];
				long MidiParam = Base * 255 / RawRange;

				#if 0
				if (i == 0)
				{
					SendString("                ", 2, 0);
					SendInt(v, 2, 0);
					
					SendInt(RawParam[i], 2, 4);
					SendInt(diff, 2, 8);
					SendInt(MidiParam, 2, 12);
				}
				#endif


				Mc1_OnParam(i + 1, MidiParam);
			}
		}
	}
}

int main(void)
{
	uint32 LastMs = 0;

	#if MC2
	uint8 BtnBits[MAX_ENCODER_PARAM] = {3};
	uint8 Mask;
	int EncoderSpeed = 0;
	#else
	// Current state of buttons (a set of bits, valid bits are 
	// defined in BUTTONS_MASK)
	ButtonBits BtnState = 0;

	// Current state of buttons held down for more than 
	// BUTTONS_HELD_TIME (valid bits defined in BUTTONS_MASK)
	ButtonBits BtnHeld = 0;

	// Setup some default parameter limits
	for (int i=0; i<PARAM_CHANNELS; i++)
	{
		if (i < 6)
		{
			RawMin[i] = 50;
			RawMax[i] = 950;
		}
		else
		{
			RawMin[i] = 400;
			RawMax[i] = 400;
		}
	}
	
	memset(RawSamples, 0, sizeof(RawSamples));

	#endif

	// These count up when a button is down, or 0 if they are up/off.
	uint32 BtnTs[MAX_BUTTONS];

	Device_InitializeButtonPorts();

	// Setup TIMER1 to interrupt every ms for button debouncing and timing
	for (int i=0; i<MAX_BUTTONS; i++)
		BtnTs[i] = 0;

	// Setup the LCD for output
	InitLCD();
	
	#if MC2
	
	// Port B, Encoder in, SPI out:
	// 0 - 0.EncA
	// 1 - 0.EncB
	// 2 - 1.EncA
	// 3 - 1.EncB
	// 4 - 0.Btn
	// 5 - SDI
	// 6 - CLK
	// 7 - LE
	Mask = (1 << SO_OUTPUT_ENABLE) |
			(1 << SO_DATA) |
			(1 << SO_CLOCK) |
			(1 << SO_INPUT_ENABLE) |
			(1 << SI_CLOCK) |
			(1 << SI_PLOAD) |
			(1 << SI_CLOCK_ENABLE);
	DDRB =	Mask; // '1' output, '0' input			
	PORTB = Mask; // '1' pull up resistor on

	SET_PIN(B, SO_OUTPUT_ENABLE);
	SET_PIN(B, SO_INPUT_ENABLE);
	SET_PIN(B, SI_CLOCK_ENABLE);
	SET_PIN(B, SI_PLOAD);

	#else

	ADCSRA = _BV(ADEN) | // Turn on the analog to digital conversions
			_BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // Select 128 as our ADC prescaler
			/*
			The pre-scaler converts the system clock (16mhz in our case) down to
			something the adc can work with. It must be within the range of 50 and
			200 khz. So 16mhz / 128 = 125khz. Hence using 128 as our prescaler.
			*/

	// Port E		LED output:		0 - MIDI IN
	//								1 - MIDI OUT
	//								2 - Preset1 LED
	//								3 - Preset2 LED
	//								4 - Preset3 LED
	//								5 - Preset4 LED
	//								6 - Preset5 LED
	//								7 - Tempo LED
	DDRE = 0xFC;	// top 6 bits used for LED output
	PORTE = 0;		// set the LED state

	// Port G		LED output:		0 - Effect1 LED
	//								1 - Effect2 LED
	//								2 - Effect3 LED
	//								3 - 
	//								4 - 
	DDRG = 0x7;		// bottom 3 bits used for LED output
	PORTG = 0x0;	// set the LED state

	// Setup USART for midi transfer
	// baud rate were going for is 31250
	// UBRR = 31 exactly for that baud, UBRR = (fOSC / (16 * BAUD)) - 1
	// in Asynchronous Normal Mode.
	USART_Init(31);

	#endif

	// This sets up the timer for tick counting...
	TIMSK = _BV(OCIE1A);
	TCCR1B = _BV(CS11) | _BV(CS10) |	// 128 prescale
			 _BV(WGM12);				// CTC mode, TOP = OCR1A
	OCR1A = 250;						// countdown value

	// Initialize the high level "program" running on this device
	#if 1
	Mc1_New();
	#endif

	// Start the interrupts
	sei();

	// The main loop...
	while (1)
	{
		// Do something every millisecond
		{
			// Read the time
			uint32 u = Device_GetTick();

			if (LastMs != u)
			{
				#if 0
				static int LastSec = 0;
				if (u / 1000 != LastSec)
				{
					LastSec = u / 1000;
					SendInt(u, 1, 11);
				}
				#endif
				
				LastMs = u;

				// Turn off tempo LED?
				if (TempoCount > 0)
				{
					TempoCount--;
					if (!TempoCount)
						Device_SetLed(LedTempo, false);
				}

				// Debounce our buttons

				// Read the button's current state
				ButtonBits Btns = Device_ReadButtonState();

				// For each button
				for (int i=0; i<16; i++)
				{
					// Only some of the bits are mapped to buttons
					ButtonBits Msk = _BV(i);

					if (BUTTONS_MASK & Msk)
					{
						// Is this button latching or momentary?
						uint8 Latching = (BUTTONS_TYPE & Msk) != 0;

						// Current value
						ButtonBits CurState = (BtnState & Msk) != 0;
						ButtonBits NewState = (Btns & Msk) != 0;
						ButtonBits HeldState = (BtnHeld & Msk) != 0;

						if
						(
							CurState != NewState
							||
							(
								NewState != 0
								&&
								CurState != HeldState
							)							
						)
						{
							// Button is changing... increment the counter
							if (BtnTs[i] == 0)
								BtnTs[i] = u;
						}
						else
						{
							// State hasn't changed, clear the count, the down and held bits
							BtnTs[i] = 0;
						}

						if (!Latching && CurState)
						{
							// Button is down, check if it has been "held" over the threshold time
							if ((BtnHeld & Msk) == 0 && u > BtnTs[i]) 
							{
								uint32 diff = u - BtnTs[i];
								if (diff > BUTTONS_HELD_TIME)
								{
									// It has, but the OnHeld event hasn't trigger yet...
									
									// Set the held flag so we know the event has happened
									BtnHeld |= Msk;

									// Make the event happen..
									Mc1_OnButtonHeld(Msk);
								}

								#if 0
								if (diff % 10 == 0)								
									SendInt(diff, 1, 10);
								#endif
							}
						}

						if (CurState != NewState && u > BtnTs[i])
						{
							uint32 diff = u - BtnTs[i];
							if (diff >= BUTTONS_DEBOUNCE)
							{
								if (NewState)
								{
									BtnState |= Msk;
								}
								else
								{
									BtnState &= ~Msk;
									BtnHeld &= ~Msk;
									BtnTs[i] = 0;
								}

								uint32 now = Device_GetTick();
								Mc1_OnButton(_BV(i), NewState);
								uint32 len = Device_GetTick() - now;
								if (NewState != 0 && len > BUTTONS_HELD_TIME - 50)
								{
									BtnHeld |= Msk; // ignore the button held
								}
							}
						}
					}
				}

				ReadParams();
				if (u % PARAM_DAMPING == 0)
				{
					ProcessParams();
				}
			}
		}

		// Check midi in
		if (MidiInUsed > 0)
		{
			int Processed = 0;
			if (MidiInBuf[0] == MIDI_SYSEX_START)
			{
				// Sysex... is the end in the buf?
				int End = 0;
				for (int i=1; i<MidiInUsed; i++)
				{
					if (MidiInBuf[i] == MIDI_SYSEX_END)
					{
						End = i;
						break;
					}
				}

				if (End)
					Processed = End + 1;
			}
			else
			{
				// Standard MIDI messages...
				int Len = 0;
				switch (MidiInBuf[0] >> 4)
				{
					case 0x8:
					case 0x9:
					case 0xa:
					case 0xb:
					case 0xe:
						Len = 3;
						break;
					case 0xc:
					case 0xd:
						Len = 2;
						break;
				}

				if (Len > 0)
				{
					if (Len <= MidiInUsed)
						Processed = Len;
				}
				else
				{
					// Error in parsing... recover... hopefully.
					MidiInUsed = 0;
					SendString("MidiErr         ", 2, 0);
					SendInt(MidiInBuf[0], 2, 7);
					SendInt(MidiInBuf[1], 2, 10);
					SendInt(MidiInBuf[1], 2, 13);
					SendInt(MidiInUsed, 1, 12);
				}
			}

			if (Processed)
			{
				Mc1_OnMidi(MidiInBuf, Processed);

				cli();
				uint16 n = 0;
				for (uint8 i=Processed; i < MidiInUsed; i++)
				{
					MidiInBuf[n++] = MidiInBuf[i];
				}
				MidiInUsed -= Processed;
				sei();
			}
		}
	}

	return 0;
}

