/*
 * USB_API_layer.c
 *
 * Created: 6.3.2016 13:05:18
 *  Author: Chenda
 *
 *	API structure elements used by this API
 *	_______________________________________
 *	RX:
 *		Event
 *		SourceAdd
 *		TargetAdd
 *		Command
 *		-- RAW data
 *		DataLength
 *		*Data			READ ONLY!!! - points to source buffer start read positionVOLY?
 *		-- Error data
 *		ErrorNumber
 *	TX:		
 *		Event
 *		Command
 *		DataLength
 *		*Data			READ ONLY!!! - points to target buffer start write position
 */

#include "config.h"
#include "conf_usb.h"
#include "../../lib_mcu/usb/usb_drv.h"
#include "usb_task.h"
#include "device_chap9/usb_standard_request.h"
#include "EP_handling/EP_tasks.h"
#include "SCPI/SCPI_defs.h"
#include "SCPI/SCPI_parser.h"

#include "../DeviceAddresses.h"
#include "../API_common_defs.h"
#include "../../Command_defs.h"
#include "../../Common/DataOperations.h"
#include "USB_API_layer.h"

#include <avr/io.h>
#include <stdio.h>
#include <stdint.h>
#include "../lcd/lcd_h.h"

/************************************************************************/
/* Global variables                                                     */
/************************************************************************/
static volatile U8 cpt_sof;
static volatile bool Next100ms;
static bool TimerEnabled = false;
static uint8_t TickCounter_100MS = 0;

static struct stAPI_layer stAPIL_USB;
static struct stAPI_layer *spAPI = &stAPIL_USB;

static struct st_Usb_EP_Interface *spUsbEpInt;
static struct stScpi_ParsingStatus *spParStat;

/************************************************************************/
/* Private functions prototypes                                         */
/************************************************************************/
void usb_ep_tasks(void);
void usb_process_mess(void);
void usb_proglang_to_ascii(void);
void usb_put_number_into_data(void);

void SOF_tick(void);

/************************************************************************/
/* Public functions                                                     */
/************************************************************************/
struct stAPI_layer* USB_API_INIT(void)
{
	usb_task_init();
	usb_task();
	cpt_sof=0;
	Usb_enable_sof_interrupt();
	spUsbEpInt = USB_EP_GET_INT();
	spParStat = SCPI_PARSER_INIT();
	spAPI->TX.Data = spUsbEpInt->BulkIn.msgBuff;
	return spAPI;
}

void USB_API(void)
{
	usb_task();
	usb_ep_tasks();
}

