#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "ap_extern.h"
#include "drv_uart_def.h"
#include "al_bluetooth.h"
#include "al_bluetooth_ctrl.h"
#include "al_bluetooth_upgrader.h"
#include "al_basictypes.h"
#include "al_console.h"
#include <gsl/gl_mutex.h>
#include <gsl/gl_task.h>
#ifdef CONFIG_BT_5851S
#include <gsl/gl_timer.h>
#endif
#include "mid_partition_list.h"
#include "audio_ioctl.h"
#include "gl_sema.h"
#include "util_ioctl.h"
#include "drv_uartb.h"


extern CON_UART con_uart;
extern pthread_mutex_t txMutex;
extern int uartbfd;

rda58xx_status _status;	
rda58xx_buffer_status _rx_buffer_status;	 
al_uint32 _rx_buffer_size;	  
al_uint32 _rx_idx;	
al_uint8 *_rx_buffer;	 
static rda58xx_at_status _at_status = NACK;
static bool _updater_ready = FALSE;
al_uint32 _ats = 0;
al_uint32 _atr = 0;
static bool reSendFlag = FALSE;
al_int32 (*atHandler) (al_int32 status);
pthread_mutex_t	uartUpgrade_Lock =  PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  uartUpgrade_Strat = PTHREAD_COND_INITIALIZER;
static al_uint8 bUpgradeProcess = FALSE;
static GL_Semaphore_t pstSemaphore = al_null;
static GL_Task_t BT_UPGRADE_HANLDER = NULL;
static BT_Upgrade_pf g_pfBTUpgradeCallback = NULL;

const al_uint8 startUpdateCmd[] = {0x01, 0x01};
const al_uint8 skipUpdateCmd[] = {0x01, 0x02};
const al_uint8 updateFinishedCmd[] = {0x01, 0x04};
#define	UART_UPGRADE_BUF_SIZE		(1024*16+2)
al_uint8 upgradebuff[UART_UPGRADE_BUF_SIZE] = { 0 };
static char BTUpdateBinPath[100] = {0};
al_uint8 res_str[100] = {0};

#define WRITE_LE_32(ptr, x) (((ptr)[0] = (x)&0xff), ((ptr)[1] = ((x)>>8)&0xff), ((ptr)[2] = ((x)>>16)&0xff), ((ptr)[3] = ((x)>>24)&0xff))
#define WRITE_LE_16(ptr, x) (((ptr)[0] = (x)&0xff), ((ptr)[1] = ((x)>>8)&0xff))

static UINT16 upgrade_crc_table[16]=
{
	0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,
	0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF
};

#define BT_UPGRADE_SEMAPHORE_INIT	if (GL_SUCCESS != GL_SemaphoreCreate("BTUpgradeSemaphore", 1, 0, &pstSemaphore))\
									{\
										 printf("call GL_SemaphoreCreate fail\n");\
									}

#define BT_UPGRADE_SEMAPHORE_TAKE if(pstSemaphore)\
						         {\
									GL_SemaphoreTake(pstSemaphore,GL_INFINITE_WAIT);\
						         }

#define BT_UPGRADE_SEMAPHORE_TAKETIMEOUT(time) if(pstSemaphore)\
						         {\
									GL_SemaphoreTake(pstSemaphore,time);\
						         }

#define BT_UPGRADE_SEMAPHORE_GIVE  if(pstSemaphore)\
    							 {\
    							 	 GL_SemaphoreGive(pstSemaphore);\
							 	 }

#define BT_UPGRADE_SEMAPHORE_DESTROY if (pstSemaphore)\
    							 {\
    							 	GL_SemaphoreDelete(pstSemaphore);\
    							 	pstSemaphore = al_null;\
							 	 }

