/*
 * GCLK_DFPLL.c
 *
 * Created: 04/01/2020 11:23:20
 * Author : pavel
 */ 


#include "sam.h"
#include "string.h"
#include "descriptors.h"
#include "SPI_lib.h"
#include "GCLOCKS_lib.h"
#include "stdlib.h"
#include "EIC_lib.h"
#include "I2C_lib.h"
#include "SPI_lib.h"
#include "PAT_lib.h"

#define ENDPOINTS_COUNT 4
#define USB_CMD_SAVE (1 << 0)
#define USB_CMD_ACTIVATE (1 << 1)
#define USB_CMD_RESET (1 << 2)
#define USB_CMD_INTENSITY (1 << 3)
#define USB_CMD_UPDATE (1 << 4)
#define USB_CMD_LED_INTENSITY (1 << 5)
#define USB_CMD_DEBOUNCE_HZ (1 << 6)

__attribute__((__aligned__(4)))



volatile LED_PATTERN pattern = { 0 };
volatile LED_PATTERN activePattern = { 0 };
volatile uint8_t currentIndex;
volatile uint8_t eepromTransferFlag = 0;
volatile uint8_t patternSaved = 0;
volatile uint32_t commandValue = 0;
volatile uint8_t commandPacket[5] = { 0 };

TABLE indexTable = { 0 };
RECORD blankRecord = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
uint16_t activeIndex = 0;

uint16_t totalLength;
uint8_t lengthInPages;

uint32_t debounceFrequency_Hz = 5000;

epStatus status;
volatile uint8_t intensityChangeFlag = 0;
volatile uint8_t patternChangeFlag = 0;
volatile uint8_t keyChangeFlag = 0;
volatile uint8_t bulkPhase = 0;
volatile uint16_t incomingBytes = 0;
USB_SetupPacket setupPacket;

uint8_t usbConfiguration = 0;

uint8_t testPacket[] = {0,1,2,3};

void USB_reset();
inline void usb_ep0_out();
inline void usb_ep0_in(uint8_t size);
void usb_enable_ep(uint8_t ep, uint32_t* data, uint8_t type, uint16_t bufsize);
inline void usb_ep_start_out(uint8_t ep, uint32_t* data, uint8_t len);
inline void usb_ep_start_in(uint8_t ep, uint32_t* data, uint8_t size, uint8_t zlp);
void usb_stop_INT(uint8_t ep);

volatile uint16_t summaryFlag = 0;

uint8_t USB_bufSize(uint16_t bufsize){
	switch(bufsize){
		case 8:
		return 0x0;
		break;
		case 16:
		return 0x1;
		break;
		case 32:
		return 0x2;
		break;
		case 64:
		return 0x3;
		break;
	}
	return 0;
}

void USB_reset(){
	EP[0].Bank[0].nonArray.ADDR.reg = (uint32_t)(EP0OUT.u32);
	EP[0].Bank[0].nonArray.PCKSIZE.bit.SIZE = 0x3;
	EP[0].Bank[1].nonArray.ADDR.reg = (uint32_t)(EP0IN.u32);
	EP[0].Bank[1].nonArray.PCKSIZE.bit.SIZE = 0x3;
	EP[0].Bank[1].nonArray.PCKSIZE.bit.AUTO_ZLP = 1;
	
	
	EP[1].Bank[1].nonArray.ADDR.reg = (uint32_t)(EP1IN.u32);
	EP[1].Bank[1].nonArray.PCKSIZE.bit.SIZE = 0x3;
	EP[1].Bank[1].nonArray.PCKSIZE.bit.AUTO_ZLP = 1;
	
	EP[2].Bank[0].nonArray.ADDR.reg = (uint32_t)(EP2OUT.u32);
	EP[2].Bank[0].nonArray.PCKSIZE.bit.SIZE = 0x3;
	EP[2].Bank[0].nonArray.PCKSIZE.bit.AUTO_ZLP = 1;
	
	USB->DEVICE.DeviceEndpoint[0].EPINTENSET.reg = USB_DEVICE_EPINTENSET_RXSTP|USB_DEVICE_EPINTENSET_TRCPT1;
	USB->DEVICE.DeviceEndpoint[0].EPCFG.bit.EPTYPE0 = 1;	//SETUP
	USB->DEVICE.DeviceEndpoint[0].EPCFG.bit.EPTYPE1 = 1;	//IN
	USB->DEVICE.DeviceEndpoint[0].EPSTATUSSET.bit.BK0RDY = 1;
	
	USB->DEVICE.DeviceEndpoint[2].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0|USB_DEVICE_EPINTENSET_TRFAIL0|USB_DEVICE_EPINTENSET_STALL0;
	USB->DEVICE.DeviceEndpoint[2].EPCFG.bit.EPTYPE0 = 0x4;	//Interrupt5555
	USB->DEVICE.DeviceEndpoint[2].EPCFG.bit.EPTYPE1 = 0x0;	//DISABLED
	USB->DEVICE.DeviceEndpoint[2].EPSTATUSCLR.bit.BK0RDY = 1;
}