/************************************************************************/
/* Private functions                                                    */
/************************************************************************/
void usb_ep_tasks(void)
{
	// otestovat:
	/*
	Is_usb_detached()
	Is_usb_pending_remote_wake_up()
	Is_usb_resume()
	Is_usb_suspend()
	*/
	
	if ( Is_device_enumerated() )
	{
		if (spAPI->RX.Event == API_PERIPH_NOT_AVAILBLE) {
			spAPI->RX.Event = API_NO_ACTION;
			spAPI->TX.Event = API_NO_ACTION;
		}
		USB_EP_BULK_OUT_TASK();
		USB_EP_BULK_IN_TASK();
		SOF_tick();
		
		if (spUsbEpInt->BulkOut.Event == BULK_INT_EVENT_DEV_MSG_OUT) {
			spUsbEpInt->BulkOut.Event = BULK_INT_EVENT_NO_EVENT;
			usb_process_mess();
			usb_put_number_into_data();
			spAPI->TX.Event = API_NO_ACTION;			// protect before stacking if USB host doesn't read data on query command
			if (spAPI->RX.Event == API_ERROR) {
				sprintf((char*)spUsbEpInt->BulkIn.msgBuff,"E%d",spAPI->RX.ErrorNumber);
				spAPI->TX.DataLength = 0;
				while(spUsbEpInt->BulkIn.msgBuff[spAPI->TX.DataLength] != 0) spAPI->TX.DataLength++;
				spUsbEpInt->BulkIn.msgLength = spAPI->TX.DataLength;
				spAPI->TX.Event = API_SENDING;		// don't use when error handling will be finished
			}
			else {
				spUsbEpInt->BulkIn.msgLength = 0;
			}
		}
		
		if (spUsbEpInt->BulkOut.Event == BULK_INT_EVENT_VENDOR_OUT) {
			spUsbEpInt->BulkOut.Event = BULK_INT_EVENT_NO_EVENT;
			lcd_clrscr();
			lcd_puts("vendor out");
		}
		
		if (spParStat->Query) {
			if (spAPI->TX.Event == API_SEND) {
				// data prepared for sending - data have to be converted to ASCII chars
				spAPI->TX.Event = API_SENDING;
				usb_proglang_to_ascii();
			}
			else if (spAPI->TX.Event == API_SEND_ACK_NACK) {
				// no respond for USB API
				spAPI->TX.Event = API_SENDING;
				spUsbEpInt->BulkIn.msgLength = 0;
				spUsbEpInt->BulkIn.msgBuff[0] = 0;
			}
		}
		else {
			// if no query, no respond expected
			spAPI->TX.Event = API_NO_ACTION;
			spUsbEpInt->BulkIn.msgLength = 0;
			spUsbEpInt->BulkIn.msgBuff[0] = 0;
		}
		
		if (spUsbEpInt->BulkOut.Event == BULK_INT_EVENT_REQ_DEV_MSG_IN && !TimerEnabled) {
			cpt_sof = 0;
			Next100ms = false;
			TickCounter_100MS = 0;
			TimerEnabled = true;
		}
		if (spUsbEpInt->BulkOut.Event == BULK_INT_EVENT_REQ_DEV_MSG_IN && (spAPI->TX.Event == API_SENDING || TickCounter_100MS >= SOF_TICK_TO_SEND_ERROR)) {
			// Sending data
			TimerEnabled = false;
			spAPI->TX.Event = API_NO_ACTION;
			spUsbEpInt->BulkOut.Event = BULK_INT_EVENT_NO_EVENT;
			spUsbEpInt->BulkIn.Event = BULK_INT_EVENT_DEV_MSG_IN;
			spUsbEpInt->BulkIn.bTag = spUsbEpInt->BulkOut.bTag;
			spUsbEpInt->BulkIn.bTagInvers = spUsbEpInt->BulkOut.bTagInvers;
			spUsbEpInt->BulkIn.TransferAttributes = EOM;
		}
		if (spUsbEpInt->BulkOut.Event == BULK_INT_EVENT_REQ_VENDOR_IN) {
			if (spAPI->TX.Event == API_NO_ACTION) {
				spUsbEpInt->BulkIn.msgLength = 0;
			}
			spUsbEpInt->BulkOut.Event = BULK_INT_EVENT_NO_EVENT;
			spAPI->TX.Event = API_NO_ACTION;
			spUsbEpInt->BulkIn.Event = BULK_INT_EVENT_VENDOR_IN;
			spUsbEpInt->BulkIn.bTag = spUsbEpInt->BulkOut.bTag;
			spUsbEpInt->BulkIn.bTagInvers = spUsbEpInt->BulkOut.bTagInvers;
			spUsbEpInt->BulkIn.TransferAttributes = EOM;
			lcd_puts("vendor in req");
		}
	}
	else {
		// USB isn't enumerated - periphery isn't available
		spAPI->RX.Event = API_PERIPH_NOT_AVAILBLE;
		spAPI->TX.Event = API_PERIPH_NOT_AVAILBLE;
	}
}