void bluetooth_upgrade_cmd_send(al_uint8 * str, al_uint size)
{
    int count = 0, outsize = 0;
    al_uint len = 0;
    al_uint off_irq_flag = 0;

	off_irq_flag = 1;
    outsize = size & 0x7fff;
	
	memset(upgradebuff,0x0,UART_UPGRADE_BUF_SIZE);

    while (1)
    {
        len = (outsize >= UART_UPGRADE_BUF_SIZE) ? UART_UPGRADE_BUF_SIZE : outsize;
        memcpy(upgradebuff, (str + count * UART_UPGRADE_BUF_SIZE), len);
		if(off_irq_flag)
        	write(uartbfd, upgradebuff, len|0x8000);
		else
        	write(uartbfd, upgradebuff, len);
			
        if (outsize > UART_UPGRADE_BUF_SIZE)
        {
            count++;
            outsize -= UART_UPGRADE_BUF_SIZE;
        }
        else
        {
            break;
        }
    }
}

static UINT16 codec_upgrader_crcCalculate(UINT8 *pBuffer, UINT16 ucLength)
{
	UINT16 uwCRC;
	UINT8 ucTemp;
	
	uwCRC=0xFFFF;
	while(ucLength--)
	{
		ucTemp=(UINT8)(uwCRC>>0x0C);
		uwCRC<<=4;
		uwCRC^=upgrade_crc_table[ucTemp^((*pBuffer)>>0x04)];
		ucTemp=(UINT8)(uwCRC>>0x0C);
		uwCRC<<=4;
		uwCRC^=upgrade_crc_table[ucTemp^((*pBuffer)&0x0f)];
		pBuffer++;
	}
	return uwCRC;
}

al_uint8 Al_Get_BTUpgradeFlag(void)
{
	return bUpgradeProcess;
}
	
void rda58xx_init(void)
{
    _status = UNREADY;
    _rx_buffer_status = EMPTY;
    _rx_buffer_size = 640;
    _rx_idx = 0;
    _rx_buffer = NULL;
    _at_status = NACK;
    _ats = 0;
    _atr = 0;
	_rx_buffer = (al_uint8 *) malloc(_rx_buffer_size * 2);
	_updater_ready = false;
	BT_UPGRADE_SEMAPHORE_INIT
}

void rda58xx_uinit()
{
    if (_rx_buffer != NULL)
    {
		free(_rx_buffer);
		_rx_buffer = NULL;
	}
	BT_UPGRADE_SEMAPHORE_DESTROY
}

void rda58xx_hardReset(void)
{
    _status = UNREADY;
    _rx_buffer_status = EMPTY;
    _rx_idx = 0;
    _at_status = NACK;
    _ats=0;
    _atr=0;
}

rda58xx_at_status rda58xx_setMode(rda58xx_mode mode)
{
    _at_status = NACK;
    _rx_idx = 0;
    if (UART_MODE== mode) {
        // Send AT CMD: UART_MODE
        CODEC_LOG(INFO, UART_MODE_CMD);
        _status = MODE_SWITCHING;
		pthread_mutex_lock(&txMutex);
		bluetooth_upgrade_cmd_send((al_uint8 *)UART_MODE_CMD,sizeof(UART_MODE_CMD));
        _ats++;
		pthread_mutex_unlock(&txMutex);
    } else if (BT_MODE== mode) {
        // Send AT CMD: BT_MODE
        CODEC_LOG(INFO, BT_MODE_CMD);
		pthread_mutex_lock(&txMutex);
		bluetooth_upgrade_cmd_send((al_uint8 *)BT_MODE_CMD,sizeof(BT_MODE_CMD));
        _ats++;
		pthread_mutex_unlock(&txMutex);
    }
    //BT_UPGRADE_SEMAPHORE_TAKE //need use timeout function
    _atr = _ats;

    return _at_status;
}

bool rda58xx_sendCmd(al_uint8 *cmd, al_uint8 cmdLen)
{
	// Raw Data bytes
    CODEC_LOG(INFO, "SendCmd\r\n");
    _at_status = NACK;
    _rx_idx = 0;
	pthread_mutex_lock(&txMutex);
	if((cmd != NULL) && (cmdLen != 0))
	{
		bluetooth_upgrade_cmd_send(cmd,cmdLen);
	}
	else
	{
		pthread_mutex_unlock(&txMutex);
		return _at_status;
	}
    _ats++;
	pthread_mutex_unlock(&txMutex);
	BT_UPGRADE_SEMAPHORE_TAKE
    _atr = _ats;

    return _at_status;	
}