inline void usb_enable_ep(uint8_t ep, uint32_t* data, uint8_t type, uint16_t bufsize) {
	if (ep & 0x80) {
		ep &= 0x3f;
		EP[ep].Bank[1].nonArray.ADDR.reg = (uint32_t)data;
		EP[ep].Bank[1].nonArray.PCKSIZE.bit.SIZE = USB_bufSize(bufsize);
		USB->DEVICE.DeviceEndpoint[ep].EPCFG.bit.EPTYPE1 = type;
		USB->DEVICE.DeviceEndpoint[ep].EPSTATUSCLR.reg =
		USB_DEVICE_EPSTATUS_BK1RDY
		| USB_DEVICE_EPSTATUS_STALLRQ(0x2)
		| USB_DEVICE_EPSTATUS_DTGLIN;
		USB->DEVICE.DeviceEndpoint[ep].EPINTENSET.reg = 0b01001010;
		} else {
		ep &= 0x3f;
		EP[ep].Bank[0].nonArray.ADDR.reg = (uint32_t)data;
		EP[ep].Bank[0].nonArray.PCKSIZE.bit.SIZE = USB_bufSize(bufsize);
		EP[ep].Bank[0].nonArray.PCKSIZE.bit.AUTO_ZLP = 1;
		USB->DEVICE.DeviceEndpoint[ep].EPCFG.bit.EPTYPE0 = type;
		USB->DEVICE.DeviceEndpoint[ep].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_BK0RDY;
		USB->DEVICE.DeviceEndpoint[ep].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_DTGLOUT;
		USB->DEVICE.DeviceEndpoint[ep].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0|USB_DEVICE_EPINTENSET_TRFAIL0|USB_DEVICE_EPINTENSET_STALL0;
	}
}

inline void usb_disable_ep(uint8_t ep) {
	if (ep & 0x80) {
		USB->DEVICE.DeviceEndpoint[ep & 0x3f].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_BK1RDY;
		USB->DEVICE.DeviceEndpoint[ep & 0x3f].EPCFG.bit.EPTYPE1 = 0;
		} else {
		USB->DEVICE.DeviceEndpoint[ep & 0x3f].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_BK0RDY;
		USB->DEVICE.DeviceEndpoint[ep & 0x3f].EPCFG.bit.EPTYPE0 = 0;
	}
}

static inline void usb_detach(void) {
	USB->DEVICE.CTRLB.bit.DETACH = 1;
	NVIC_DisableIRQ(USB_IRQn);
}

static inline void usb_attach(void) {
	NVIC_EnableIRQ(USB_IRQn);
	USB->DEVICE.CTRLB.bit.DETACH = 0;
}

inline void usb_ep_start_out(uint8_t ep, uint32_t* data, uint8_t len) {
	EP[ep].Bank[0].nonArray.PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
	EP[ep].Bank[0].nonArray.PCKSIZE.bit.BYTE_COUNT = len;
	EP[ep].Bank[0].nonArray.ADDR.reg = (uint32_t) data;
	USB->DEVICE.DeviceEndpoint[ep].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0 | USB_DEVICE_EPINTFLAG_TRFAIL0;
	USB->DEVICE.DeviceEndpoint[ep].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0;
	USB->DEVICE.DeviceEndpoint[ep].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_BK0RDY;
}

inline void usb_ep_start_in(uint8_t ep, uint32_t* data, uint8_t size, uint8_t zlp) {
	ep &= 0x3f;
	EP[ep].Bank[1].nonArray.PCKSIZE.bit.AUTO_ZLP = zlp;
	EP[ep].Bank[1].nonArray.PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
	EP[ep].Bank[1].nonArray.PCKSIZE.bit.BYTE_COUNT = size;
	EP[ep].Bank[1].nonArray.ADDR.reg = (uint32_t) data;
	USB->DEVICE.DeviceEndpoint[ep].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1 | USB_DEVICE_EPINTFLAG_TRFAIL1;
	USB->DEVICE.DeviceEndpoint[ep].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT1;
	USB->DEVICE.DeviceEndpoint[ep].EPSTATUSSET.reg = USB_DEVICE_EPSTATUS_BK1RDY;
}

