/**
 * \file
 *
 * \brief User Interface
 *
 * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip
 * software and any derivatives exclusively with Microchip products.
 * It is your responsibility to comply with third party license terms applicable
 * to your use of third party software (including open source software) that
 * may accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
 * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
 * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
 * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
 * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
 * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
 * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
 * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
 * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
 *
 * \asf_license_stop
 *
 */
/*
 * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
 */

#include <asf.h>
#include "ui.h"

/** HID report buffer */
static uint8_t ui_hid_report[UDI_HID_REPORT_OUT_SIZE];

void ui_init(void)
{
	/* Initialize LEDs */
	LED_Off(LED0);
	//LED_Off(LED1);
}

void ui_powerdown(void)
{
	LED_Off(LED0);
	//LED_Off(LED1);
}


void ui_wakeup_enable(void)
{
}

void ui_wakeup_disable(void)
{
}

void ui_wakeup(void)
{
	LED_On(LED0);
}

void ui_process(uint16_t framenumber)
{
	bool b_btn_state;
	static bool btn0_last_state = false;
	static bool btn1_last_state = false;
	static uint8_t cpt_sof = 0;

	if ((framenumber % 1000) == 0) {
		LED_On(LED0);
	}
	if ((framenumber % 1000) == 500) {
		LED_Off(LED0);
	}
	/* Scan process running each 40ms */
	cpt_sof++;
	if (cpt_sof < 40) {
		return;
	}
	cpt_sof = 0;

	/* Scan push buttons 1 and 2 */
	/*
	b_btn_state = !ioport_get_pin_level(GPIO_PUSH_BUTTON_1);
	if (b_btn_state != btn0_last_state) {
		ui_hid_report[0]=b_btn_state;
		udi_hid_generic_send_report_in(ui_hid_report);
		btn0_last_state = b_btn_state;
	}
	b_btn_state = !ioport_get_pin_level(GPIO_PUSH_BUTTON_2);
	if (b_btn_state != btn1_last_state) {
		ui_hid_report[0]=b_btn_state;
		udi_hid_generic_send_report_in(ui_hid_report);
		btn1_last_state = b_btn_state;
	}
	*/
}

void ui_led_change(uint8_t *report)
{
	if (report[0]=='1') {
		/* A led must be on */
		switch(report[1]) {
			case '1':
			LED_On(LED2);
			break;
		}
	} else {
		/* A led must be off */
		switch(report[1]) {
			case '1':
			LED_Off(LED2);
			break;
		}
	}
}

report_send_typ Report_send_struct;
cmd_op_typ Cmd_received_struct;
uint8_t	   Connection_Status=IDLE;
void Push_Send_Report(uint8_t status, uint8_t cmd, uint8_t length, uint8_t * src)
{
	uint8_t i=0;
	uint8_t checksum=0;
	if((length <= MAX_LENGTH) && (length >= MIN_LENGTH))
	{
		if(Report_send_struct.report_valid == IDLE)
		{
			Report_send_struct.report_valid=PENDING;
			Report_send_struct.buf[0]= HEADER;
			Report_send_struct.buf[1]= status;
			Report_send_struct.buf[2]= cmd;
			checksum=0;
			Report_send_struct.buf[3]=length;
			for(i=0;i<length;i++)
			{
				Report_send_struct.buf[4+i]= *(src+i);
				//checksum += Report_send_struct.buf[4+i];
			}
			for(i=1;i<(4+length);i++)
				checksum += Report_send_struct.buf[i];
			checksum = (checksum == TAIL)? (TAIL+1):checksum;
			Report_send_struct.buf[4+length] = checksum;
			Report_send_struct.buf[4+length+1] = Report_send_struct.buf[4+length+2] = TAIL;
			Report_send_struct.report_len=4+length+2;
		}
	}
	
}

bool Check_Op_status(void)
{
	if (Cmd_received_struct.cmd_valid==IDLE)
		return true;
	else
		return false;	
}

bool Is_Cmd_Pending(void)
{
	if (Cmd_received_struct.cmd_valid==PENDING)
		return true;
	else
		return false;
}


void Push_cmd_Op(uint8_t cmd, uint8_t typ, uint8_t length, uint8_t * src, cmd_op_typ * p)
{
	uint8_t i=0;

	if((length < MAX_LENGTH) && (length > MIN_LENGTH))
	{
		p->cmd=cmd;
		p->typ=typ;
		p->data_len=length;
		for(i=0;i<length;i++)
			p->buf[i]= *(src+i);
		p->cmd_valid=PENDING;
	}
	
}
uint8_t Write_packet_Buf[2000]={0};
uint16_t Write_packet_idx=0;
uint16_t Wp_fill_length=0;