UINT8 rda58xx_writeBlock(al_uint8* data, al_uint16 len)
{
    // Raw Data bytes
    CODEC_LOG(INFO, "SendData\r\n");
    _at_status = NACK;
    _rx_idx = 0;

	pthread_mutex_lock(&txMutex);
	if((data != NULL) && (len != 0))
	{
		bluetooth_upgrade_cmd_send(data,len);
	}
	else
	{
		pthread_mutex_unlock(&txMutex);
		return _at_status;
	}
    _ats++;
	pthread_mutex_unlock(&txMutex);
	BT_UPGRADE_SEMAPHORE_TAKE
	if(reSendFlag == TRUE)
	{
		reSendFlag = FALSE;
		_at_status = NEEDRESEND;
	}
    _atr = _ats;

    return _at_status;	
}

bool rda58xx_isUpdaterReady(void)
{
    return _updater_ready;
}

al_int32 atAckHandler(al_int32 status)
{
    if (VACK != (rda58xx_at_status) status) {
        switch ((rda58xx_at_status) status) {
        case NACK:
            CODEC_LOG(ERROR, "No ACK for AT command!\n");
            break;
        case IACK:
            CODEC_LOG(ERROR, "Invalid ACK for AT command!\n");
            break;
        default:
            CODEC_LOG(ERROR, "Unknown ACK for AT command!\n");
            break;
        }
    }

    return 0;
}