/// Enable the OUT stage on the default control pipe.
inline void usb_ep0_out(void) {
	usb_ep_start_out(0x00, EP0OUT.u32, 64);
}

inline void usb_ep0_in(uint8_t size){
	usb_ep_start_in(0x80, EP0IN.u32, size, 0x1);
}



inline void usb_stop_INT(uint8_t ep) {
	ep &= 0x3f;
	USB->DEVICE.DeviceEndpoint[ep].EPSTATUSSET.reg = USB_DEVICE_EPSTATUS_CURBK;
	USB->DEVICE.DeviceEndpoint[ep].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_BK1RDY|USB_DEVICE_EPSTATUS_STALLRQ(0x2);
	USB->DEVICE.DeviceEndpoint[ep].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1|USB_DEVICE_EPINTFLAG_TRFAIL1|USB_DEVICE_EPINTFLAG_STALL1;
}

inline void usb_set_INT(uint8_t ep, uint8_t size) {
	if(ep & 0x80){
		ep &= 0x3f;
		EP[ep].Bank[1].nonArray.PCKSIZE.bit.BYTE_COUNT = size;
		USB->DEVICE.DeviceEndpoint[ep].EPSTATUSSET.reg = USB_DEVICE_EPSTATUS_BK1RDY|USB_DEVICE_EPSTATUS_CURBK;
		USB->DEVICE.DeviceEndpoint[ep].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_STALLRQ(0x2);
		USB->DEVICE.DeviceEndpoint[ep].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1 | USB_DEVICE_EPINTFLAG_TRFAIL1;	
	} else {
		EP[ep].Bank[0].nonArray.PCKSIZE.bit.BYTE_COUNT = 0;
		USB->DEVICE.DeviceEndpoint[ep].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_CURBK|USB_DEVICE_EPSTATUS_BK0RDY;//|USB_DEVICE_EPSTATUS_STALLRQ(0x1);
		//USB->DEVICE.DeviceEndpoint[ep].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0 | USB_DEVICE_EPINTFLAG_TRFAIL0;
		USB->DEVICE.DeviceEndpoint[ep].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0|USB_DEVICE_EPINTENSET_TRFAIL0|USB_DEVICE_EPINTENSET_STALL0;
	}
	
}


void initUSB_IO(){
	PORT->Group[0].PMUX[12].bit.PMUXE = 0x6;
	PORT->Group[0].PMUX[12].bit.PMUXO = 0x6;
	PORT->Group[0].PINCFG[24].reg = 0x03;
	PORT->Group[0].PINCFG[25].reg = 0x03;
	PORT->Group[0].DIRSET.reg = PORT_PA24|PORT_PA25;
	
}



void USB_init(){
	
	MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
	MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
	//Enable GCLK for TCC0 and link it to GCLK0 see "PCHCTRLm Mapping" from product datasheet
	GCLK->PCHCTRL[4].reg = (GCLK_PCHCTRL_CHEN|GCLK_PCHCTRL_GEN(4));		//16M
	
	while(USB->DEVICE.SYNCBUSY.bit.SWRST);
	USB->DEVICE.CTRLA.bit.SWRST = 1;
	while(USB->DEVICE.SYNCBUSY.bit.SWRST);
	
	//Calibration NVM reg
	uint32_t padcal = (*(uint32_t*)(0x806020));
	USB->DEVICE.PADCAL.bit.TRANSN = (padcal >> 13) & 0x1F;
	USB->DEVICE.PADCAL.bit.TRANSP = (padcal >> 18) & 0x1F;
	USB->DEVICE.PADCAL.bit.TRIM = (padcal >> 23) & 0x07;
	
	USB->DEVICE.CTRLA.bit.MODE = 0;
	USB->DEVICE.CTRLB.bit.SPDCONF = 0x0;
	USB->DEVICE.QOSCTRL.reg |= 0x0F;
	
	memset(EP, 0, ENDPOINTS_COUNT*sizeof(USB_Endpoint));
	USB->DEVICE.DESCADD.reg = (uint32_t)(EP);

	NVIC_EnableIRQ(USB_IRQn);
	USB->DEVICE.INTENSET.bit.EORST = 1;

	
	while(USB->DEVICE.SYNCBUSY.bit.ENABLE);
	USB->DEVICE.CTRLA.bit.ENABLE = 1;
	while(USB->DEVICE.SYNCBUSY.bit.ENABLE);
	
	USB_reset();
	usb_ep_start_out(0, EP0OUT.u32, 64);

	status.bPowered = confDesc.bmAttributes.bit.bPow;
	status.remoteWU = confDesc.bmAttributes.bit.remoteWU;
}