uint8_t Read_packet_Buf[2000]={0};
uint16_t Read_packet_idx=0;	
uint16_t Rp_fill_length=0;
// command execution
uint32_t	zero[32]={0};
u32_union	Packet_W[64];
uint8_t     Packet_W_idx=0;
uint8_t     Packet_W_byte_len=0;
void cmd_Op(cmd_op_typ * p)
{
	uint8_t reply,cmd, typ,reply_len,u8temp,u8temp1,u8temp2,Ya, chip_sel;
	uint8_t buf[64];
	uint16_t adr, length, len_remain,Xa;
	uint8_t i=0;

	if(p->cmd_valid==PENDING)
	{
		reply=ERR_NONE;
		cmd=p->cmd;
		typ=p->typ;
		reply_len=1;
		buf[0]=typ;
		switch(cmd)
		{		
			case CMD_SET:
					
					switch(typ)
					{
						case TYP_MUX_CTRL:
								u8temp = p->buf[0] & 0x3;
								MUX_Select((u8temp & 0x1),  (u8temp & 0x2));
						break;
						
						case TYP_RST_CTRL:
								PwerOnRst();
						break;
						
						case TYP_LED_CTRL:
								u8temp = p->buf[0] & 0x1;
								if(u8temp)
									LED_On(LED0);
								else
									LED_Off(LED0);
						break;	
									
						default:
								reply = ERR_TYP;
							break;
					}
				break;
				
			case CMD_PRG:
					
					switch(typ)
					{
						case TYP_CHIP_ERASE:
								u8temp = p->buf[0] & 0xf; // only support 4 chip
								u8temp1= p->buf[1];		  // trim data
								if(u8temp < MAX_CHIP_NUM)
								{
									ChipErase(u8temp, u8temp1);
								}
								else
								{
									reply=OUT_RANGE;
								}
							
						break;
											
						case TYP_SECT_ERASE:
								u8temp = p->buf[0] & 0xf; // only support 4 chip
								u8temp1= p->buf[1];		  // trim data
								adr =p->buf[3];
								adr <<=8;
								adr |= p->buf[2];
								if(u8temp < MAX_CHIP_NUM)
								{
									SectorErase(u8temp, u8temp1, adr, 1);
								}
								else
								{
									reply=OUT_RANGE;
								}
						break;
											
						case TYP_WR_PACKET:  ////format |packet idx|valid data length followed| data |
								u8temp1 = p->buf[0]; 
								u8temp = p->buf[1];
								if(u8temp < MAX_DATA_LENTH)
								{
									for(i=0;i<u8temp;i++)
									{
										*((uint8_t *)(&Packet_W[0])+Packet_W_byte_len)=p->buf[2+i];
										Packet_W_byte_len++;
										if(Packet_W_byte_len >= sizeof(Packet_W))
										{
											reply=OUT_RANGE;
											break;
										}
										
									}

								}
								else
									reply=ERR_LENGTH;							
						break;

						case TYP_TEST_PRG:
								chip_sel = p->buf[0]; // chip idx
								zero[0] = p->buf[1]; // test prog data
								u8temp	= p->buf[2]; // PRGDIG_HV
								u8temp1= p->buf[3];	 // ENIP_PRGDIG
								u8temp2 =p->buf[4];  //ENBIAS_PRGDIG

								for(i=1;i<32;i++)
									zero[i]=zero[0];
								Dig_Prog(chip_sel, u8temp,u8temp1,u8temp2, &zero[0], 32,0, 0);		
						break;

						case TYP_DIG_PRG:
						        chip_sel = p->buf[0];
								u8temp	= p->buf[1]; // PRGDIG_HV
								u8temp1= p->buf[2];	 // ENIP_PRGDIG
								u8temp2 =p->buf[3];  //ENBIAS_PRGDIG
								length = p->buf[5]; 
								length <<=8;
								length |= p->buf[4];
								Xa = p->buf[7];
								Xa <<=8;
								Xa |= p->buf[6];
								Ya= p->buf[8];
								Dig_Prog(chip_sel, u8temp,u8temp1,u8temp2, (uint32_t *)(&Packet_W[0]), length,Xa, Ya);
						break;
											
						default:
							reply = ERR_TYP;			
						break;
					}
				break;	

			case CMD_READ:
					
					switch(typ)
					{
						case TYP_DIG_RD:
								u8temp = p->buf[0] & 0xf; // only support 4 chip
								
								if(u8temp < MAX_CHIP_NUM)
								{
									u8temp1= p->buf[1];		  // ya
									adr =p->buf[3];
									adr <<=8;
									adr |= p->buf[2];         // xa
								
								/*
									length =p->buf[5];
									length <<=8;
									length |= p->buf[4];         // length	
								*/	
									length = p->buf[4];
									////////////////////// 1st version only read one packet
									if(length > (MAX_RPL_DATA_LENGTH-1))
										length = MAX_RPL_DATA_LENGTH-1;
									//////////////////////////////////////////////////////////
									//EsfRead(u8temp, adr, u8temp1, length, &Read_packet_Buf[0]);
									EsfRead(u8temp, adr, u8temp1, length, &buf[1]);
									Rp_fill_length=length;
									Read_packet_idx=0;
									reply_len = length +1;
									/*
									if(length>MAX_RPL_DATA_LENGTH)
									{
										
										do 
										{
											Push_Send_Report(reply, cmd, MAX_RPL_DATA_LENGTH, &Read_packet_Buf[Read_packet_idx]);
											udi_hid_generic_send_report_in((uint8_t *) &Report_send_struct.buf[0]);
											
											Read_packet_idx+=MAX_RPL_DATA_LENGTH;
											length -= MAX_RPL_DATA_LENGTH;

										} while (length>MAX_RPL_DATA_LENGTH);
									}
									
									if(length >0)
									{
										for(len_remain=0;len_remain<length;len_remain++)
											buf[len_remain] = Read_packet_Buf[Read_packet_idx+len_remain];
										reply_len = len_remain;
									}
									*/
									
								}
								else
									reply = OUT_RANGE;
						break;

						case TYP_VER_INFO:   // get F/W information
													 
									
									reply_len=7;
									buf[0]=TYP_VER_INFO;
									buf[1]=ESF_CONTRL;		
									buf[2]=VERSION_INFO_MSB;
									buf[3]=VERSION_INFO_LSB;
									buf[4]=YEAR_INFO;		//
									buf[5]=MONTH_INFO;		//
									buf[6]=DATE_INFO;		//
													 
						break;

						default:
								reply = ERR_TYP;
						break;
					}
			break;
				
			default:
					
					reply=ERR_CMD;
					
				break;
		}	
		
		//if(state!=false)
		{
			if(p->reply_status==PENDING)
			{
				p->reply_status = IDLE;
				Push_Send_Report(reply, cmd, reply_len, &buf[0]);
				udi_hid_generic_send_report_in((uint8_t *) &Report_send_struct.buf[0]);
				delay_ms(32);
				Report_send_struct.report_valid=IDLE;
			}
		}
		
		p->cmd_valid=IDLE;
	}
}