void rda58xx_rx_handler(al_int8 * buf,volatile unsigned int buflen)
{
	if(buf == NULL || buflen ==0)
		return;

    memcpy(&_rx_buffer[_rx_idx], buf, buflen);
    _rx_idx += buflen;
#ifdef RDA58XX_DEBUG
	printf("\r\nstatus:%d\n\r", _status);
#endif
    switch (_status) {
    case RECORDING:
        if (_rx_buffer_size == _rx_idx) {
            memcpy(&_rx_buffer[_rx_buffer_size], &_rx_buffer[0], _rx_buffer_size);
            _rx_buffer_status = FULL;
            _rx_idx = 0;
        }
        break;
        
    case STOP_RECORDING:
#ifdef RDA58XX_DEBUG
        printf("%d\n", _rx_idx);
#endif
        if ((_rx_idx >= 4) && (_rx_buffer[_rx_idx - 1] == '\n')) {
            memcpy(res_str, &_rx_buffer[_rx_idx - 4], 4);
            res_str[4] = '\0';
            if (strstr(RX_VALID_RSP2, (char *)res_str) != NULL) {
                _atr++;
                if (_atr == _ats) {
					BT_UPGRADE_SEMAPHORE_GIVE
                }
                _at_status = VACK;
                _rx_idx = 0;
                _status = PLAY;
#ifdef RDA58XX_DEBUG
                printf("%s\n", res_str);
#endif
            }
        }
        if (_rx_buffer_size == _rx_idx) {
            memcpy(&_rx_buffer[0], &_rx_buffer[_rx_buffer_size - 10], 10);
            _rx_idx = 10;
        }
        break;

    case UNREADY:
        if ((_rx_idx >= 6) && ('\n' == _rx_buffer[_rx_idx - 1])) {
            memcpy(res_str, &_rx_buffer[_rx_idx - 6], 6);
            res_str[6] = '\0';
            if (strstr(CODEC_READY_RSP, (char *)res_str) != NULL) {
                _rx_idx = 0;
                _status = UPGRADE_STOP;
#ifdef RDA58XX_DEBUG
                printf("\r\nCODEC_READY_RSP:%s\r\n", res_str);
#endif
			} else if (strstr(UPDATER_RUNNING, (char *)res_str) != NULL) {
				_updater_ready= true;
				_rx_idx = 0;
				pthread_cond_signal(&uartUpgrade_Strat);
#ifdef RDA58XX_DEBUG
				printf("\r\nUPDATER_RUNNING:%s\r\n", res_str);
#endif           
            } else if (strstr(AST_POWERON_RSP, (char *)res_str) != NULL) {
                _rx_idx = 0;
#ifdef RDA58XX_DEBUG
                printf("\r\nAST_POWERON_RSP:%s\r\n", res_str);
#endif                
            } else if (strstr(RX_VALID_RSP, (char *)res_str) != NULL) {
                _atr++;
                if (_atr == _ats) {
					BT_UPGRADE_SEMAPHORE_GIVE
                }
                _at_status = VACK;
#ifdef RDA58XX_DEBUG
                printf("\r\nRX_VALID_RSP:%s\r\n", res_str);
#endif
                _rx_idx = 0;
            }else if (strstr(RX_UNVALID_RSP, (char *)res_str) != NULL) {
                _atr++;
                if (_atr == _ats) {
					reSendFlag = TRUE;		
					printf("\r\nreSendFlag:%d\r\n", reSendFlag);
					BT_UPGRADE_SEMAPHORE_GIVE
                }
                _at_status = VACK;
#ifdef RDA58XX_DEBUG
                printf("\r\nRX_UNVALID_RSP:%s\r\n", res_str);
#endif
                _rx_idx = 0;
            }else {
#if 1
                _rx_buffer[_rx_idx] = '\0';
                printf("\r\n%s\r\n", _rx_buffer);
#else
                for (uint32_t i = 0; i < _rx_idx; i++) {
                    mbed_error_printf("%02X#", _rx_buffer[i]);
                }
#endif
            }
        }
        break;

    case MODE_SWITCHING:
        if ((_rx_idx >= 4) && ('\n' == _rx_buffer[_rx_idx - 1])) {
            memcpy(res_str, &_rx_buffer[_rx_idx - 4], 4);
            res_str[4] = '\0';
            if (strstr(RX_VALID_RSP2, (char *)res_str) != NULL) {
                _atr++;
                if (_atr == _ats) {
					BT_UPGRADE_SEMAPHORE_GIVE
                }
                _at_status = VACK;
                _status = UNREADY;
#ifdef RDA58XX_DEBUG
                printf("\nRX_VALID_RSP2:%s\n", res_str);
#endif
                _rx_idx = 0;
            } else {
#if 1
                _rx_buffer[_rx_idx] = '\0';
                printf("%s", _rx_buffer);
#else
                for (uint32_t i = 0; i < _rx_idx; i++) {
                    mbed_error_printf("%02X#", _rx_buffer[i]);
                }
#endif
                _at_status = IACK;
                _status = UPGRADE_STOP;
            }
        }
        break;
    
    default:
        if ((_rx_idx >= 4) && ('\n' == _rx_buffer[_rx_idx - 1])) {
            memcpy(res_str, &_rx_buffer[_rx_idx - 4], 4);
            res_str[4] = '\0';
            if (strstr(RX_VALID_RSP2, (char *)res_str) != NULL) {
                _atr++;
                if (_atr == _ats) {
					BT_UPGRADE_SEMAPHORE_GIVE
                }
                _at_status = VACK;
#ifdef RDA58XX_DEBUG
                printf("%s\n", res_str);
#endif
                _rx_idx = 0;
            } else {
#if 1
                _rx_buffer[_rx_idx] = '\0';
                printf("%s", _rx_buffer);
#else
                for (uint32_t i = 0; i < _rx_idx; i++) {
                    mbed_error_printf("%02X#", _rx_buffer[i]);
                }
#endif
                _at_status = IACK;
            }
        }
        break;
    }

    buflen = 0;

    if (_rx_idx >= _rx_buffer_size)
        _rx_idx = 0;
}

bool Upgrader_isUpdaterReady(void)
{
	return rda58xx_isUpdaterReady();
}

void Upgrader_setMode(rda58xx_mode mode)
{
	rda58xx_setMode(mode);
	//atAckHandler(ret);
}

bool Upgrader_startUpdate(void)
{
	rda58xx_sendCmd((al_uint8 *)startUpdateCmd, sizeof(startUpdateCmd));
	return true;
}

bool Upgrader_skipUpdate(void)
{
	rda58xx_sendCmd((al_uint8 *)skipUpdateCmd, sizeof(skipUpdateCmd));
	return true;
}