inline void usb_handle_controlIN(){
	if ((setupPacket.nonArray.bmRequestType & USB_REQTYPE_TYPE_MASK) == USB_REQTYPE_STANDARD) {
		switch (setupPacket.nonArray.bRequest){
			case USB_REQ_SetAddress:
				USB->DEVICE.DADD.bit.DADD = setupPacket.nonArray.wValue;
				USB->DEVICE.DADD.bit.ADDEN = 1;
				usb_ep0_out();
				return;
			case USB_REQ_GetDescriptor:
				usb_ep0_out();
				return;
			default:
				usb_ep0_out();
		}
	}
}


void usb_handle_setup(){
	if(setupPacket.nonArray.bmRequestType == 0xA1 || setupPacket.nonArray.bmRequestType == 0x21){
		switch(setupPacket.nonArray.bRequest){
			case USB_REQ_SetIdle:
				usb_ep0_in(0);
			break;
		}
	} else {
		switch(setupPacket.nonArray.bRequest){
			case USB_REQ_GetStatus:
				break;
			case USB_REQ_ClearFeature:
				status.bPowered = 0;
				confDesc.bmAttributes.bit.bPow = 0;
			
				usb_ep0_in(0);
				break;
			case USB_REQ_SetFeature:
				break;
			case USB_REQ_SetAddress:
				usb_ep0_in(0);
				break;
			case USB_REQ_GetDescriptor:			
				switch((uint8_t)(setupPacket.nonArray.wValue >> 8)){
					case USB_DTYPE_Device:
						memset(EP0IN.u32, 0, setupPacket.nonArray.wLength);
						memcpy(EP0IN.u32, &devDesc, sizeof(deviceDescriptor));
					
						usb_ep_start_in(0x80, EP0IN.u32, setupPacket.nonArray.wLength, 1);
						break; 
					case USB_DTYPE_Configuration:
						if(setupPacket.nonArray.wLength == sizeof(configurationDescriptor)){
							memset(EP0IN.u32, 0, sizeof(configurationDescriptor));
							memcpy(EP0IN.u8, &confDesc, sizeof(configurationDescriptor));
						
							usb_ep_start_in(0x80, EP0IN.u32, setupPacket.nonArray.wLength, 1);
						} else {
							uint8_t copyPosition = 0;
							memset(EP0IN.u32, 0, sizeof(USB_EndpointData));
						
							memcpy(EP0IN.u8 + copyPosition, &confDesc, sizeof(configurationDescriptor));
							copyPosition += sizeof(configurationDescriptor);
						
							memcpy(EP0IN.u8 + copyPosition, &intDesc0, sizeof(interfaceDescriptor));
							copyPosition += sizeof(interfaceDescriptor);
						
							memcpy(EP0IN.u8 + copyPosition, &hidDesc0, sizeof(hidDescriptor));
							copyPosition += sizeof(hidDescriptor);
						
							memcpy(EP0IN.u8 + copyPosition, &endpointDesc1, sizeof(endpointDescriptor));
							copyPosition += sizeof(endpointDescriptor);
							
							memcpy(EP0IN.u8 + copyPosition, &intDesc1, sizeof(interfaceDescriptor));
							copyPosition += sizeof(interfaceDescriptor);
							
							memcpy(EP0IN.u8 + copyPosition, &hidDesc1, sizeof(hidDescriptor));
							copyPosition += sizeof(hidDescriptor);
							
							memcpy(EP0IN.u8 + copyPosition, &endpointDesc2, sizeof(endpointDescriptor));
							copyPosition += sizeof(endpointDescriptor);
						
							testPacket[0] = 1;
						
							usb_ep_start_in(0x80, EP0IN.u32, copyPosition, 1);
						}
						break;
					case USB_DTYPE_HID:
						memcpy(EP0IN.u32, &hidDesc0, sizeof(hidDescriptor));
						usb_ep_start_in(0x80, EP0IN.u32, sizeof(hidDescriptor), 1);
						break;
					case USB_DTYPE_Interface:
						memcpy(EP0IN.u32, &intDesc0, sizeof(interfaceDescriptor));
						//usb_enable_ep(0x80, 1, sizeof(USB_DescriptorBank));
						usb_ep_start_in(0x80, EP0IN.u32, sizeof(interfaceDescriptor), 1);
						break;
					case USB_DTYPE_Report:
						if (setupPacket.nonArray.wIndex == 0) {
							memset(EP0IN.u8, 0, sizeof(reportDescriptor0));
							memcpy(EP0IN.u8, &reportDescriptor0, sizeof(reportDescriptor0));
							usb_ep_start_in(0x80, EP0IN.u32, sizeof(reportDescriptor0), 1);
						} else {
							memset(EP0IN.u8, 0, sizeof(reportDescriptor0));
							memcpy(EP0IN.u8, &reportDescriptor1, sizeof(reportDescriptor1));
							usb_ep_start_in(0x80, EP0IN.u32, sizeof(reportDescriptor1), 1);
						}
						break;
				}
				break;
			case USB_REQ_SetDescriptor:
				break;
			case USB_REQ_GetConfiguration:
				memset(EP0IN.u32, 0, setupPacket.nonArray.wLength);
				memcpy(EP0IN.u32, &usbConfiguration, sizeof(usbConfiguration));
				//usb_enable_ep(0x80, 1, sizeof(USB_DescriptorBank));
				usb_ep_start_in(0x80, EP0IN.u32, 1, setupPacket.nonArray.wLength);
				break;
			case USB_REQ_SetConfiguration:
				usbConfiguration = setupPacket.nonArray.wValue;
				usb_ep0_in(0);
				usb_ep0_out();
				break;
			case USB_REQ_GetInterface:
				break;
			case USB_REQ_SetInterface:
				break;
			case USB_REQ_SynchFrame:
				break;
		}
	}
}void USB_Handler(){
	uint32_t summary = USB->DEVICE.EPINTSMRY.reg;
	uint32_t status = USB->DEVICE.INTFLAG.reg;
	uint32_t flags;
	
	if (status & USB_DEVICE_INTFLAG_EORST) {
		USB_reset();
		usb_enable_ep(0x02, EP2OUT.u32, 0x4, 64);
		usb_enable_ep(0x81, EP1IN.u32, 0x4, 8);
		usb_stop_INT(0x81);
		usb_set_INT(0x02, 0);
		USB->DEVICE.INTFLAG.reg |= USB_DEVICE_INTFLAG_EORST;
		return;	
	}
	
	if (summary & 1) {
		flags = USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg;
		if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
			
			memcpy(&setupPacket, &EP0OUT.u32, sizeof(USB_SetupPacket));
			
			usb_handle_setup();
			USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_RXSTP;
		}
		if (flags & USB_DEVICE_EPINTFLAG_TRCPT0) {
			//usb_handle_controlOUT();
			//USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRCPT0;
		}
		if (flags & USB_DEVICE_EPINTFLAG_TRCPT1) {
			usb_handle_controlIN();
			USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRCPT1;
		}
		USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg |= flags;
	}
	if (summary & 2){
		flags = USB->DEVICE.DeviceEndpoint[1].EPINTFLAG.reg;
		if (flags & USB_DEVICE_EPINTFLAG_TRCPT1) {
			//PORT->Group[0].OUTTGL.reg = PORT_PA07;
			usb_stop_INT(0x81);
			//appendMessage16('a',0xFFFF);
			USB->DEVICE.DeviceEndpoint[1].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRCPT1;
		}
		if (flags & USB_DEVICE_EPINTFLAG_STALL1) {
			//appendMessage16('a',0x8001);
			USB->DEVICE.DeviceEndpoint[1].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_STALLRQ1;
			USB->DEVICE.DeviceEndpoint[1].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_STALL1;
		}
		if (flags & USB_DEVICE_EPINTFLAG_TRFAIL1) {
			//appendMessage16('a',0x8181);
			USB->DEVICE.DeviceEndpoint[1].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1;
		}
		
		USB->DEVICE.DeviceEndpoint[1].EPINTFLAG.reg |= flags;
	}
	
	if (summary & 4){
		flags = USB->DEVICE.DeviceEndpoint[2].EPINTFLAG.reg;
		if (flags & USB_DEVICE_EPINTFLAG_TRCPT0) {
			switch(EP2OUT.u8[0]){
				case 1:
					memcpy((void*)&pattern.array, (void*)&EP2OUT.u8[1], 6);
					//USB->DEVICE.DeviceEndpoint[2].EPSTATUSCLR.bit.DTGLOUT = 1;
					patternSaved = 0;
					break;
				case 2:
					currentIndex = EP2OUT.u8[1];
					if (EP2OUT.u8[2] == 0) memcpy(&pattern.nonArray.FRAME.structFrame[currentIndex].TIMEFRAME_STRUCT[0], &EP2OUT.u8[3], 9*sizeof(LED_COLOR));
					else memcpy(&pattern.nonArray.FRAME.structFrame[currentIndex].TIMEFRAME_STRUCT[9], &EP2OUT.u8[3], 9*sizeof(LED_COLOR));
					break;
				case 3:
					if (EP2OUT.u8[1] == 0 && patternSaved == 0) eepromTransferFlag |= 1;
					else {
						eepromTransferFlag |= (1 << EP2OUT.u8[1]);
						commandValue = ((uint16_t)EP2OUT.u8[3] << 8) | EP2OUT.u8[2];
						eepromTransferFlag &= 0xFE;
						memcpy(commandPacket, &EP2OUT.u8[4], sizeof(commandPacket));
					}
				break;
			}
			usb_set_INT(0x02, 0);
			//appendMessage16('a',0xFFFF);
			USB->DEVICE.DeviceEndpoint[2].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRCPT0;
		}
		flags = USB->DEVICE.DeviceEndpoint[2].EPINTFLAG.reg;
		if (flags & USB_DEVICE_EPINTFLAG_STALL0) {
			//appendMessage16('a',0x8001);
			USB->DEVICE.DeviceEndpoint[2].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUS_STALLRQ0;
			USB->DEVICE.DeviceEndpoint[2].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_STALL0;
		}
		flags = USB->DEVICE.DeviceEndpoint[2].EPINTFLAG.reg;
		if (flags & USB_DEVICE_EPINTFLAG_TRFAIL0) {
			//appendMessage16('a',0x8181);
			if(EP[2].Bank[0].nonArray.STATUS_BK.bit.ERRORFLOW ) {
				USB->DEVICE.DeviceEndpoint[2].EPSTATUSCLR.bit.BK0RDY = 1;
				EP[2].Bank[0].nonArray.STATUS_BK.bit.ERRORFLOW = 0;
			}
			USB->DEVICE.DeviceEndpoint[2].EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0;
		}
		USB->DEVICE.DeviceEndpoint[2].EPINTFLAG.reg |= flags;
	}
}

