/**********************************************************************
	Copyright (C) 2018  MisfitTech LLC,  All rights reserved.

 	MisfitTech uses a dual license model that allows the software to be used under
	a standard GPL open source license, or a commercial license.  The standard GPL
	license  requires that all software statically linked with MisfitTec Code is
	also distributed under the same GPL V2 license terms.  Details of both license
	options follow:

	- Open source licensing -
	MisfitTech is a free download and may be used, modified, evaluated and
	distributed without charge provided the user adheres to version two of the GNU
	General Public License (GPL) and does not remove the copyright notice or this
	text.  The GPL V2 text is available on the gnu.org web site

	- Commercial licensing -
	Businesses and individuals that for commercial or other reasons cannot comply
	with the terms of the GPL V2 license must obtain a low cost commercial license
	before incorporating MisfitTech code into proprietary software for distribution in
	any form.  Commercial licenses can be purchased from www.misfittech.net
	and do not require any source files to be changed.


	This code is distributed in the hope that it will be useful.  You cannot
	use MisfitTech's code unless you agree that you use the software 'as is'.
	MisfitTech's code is provided WITHOUT ANY WARRANTY; without even the implied
	warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
	PURPOSE. MisfitTech LLC disclaims all conditions and terms, be they
	implied, expressed, or statutory.


    Written by Trampas Stern for MisfitTech.

    Misfit Tech invests time and resources providing this open source code,
    please support MisfitTech and open-source hardware by purchasing
	products from MisfitTech, www.misifittech.net!
 *********************************************************************/

#include "command.h"
#include <string.h>


#define ASCII_BACKSPACE 0x08
#define ASCII_ESC 0x1B
#define ASCII_UP_ARROW 0x9b
//const char CMD_ANSI_UP[]= {ASCII_ESC,'[','A',0};

int strcicmp(char const *a, char const *b)
{
    for (;; a++, b++) {
        int d = tolower(*a) - tolower(*b);
        if (d != 0 || !*a)
            return d;
    }
}

int CommandInit(sCmdUart *ptrUart, uint8_t (*kbhit)(void), uint8_t (*getch)(void),uint8_t (*putch)(char data),uint8_t (*puts)(uint8_t *buffer, uint8_t size) )
{
	ptrUart->kbhit=kbhit;
	ptrUart->getch=getch;
	ptrUart->putch=putch;
	ptrUart->puts=puts;
	ptrUart->histIndex=0;
	ptrUart->buffIndex=0;
	return 0;
}

#ifdef PGM_P //check and see if the PGM_P is defined for the AVR

int CommandPrintf(sCmdUart *ptrUart, const char *fmt, ...)
{
    int ret=0;
	char vastr[MAX_STRING]={0};
	//char str[MAX_STRING]={0};
	char *ptr;
    va_list ap;

    //LOG("Command printf");
    memset(vastr,0,MAX_STRING);
    va_start(ap,fmt);
    ret=vsprintf(vastr,(const char *)fmt,ap);
    //ret=sprintf(vastr,"%s\r\n",str);
    //LOG("%s",vastr);
    if (ptrUart->puts!=NULL)
    {
    	return ptrUart->puts((uint8_t *)vastr, (uint8_t)ret);
    }

    if (ptrUart->putch!=NULL)
    {
		ptr=vastr;
		while(*ptr)
		{
			ptrUart->putch(*ptr++);
		}

		return ret;
    }
    return 0;
}


#else
int CommandPrintf(sCmdUart *ptrUart, char *fmt, ...)
{
    int ret=0;
	char vastr[MAX_STRING]={0};
	char *ptr;
    va_list ap;


    memset(vastr,0,MAX_STRING);
    va_start(ap,fmt);
    ret=vsprintf(vastr,(char *)fmt,ap);
    if (ptrUart->puts!=NULL)
    {
    	return ptrUart->puts((uint8_t *)vastr, (uint8_t)ret);
    }

    if (ptrUart->putch!=NULL)
    {
		ptr=vastr;
		while(*ptr)
		{
			ptrUart->putch(*ptr++);
		}

		return ret;
    }
    return 0;
}
#endif