bool Upgrader_finishUpdate(void)
{
	rda58xx_sendCmd((al_uint8 *)updateFinishedCmd, sizeof(updateFinishedCmd));
	return true;
}

bool Upgrader_writeBlock(al_uint32 address, al_uint8* data, al_uint16 len)
{
	al_uint8 cmd[8] = {0x01, 0x03};
	al_uint8 crctable[2] = {0};
	al_uint16 crc = 0;
ReSend:		
	WRITE_LE_16(cmd+2, (len+sizeof(crctable))); //plus 
	WRITE_LE_32(cmd+4, address);
	rda58xx_sendCmd(cmd, sizeof(cmd));
	crc = codec_upgrader_crcCalculate(data, len);
	crctable[0] = (crc>>8)&0xff;
	crctable[1] = crc&0xff;
	memcpy(data+len,crctable,sizeof(crctable));
	if(NEEDRESEND == rda58xx_writeBlock(data, (len+sizeof(crctable))))
	{
		printf("\nNeedResend Block!\n");
		goto ReSend;
	}
	return true;
}

al_uint8 upate_going(void)
{
	FILE * fp = NULL;
	int i = 0;
	al_uint8 *src = NULL;
	int count = 0;
	al_uint8 ret = TRUE;
	al_uint8 *dst = NULL;
	al_uint8 *readbuf = NULL;

	char lineChars[100] = {0};
	al_uint8 templine[11] = {0};
	al_uint32 address = 0;
	al_uint32 fileSize = 0;
	al_uint32 haveUpgradeSize = 0;
	al_uint8 upgradeProgress = 0;
	int j = 0;
	int	k = 0;

	fp = fopen(BTUpdateBinPath, "r");
	if(fp == NULL)
	{
		return FALSE;
	}
	dst = (al_uint8 *)malloc(16384+2);
	if(dst == NULL)
	{
		fclose(fp);
		return FALSE;
	}
	readbuf = (al_uint8 *)malloc(4096*9);
	if(readbuf == NULL)
	{
		fclose(fp);
		free(dst);
		return FALSE;
	}
	
	//get filesize
	fseek(fp,0,SEEK_END);
	fileSize = ftell(fp);
	fseek(fp,0,SEEK_SET);
	
	BTUpgradeSendMessageToApp(BT_UPGRADE_START,0);
	BTUpgradeSendMessageToApp(BT_SYSTEM_AUDIO_CONTROL,DISABLE_AUDIO);
	Upgrader_startUpdate();
	do
	{
		memset(lineChars, 0, sizeof(lineChars));
		fscanf(fp, "%s", lineChars);
		printf("%s\n\r", lineChars);
	}while(lineChars[0] != '@');

	address = strtoul((const char*)&lineChars[1], NULL, 16);
	printf("%08x\n\r", address);
	memset(readbuf,0x0,4096*9);
	fread(readbuf, 1, 1, fp);

	while(!feof(fp))
	{
		src = readbuf;
		j = 0;
		k = 0;
		
		fread(src, 1024*9, 1, fp);

		fread(templine, 9, 1, fp);

		if(templine[0] == '@' || templine[0] == '#')
		{
			printf("4K bytes\n\r");
			fread(lineChars, 1, 1, fp);
			count = 1024;
		}
		else
		{
			memcpy(src+1024*9, templine, 9);
			fread(src+1025*9, 3071*9, 1, fp); 
			count = 4096;
		}
		for(i = 0; i<count; i++)
		{
			//*dstInt = strtoul((const char*)src, NULL, 16);
			for(j = 7; j>0; j-=2)
			{
				if(src[j] <= '9')
					dst[k] = src[j]-0x30;
				else if(src[j] <= 'f')
					dst[k] = src[j]-0x57;

				if(src[j-1] <= '9')
					dst[k] |= ((src[j-1]-0x30)<<4);
				else if(src[j-1] <= 'f')
					dst[k] |= ((src[j-1]-0x57)<<4);
				k++;
			}
			src+=9;
		}
		//printf("address: %08x ; count:0x%x\n ", address,k);
		Upgrader_writeBlock(address, dst, k);
		haveUpgradeSize = ftell(fp);
		upgradeProgress = haveUpgradeSize*100/fileSize;
		if(upgradeProgress >= 100)
		{
			upgradeProgress = 100;
		}
		BTUpgradeSendMessageToApp(BT_UPDATE_PROGRESS,upgradeProgress);

		if(templine[0] == '#')
		{
			break;
		}
		
		if(templine[0] != '@')
		{
			fread(templine, 10, 1, fp);
			if(templine[0] == '#')
			{		
				break;
			}	
		}
		address = strtoul((const char*)&templine[1], NULL, 16);
	}
	
	if(dst)
	{
		free(dst);
		dst = NULL;
	}
	if(readbuf)
	{
		free(readbuf);
		readbuf = NULL;
	}
	fclose(fp);

	printf("read file complete\n\r");
	Upgrader_finishUpdate();
	//open audio isr
	BTUpgradeSendMessageToApp(BT_UPDATE_PROGRESS,100);
	BTUpgradeSendMessageToApp(BT_SYSTEM_AUDIO_CONTROL,ENABLE_AUDIO);
	
	return ret;
}