uint8_t findID(uint16_t recordIndex){
	int idIndex = 0;
	for (idIndex = 0; idIndex < PAGESIZE/sizeof(RECORD); idIndex++){
		if (indexTable.structure[idIndex].ID == recordIndex) return idIndex;
	}
	return idIndex;
}

void savePattern(void){
	int i = findID(pattern.nonArray.ID);
	if(i == (PAGESIZE/sizeof(RECORD))){
		i = findID(0xFFFF);	
	}
	if(i != (PAGESIZE/sizeof(RECORD))){
		indexTable.structure[i].ID = pattern.nonArray.ID;
		indexTable.structure[i].length = pattern.nonArray.FNUM;
		indexTable.structure[i].pageNumber = 29*(i + 1);
		I2C_MasterWriteBytes(ADDRESS0, i*sizeof(RECORD), sizeof(RECORD), &indexTable.structure[i]);
		totalLength = 6 + 72*indexTable.structure[i].length;
		if ((totalLength % PAGESIZE) != 0) lengthInPages = totalLength/PAGESIZE + 1;
		else lengthInPages = totalLength/PAGESIZE;
		for (int j = 0; j < lengthInPages; j++) I2C_MasterWritePage(ADDRESS0, indexTable.structure[i].pageNumber + j, (uint8_t *)&pattern.array[j*PAGESIZE]);
	}
}