// the delimiter is command/parameter delimiter
// by default a ' '0x20 is used but for the TDR with GUI a ':' was preferred, not sure why
// set to ' '/0x20 if you want normal command parsing, like DOS
unsigned int CommandParse(sCmdUart *ptrUart,sCommand *ptrCmds, char *str, char delimitor )
{
	char *ptr;
	char *ptr2;
	unsigned int i;
	//char cmd[MAX_STRING];
	char buff[MAX_CMD_LENGTH];
	char argv[MAX_ARGS][MAX_ARG_LENGTH];
	char *ptrArgv[MAX_ARGS];
	unsigned int numArgs;
	int emptyArg=0;

	sCommand cmd_list;


	while (*str==0x20 || *str=='\n' || *str=='\r' || *str=='\t') str++;
	//first we need find command and arguments
	ptr=strchr(str,delimitor); //find first char

	//LOG("2parsing %s",str);


	if (ptr==0)
	{
		//we have two options, frist whole thing is command
		//second bad command
		if(strlen(str)>0)
			ptr=str+strlen(str);
		else
			return 0; //bad command
	}

	//copy string to command buffer.
	i=0;
	ptr2=str;
	while(ptr!=0 && ptr!=ptr2 && i<(MAX_CMD_LENGTH-1))
	{
		//if (*ptr2!='\n' && *ptr2!='\r') //do not include newlines
		{
			buff[i++]=*ptr2;
		}
		ptr2++;
	}
	buff[i]=0;

	//now buff contains the command let's get the args
	numArgs=0;
	while(*ptr!=0 && (*ptr==' ' || *ptr==delimitor))
		ptr++; //increment pointer past ' '
	if (*ptr!=0)
	{
		if (*ptr==34) // " char
		{
			ptr++;
			ptr2=strchr(ptr,34); //find match
		} else if (*ptr==39) // 'char
		{
			ptr++;
			ptr2=strchr(ptr,39); //find match
		} else
		{
			ptr2=strchr(ptr,delimitor);
		}
		if (ptr2==0)
		{
			//we have two options, frist whole thing is command
			//second bad command
			//LOG("strlen ptr is %d",strlen(ptr));
			if(strlen(ptr)>0)
				ptr2=ptr+strlen(ptr);
		}
		emptyArg=0;
		while((ptr2!=0 && numArgs<MAX_ARGS) || emptyArg==1)
		{
			int j;
			emptyArg=0;
			j=0;
			//LOG("arg %s",ptr);
			while (ptr2!=ptr && j<(MAX_ARG_LENGTH-1) && ptr2!=0)
			{
				argv[numArgs][j++]=*ptr++;
			}
			argv[numArgs][j++]=0;
			numArgs++;
			ptr2=0;
			if (*ptr!=0)
			{
				if (*ptr==34 || *ptr==39) ptr++;
				if (*ptr==delimitor && strlen(ptr)==1)
				{
					//LOG("Empty arg");
					emptyArg=1;
				}
				while(*ptr!=0 && (*ptr==' ' || *ptr==delimitor))//p || *ptr==34 || *ptr==39))
					ptr++; //increment pointer past ' '
				if (*ptr==34) // " char
				{
					ptr++;
					ptr2=strchr(ptr,34); //find match
				} else if (*ptr==39) // 'char
				{
					ptr++;
					ptr2=strchr(ptr,39); //find match
				} else
				{
					ptr2=strchr(ptr,delimitor);
				}
				if (ptr2==0)
				{
					//we have two options, frist whole thing is command
					//second bad command
					if(strlen(ptr)>0)
						ptr2=ptr+strlen(ptr);
				}
			}
		}
	}

	for(i=0; i<MAX_ARGS; i++)
	{
		ptrArgv[i]=argv[i];
	}

	//now let's parse the command
	i=0;
	memcpy(&cmd_list, &ptrCmds[i], sizeof(sCommand));


	//LOG("command is %s %d",buff,numArgs);

	while(cmd_list.function!=0)
	{
		/*char str[20];
		strcpy_P(str,cmd_list.name);
		LOG("checkign '%s' to '%s'",buff,str);
		LOG("comapre is %d",strcmp_P(buff,cmd_list.name));
*/

		//memcpy_P(&p, cmd_list.name, sizeof(PGM_P));
#ifdef PGM_P //check and see if the PGM_P is defined for the AVR
		if (strlen(buff)==strlen_P(cmd_list.name))
		{
			if (strcicmp(buff,cmd_list.name)==0) //ignore device ID
#else
		if (strlen(buff)==strlen(cmd_list.name))
		{
			if (strcicmp(buff,cmd_list.name)==0) //ignore device ID
#endif
			{
				//LOG("calling function");
				//return 1;
				return (*cmd_list.function)(ptrUart,numArgs,ptrArgv);
			}
		}
		i=i+1;
		memcpy(&cmd_list, &ptrCmds[i], sizeof(sCommand));
	}
	CommandPrintf(ptrUart,PSTR("Unknown command (try 'help')\n\r"));
	return -1;
}

//This function will process commands from the UART
int CommandProcess(sCmdUart *ptrUart,sCommand *ptrCmds, char delimitor, char *cmdPrompt)
{
	if(ptrUart->kbhit())
	{
		ptrUart->data=ptrUart->getch();

		//echo the data
		ptrUart->putch(ptrUart->data);

		//if the data is the CR we need to process buffer
		if (ptrUart->data==0x0D)
		{
			ptrUart->putch(0x0A);
			if (strlen(ptrUart->buffer)>0)
			{
				if (ptrUart->lastChar!=ASCII_UP_ARROW)
				{
					strcpy(ptrUart->bufferHist[ptrUart->histIndex],ptrUart->buffer);
					ptrUart->histIndex=(ptrUart->histIndex+1) % CMD_HISTORY;
				}
				CommandParse(ptrUart,ptrCmds,ptrUart->buffer,delimitor);
			}

			CommandPrintf(ptrUart,PSTR("\n\r%s"),cmdPrompt);
			ptrUart->buffIndex=0;
			ptrUart->buffer[ptrUart->buffIndex]=0;
		}

		if (ptrUart->data==ASCII_BACKSPACE) //backspace
		{
			if (ptrUart->buffIndex>0)
			{
				ptrUart->buffIndex--;
				ptrUart->buffer[ptrUart->buffIndex]='\0';
				//Echo the backspace
				ptrUart->putch(' ');
				ptrUart->putch(ASCII_BACKSPACE);
			}
		}else if (ptrUart->data != 0x0A && ptrUart->data !=0x0D && ptrUart->data<127)
		{
			ptrUart->buffer[ptrUart->buffIndex++]=ptrUart->data;
			ptrUart->buffer[ptrUart->buffIndex]=0;
		}
		if (ptrUart->buffIndex>=(MAX_CMD_LENGTH-1))
		{
			CommandPrintf(ptrUart,PSTR("\n\rERROR: Command buffer overflow\n\r"));\
			ERROR("Command buffer overflow");
			ptrUart->buffIndex=0;
			ptrUart->buffer[0]=0;
			CommandPrintf(ptrUart,PSTR("\n\r%s"),cmdPrompt);
		}
	}


	if (strstr(ptrUart->buffer,ANSI_UP)) //up arrow
	{
		uint8_t i;

		CommandPrintf(ptrUart,PSTR("\n\r%s"),cmdPrompt);
		i=CMD_HISTORY-1;
		if (ptrUart->histIndex>0)
		{
			i=ptrUart->histIndex-1;
		}
		if (strlen(ptrUart->bufferHist[i])>0)
		{
			strcpy(ptrUart->buffer,ptrUart->bufferHist[i]);
			ptrUart->buffIndex=strlen(ptrUart->buffer);
			CommandPrintf(ptrUart,PSTR("%s"),ptrUart->buffer);
		}else
		{
			ptrUart->buffIndex=0;
			ptrUart->buffer[0]=0;
		}
		ptrUart->data=ASCII_UP_ARROW;
	}


	ptrUart->lastChar=ptrUart->data;
	return 0;
}