void usb_process_mess(void)
{
	// process incoming SCPI messages - translate into program language
	if (SCPI_PARSER_TASK((char*)spUsbEpInt->BulkOut.msgBuff,spUsbEpInt->BulkOut.msgLength)) {
		// update API - data are handled in usb_put_number_into_data()
		spAPI->RX.Event = API_PRIVATE_MESS;
		spAPI->RX.ErrorNumber = 0;		
		spAPI->RX.CmdAckNack_type = API_CMD_MESS;
		spAPI->RX.SourceAdd = DEV_ADD_USB_REM_CTRL;
		spAPI->RX.TargetAdd = DEV_ADD_USBTMC_INT;
		spAPI->RX.Command = spParStat->ParsedCmd;
		spAPI->RX.DataLength = spParStat->UnparsedParLen;
		spAPI->RX.Data = (uint8_t *)spParStat->UnparsedParam;
		spAPI->TX.Command = spAPI->RX.Command;
	}
	else {
		// update API 
		spAPI->RX.Event = API_ERROR;
		spAPI->RX.ErrorNumber = API_E_UNKNOWN_CMD;
	}	
}

void usb_proglang_to_ascii(void)
{
	bool text = false;
	uint8_t Cmd = spAPI->TX.Command;
	int16_t IntNo;
	int16_t TenthNo;

	if (Cmd == DISP_TEXT_QRY) text = true;
	if (Cmd == INST_SEL_PAR_QRY) text = true;
	if (Cmd == INST_CONN_QRY) text = true;
	if (Cmd == CORE_IDN_QRY) text = true;
	if (Cmd == VER_QRY) text = true;
	//if (Cmd == ERROR_QRY) text = true;
	
	if (!text && spAPI->TX.DataLength > 0) {
		DAT_OPS_GetNumber(spAPI->TX.Data,&IntNo,&TenthNo);
		spUsbEpInt->BulkIn.msgLength = SCPI_NUM_TO_ASCII(IntNo,TenthNo,(char*)spUsbEpInt->BulkIn.msgBuff);
	}
	else {
		spUsbEpInt->BulkIn.msgLength = spAPI->TX.DataLength;
	}
}

void usb_put_number_into_data(void)
{
	// check parameter type and presence
	uint8_t PeramType = NO_PAR;	// default value no param expected
	
	// setting right parameter type according to SCPI commands
	if (spAPI->RX.Command == DISP_STAT) PeramType = NUMBER_PAR;
	if (spAPI->RX.Command == DISP_TEXT) PeramType = TEXT_PAR;
	if (spAPI->RX.Command == INST_SEL) PeramType = NUMBER_PAR;
	if (spAPI->RX.Command == SRC_IMM_CURR) PeramType = NUMBER_PAR;
	if (spAPI->RX.Command == SRC_IMM_VOLT) PeramType = NUMBER_PAR;
	if (spAPI->RX.Command == OUT_STAT) PeramType = NUMBER_PAR;
	if (spAPI->RX.Command == OUT_STAT_ALL) PeramType = NUMBER_PAR;
	
	
	if (PeramType == NO_PAR && spParStat->UnparsedParLen == 0) {
	}
	else if (PeramType == TEXT_PAR ) {
	}
	else if (PeramType == NUMBER_PAR && spParStat->ParsingResult == SCPI_PARSING_PASS && spParStat->UnparsedParLen > 0) {
		DAT_OPS_PutNumber(spAPI->RX.Data,&spParStat->IntPartOfNumber,&spParStat->TenthPartOfNumber);
		spAPI->RX.DataLength = 4;
		spAPI->RX.Data[4] = spParStat->ValueOfNumber;
		spAPI->RX.DataLength = 5;
	}
	else {
		spAPI->RX.Event = API_ERROR;
		spAPI->RX.ErrorNumber = API_E_CMD_PARAM;
	}
}

/************************************************************************/
/* millisecond counter from USB interrupt (interrupt each 1ms)          */
/************************************************************************/
void sof_action()
{
	cpt_sof++;
	if (cpt_sof > 100) {
		Next100ms = true;
		cpt_sof = 0;
	}
}

void SOF_tick(void)
{
	if (Next100ms) {
		// next 100ms
		Next100ms = false;
		if (TimerEnabled) {
			TickCounter_100MS++;
		}
	}
}