uint8_t activatePattern(uint16_t ID){
	int i = findID(ID);
	if(i != (PAGESIZE/sizeof(RECORD))){
		activeIndex = i;
		I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE, (uint8_t)activeIndex);
		I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE+1, activeIndex >> 8);
		//totalLength = 6 + 72*indexTable.structure[i].length;
		//if ((totalLength % PAGESIZE) != 0) lengthInPages = totalLength/PAGESIZE + 1;
		//else lengthInPages = totalLength/PAGESIZE;
		//for (int j = 0; j < lengthInPages; j++) 
		//I2C_MasterReadPage(ADDRESS0, indexTable.structure[activeIndex].pageNumber + j, (uint8_t *)&activePattern.array[j*PAGESIZE]);
	}
}

void resetPattern(uint16_t ID){
	if (ID == 0xFFFF) {
		memset((void *)&indexTable, 0xFF, sizeof(TABLE));
		I2C_MasterWritePage(ADDRESS0, 0, &indexTable);
	} else {
		int i = findID(ID);
		if(i != (PAGESIZE/sizeof(RECORD))){
			I2C_MasterWriteBytes(ADDRESS0, i*sizeof(RECORD), sizeof(RECORD), &blankRecord);
			memset((void *)&indexTable.structure[i], 0xFF, sizeof(RECORD));
		}
	}
}