void ui_cmd_process(uint8_t *report)
{
	uint8_t reply, length,cmd, typ,reply_len;
	uint8_t buf[64];
	reply=ERR_NONE;
	length=reply_len=1;
	
	cmd =report[CMD_BYTE_NUM];
	length=report[LEN_BYTE_NUM];
	typ=report[TYP_BYTE_NUM];
	buf[0]=typ;
	
	#ifndef __IN_DEBUG
	
    if((length<MAX_LENGTH) && (length>=MIN_LENGTH))
	{	
		switch(cmd)
		{
			
			case CMD_GET:
			case CMD_READ:
			case CMD_PRG:
			case CMD_SET:
					if(Check_Op_status()==false)
					{
						reply=ERR_BUSY;
					}
					else					
						Push_cmd_Op(cmd,typ,length, &report[DATA_BYTE_NUM], &Cmd_received_struct);
					//Cmd_received_struct.reply_status = (state==1)? PENDING:IDLE;	
					Cmd_received_struct.reply_status = PENDING;	
				break;	
				
			default:
					reply=ERR_CMD;
				break;
		}
	}
	else
	{
		reply=ERR_LENGTH;
	}
	if(reply!= ERR_NONE)
	{
		Push_Send_Report(reply, cmd, reply_len, &buf[0]);
		udi_hid_generic_send_report_in((uint8_t *) &Report_send_struct.buf[0]);	
		Report_send_struct.report_valid=IDLE;
	}
		
	#else
		Push_Send_Report(reply, cmd, reply_len, &buf[0]);
		udi_hid_generic_send_report_in((uint8_t *) &Report_send_struct.buf[0]);	
		Report_send_struct.report_valid=IDLE;
	#endif
}
/**
 * \defgroup UI User Interface
 *
 * Human interface on SAM4E-EK:
 * - Led 0 (D2) is on when USB is wakeup
 * - Led 1 (D3) blinks when USB host has checked and enabled HID generic interface
 * - Led 2 (D4) is linked on HID events LED1
 * - Event buttons are linked to push button 1 (BP2) and push button 2 (BP3)
 *
 */

void MUX_Select(uint8_t sel, uint8_t state)
{
	if(sel)
		ioport_set_pin_level(MUX_CTRL_GPIO, MUX_MCU_LEVEL);
	else
		ioport_set_pin_level(MUX_CTRL_GPIO, MUX_FPGA_LEVEL);
	
	if(state)
		ioport_set_pin_level(MUX_EN_GPIO, MUX_EN_ACTIVE_LEVEL);
	else
		ioport_set_pin_level(MUX_EN_GPIO, MUX_EN_INACTIVE_LEVEL);
}