static void blueToothUpgrade_hanlder(void *arg)
{
	al_uint8 ret = FALSE;
	int checkcount = 0;

	//init
	rda58xx_init();
	bUpgradeProcess = TRUE;
	//bt reset bt
	BTUpgradeSendMessageToApp(BT_MODULE_RESET,1);
	
	pthread_mutex_lock(&uartUpgrade_Lock);

	//set baudrate 921600
	GL_TaskSleep(100);//watit cmd send to bt    
  	ioctl(uartbfd, UARTB_SET_BAUDRATE, 921600);

	//pthread_cond_wait(&uartUpgrade_Strat, &uartUpgrade_Lock);
	do
	{
		printf("Upgrader_isUpdaterReady:%x\n", Upgrader_isUpdaterReady());
		if(Upgrader_isUpdaterReady())
		{
			ret = upate_going();
			if(ret == TRUE)
			{
				printf("\nBT Upgrade sucessful!!!\n");				
				BTUpgradeSendMessageToApp(BT_UPGRADE_DONE,0);
				Upgrader_setMode(BT_MODE);
			}
			else
			{
				BTUpgradeSendMessageToApp(BT_UPGRADE_FAIL,0);
			}
			break;
		}
		GL_TaskSleep(500);
	}while(++checkcount < 10);
	
	//exit baudrate 921600,set baudrate 115200
	ioctl(uartbfd, UARTB_SET_BAUDRATE, 115200);

	pthread_mutex_unlock(&uartUpgrade_Lock);
	bUpgradeProcess = FALSE;	
    rda58xx_uinit();
	GL_TaskSelfDelete();
}

void BTUpgradeSendMessageToApp(al_uint32 event,al_uint32 parm)
{
	BT_UpgradeMsg_t gUpgrademessage = {0};

	if(g_pfBTUpgradeCallback == NULL)
		return;
	
	gUpgrademessage.dMessage = event;
	gUpgrademessage.dParam = parm;
	g_pfBTUpgradeCallback(gUpgrademessage);
}


void RegisterBTUpgradeCallback(BT_UpgradeCfg_t * callback)
{
    if ((callback != NULL) && (callback->pfCallBack != NULL))
    {
        g_pfBTUpgradeCallback = callback->pfCallBack;
    }
}

int al_bluetooth_upgrade_start(char *pPath)
{
	if(pPath != NULL)
	{
		memset(BTUpdateBinPath, 0x0, 100);
		snprintf(BTUpdateBinPath,100,"%s",pPath);
	}
	
	if(bUpgradeProcess == FALSE)
	{
		printf("Bt upgrade process......bin:%s\n",BTUpdateBinPath);
		if (GL_TaskCreate("Bluetooth Upgrade hanlder", (void *) blueToothUpgrade_hanlder, NULL, 2, 4096, TRUE, &(BT_UPGRADE_HANLDER)) !=
			GL_SUCCESS)
			printf("pthread_create fail: blueToothUpgrade_hanlder thread\n");
	}
	
	return 0;
}