void loadPattern(){
	if(activeIndex < 32){
		if(indexTable.structure[activeIndex].ID != 0xFFFF) {
			totalLength = 6 + 72*indexTable.structure[activeIndex].length;
			if ((totalLength % PAGESIZE) != 0) lengthInPages = totalLength/PAGESIZE + 1;
			else lengthInPages = totalLength/PAGESIZE;
			for (int i = 0; i < lengthInPages; i++){
				I2C_MasterReadPage(ADDRESS0, indexTable.structure[activeIndex].pageNumber + i, (uint8_t *)&activePattern.array[i*PAGESIZE]);
			}
		}	
	}
}

void updateIntensity(uint16_t newIntensity){
	
	intensity = (uint8_t)newIntensity;
	I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE + 3, intensity);
}

void updateLEDIntensity(uint16_t newIntensity){
	
	overallIntensity = (uint8_t)newIntensity;
	I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE + 2, overallIntensity);
}

void updateRecord(uint16_t ID){
	int i = findID(ID);
	if(i == (PAGESIZE/sizeof(RECORD))) return;
	
	indexTable.structure[i].ID = ((uint16_t)EP2OUT.u8[5] << 8) | EP2OUT.u8[4];
	indexTable.structure[i].length = EP2OUT.u8[8];
	
	
	I2C_MasterWriteBytes(ADDRESS0, i*sizeof(RECORD), sizeof(RECORD), &indexTable.structure[i]);
	I2C_MasterWriteBytes(ADDRESS0, indexTable.structure[i].pageNumber*PAGESIZE, sizeof(commandPacket), &commandPacket);
}

void updateFrequency(uint32_t freq){
	debounceFrequency_Hz = freq;
	
	enableBouncerOsc(freq);
	I2C_MasterWriteSingle(ADDRESS0, PAGESIZE + 8, 4, freq);
}

void loadState(void){
	I2C_MasterReadPage(ADDRESS0, 0, indexTable.array);
	activeIndex = (uint16_t)I2C_MasterReadSingle(ADDRESS0, PAGESIZE, 2);
	//activeIndex = (uint16_t)
	overallIntensity = I2C_MasterReadSingleByte(ADDRESS0, PAGESIZE + 2);
	intensity = I2C_MasterReadSingleByte(ADDRESS0, PAGESIZE + 3);
	intensityExp = I2C_MasterReadSingleByte(ADDRESS0, PAGESIZE + 4);
	debounceFrequency_Hz = I2C_MasterReadSingle(ADDRESS0, PAGESIZE + 8, 4);
	if(overallIntensity > 31) overallIntensity = 22;
	loadPattern();
}

void setPriority(uint8_t nvicNumber, uint8_t value) {
	if(value > 3) value = 3;
	if(value < 0) value = 0;
	value = value << 6;
	
	uint32_t regValue = value << ((nvicNumber % 4)*8);
	uint32_t zeroValue = ~(0xC0 << ((nvicNumber % 4)*8));

	uint32_t lastRegValue = NVIC->IP[nvicNumber/4]; 
	lastRegValue &= zeroValue;
	lastRegValue |= regValue;
	NVIC->IP[nvicNumber/4] = lastRegValue;
}

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();

	DPLL_init();
	
	initUSB_IO();
	USB_init();
	usb_attach();
	
	TCC0_init();
	
	SPIMasterInit();

	//TCC0_init();
	//TCC0->CTRLBSET.bit.CMD = 0x2;
	
	IO_initI2CMaster();
	I2C_MasterInit();
	//I2C_MasterWritePage(ADDRESS0, 0, indexTable.array);
	
	//memset(indexTable.array, 0, 2*sizeof(RECORD));
	
	
	set32kClk();
	enableGCLK3_32k();
	
	

	initKeyboardISR();
	
	memset(NVIC->IP, 0xC0C0C0C0, 8*sizeof(uint32_t));
	
	setPriority(TC0_IRQn, 0x01);
	setPriority(TC1_IRQn, 0x01);
	setPriority(EIC_IRQn, 0x01);
	setPriority(USB_IRQn, 0x00);
	
	loadState();
	//I2C_MasterWritePage(ADDRESS0, 0, patternArray);
	//I2C_MasterReadPage(ADDRESS0, 0, (uint8_t *)&pattern.array[2]);
	/*
	
	pattern.nonArray.FNUM = 100;
	pattern.nonArray.STEP = 25;
	
	memcpy(&pattern.nonArray.FRAME.arrayFrame, patternArray, sizeof(patternArray));
	SPI_playPattern(&pattern);
	*/
	//overallIntensity = 16;
	enableBouncerOsc(debounceFrequency_Hz);
	SPI_playPattern(&activePattern);
	//updateLEDIntensity(4);
	
    while (1) 
    {
		if(keyChangeFlag){
			usb_set_INT(0x81, 4);
//			appendMessage16('a', 0x8001);
			
			keyChangeFlag = 0;
		}
		
		if(patternChangeFlag){
			uint8_t floatingIndex = activeIndex;
			if(patternChangeFlag == 1) {
				if(floatingIndex > 0) floatingIndex--;
				while((floatingIndex > 0) && (indexTable.structure[floatingIndex].ID == 0xFFFF)) floatingIndex--;
				if(indexTable.structure[floatingIndex].ID != 0xFFFF){
					if(activeIndex != floatingIndex) {
						SPI_stopPattern();
						activeIndex = floatingIndex;
						loadPattern();
						I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE, (uint8_t)activeIndex);
						I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE+1, activeIndex >> 8);
						SPI_playPattern(&activePattern);	
					}
				}
			} else {
				if(floatingIndex < 30) floatingIndex++;
				while((floatingIndex < 30) && (indexTable.structure[floatingIndex].ID == 0xFFFF)) floatingIndex++;
				if(indexTable.structure[floatingIndex].ID != 0xFFFF){
					SPI_stopPattern();
					activeIndex = floatingIndex;
					loadPattern();
					I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE, (uint8_t)activeIndex);
					I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE+1, activeIndex >> 8);
					SPI_playPattern(&activePattern);	
				}
			}
			patternChangeFlag = 0;
		}
		
		if(intensityChangeFlag){
			if (intensityExp == 1) intensity = 0;
			else {
				if (intensityExp >= 8) {
					intensity = 255;
					intensityExp = 8;
				} else intensity = 1 << intensityExp;
			}
			I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE + 2, overallIntensity);
			I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE + 3, intensity);
			I2C_MasterWriteSingleByte(ADDRESS0, PAGESIZE + 4, intensityExp);
			intensityChangeFlag = 0;
		}
		
		if(eepromTransferFlag){
			if(eepromTransferFlag & USB_CMD_SAVE) {
				savePattern();
				patternSaved = 1;
				eepromTransferFlag &= (~USB_CMD_SAVE);
			}
			
			if(eepromTransferFlag & USB_CMD_ACTIVATE) {
				SPI_stopPattern();
				activatePattern((uint16_t)commandValue);
				loadPattern();
				SPI_playPattern(&activePattern);
				eepromTransferFlag &= (~USB_CMD_ACTIVATE);
			}
			
			if(eepromTransferFlag & USB_CMD_RESET) {
				resetPattern((uint16_t)commandValue);
				eepromTransferFlag &= (~USB_CMD_RESET);
			}
			
			if(eepromTransferFlag & USB_CMD_INTENSITY) {
				updateIntensity((uint16_t)commandValue);
				eepromTransferFlag &= (~USB_CMD_INTENSITY);
			}
			
			if(eepromTransferFlag & USB_CMD_UPDATE) {
				updateRecord((uint16_t)commandValue);
				eepromTransferFlag &= (~USB_CMD_UPDATE);
			}
			
			if(eepromTransferFlag & USB_CMD_LED_INTENSITY) {
				updateLEDIntensity((uint16_t)commandValue);
				eepromTransferFlag &= (~USB_CMD_LED_INTENSITY);
			}
			
			if(eepromTransferFlag & USB_CMD_DEBOUNCE_HZ) {
				updateFrequency(commandValue);
				eepromTransferFlag &= (~USB_CMD_DEBOUNCE_HZ);
			}
		}
    }
}