/**************************************************************************//**
* @file
* @brief         Contains the parser, that parses the users console inputs
*                to the dedicated commands.
* @headerfile    CommandParser.h
* @details       
******************************************************************************
* @author        M. Eng. CCR (Technikreferent des Durstigen Donnerstags)
* @version       1.0
* @date          Q1 2016
******************************************************************************
* @bug
* @warning       Because of the lazyness of the author there are a lot of magic numbers
******************************************************************************
* @copyright     <b>CC-Software</b> - http://www.cc-software.de
*                Open Source Software for http://www.durstiger-donnerstag.de
* @section       License
*                This code is open source. Feel free to use it for non-commercial purposes.
*                However consider:
*                - always mention the origin (CC-Software and Durstiger Donnerstag)
*                - if you altered code, additionally mark it as altered by you
*                - the author doesn't guarantee functionality and is not liable for
*                  any accidents or other occurrences due to the usage of this code
*******************************************************************************/

#include "stdafx.h" 

#include "DDProtocol.h"
#include "CommandParser.h"
#include "Command.h"

/**************************************************************************//**
* @addtogroup AlcoLyzer Console
*****************************************************************************/

#define MAX_NAME_LENGTH       10
#define MAX_EVENT_DESC_LENGTH 32

#define MAX_LENGTH_STRING MAX_EVENT_DESC_LENGTH

/**************************************************************************//**
* @brief
*    Parses the input characters, typed in by the user and calls the desired
*    functions of the command module.
*
* @param user_input
*    The input string, zero terminated.
*****************************************************************************/
void CommandParse(char *user_input)
{
    char cmd, content, data_buffer[6], char_buffer[MAX_LENGTH_STRING + 2], temp;
    //decode command
    if (cmd = CommandParseCommand(user_input))
    {
        if (cmd == COMMAND_PARSER_CMD_PING){
            requestPing();
        }
        else if (cmd == COMMAND_PARSER_CMD_HELP){
            CommandParserPrintHelp();
        }
        else if (cmd == COMMAND_PARSER_CMD_QUIT){
            printf("<> Beende AlcoLyzer Console, BierAlyzer ausstecken und neu starten! ...\n");
        }
        //decode type
        else if (content = CommandParseContent(user_input + 4)){
            //decode parameter(s)
            switch (cmd){

            case COMMAND_PARSER_CMD_GET:
                switch (content){
                case DDP_PACKET_CONTENT_USER:
                    //get all users
                    if (user_input[9] == '*' && user_input[10] == '\n'){
                        requestAllUsers();
                    }
                    //get single user: extract entered hex id
                    else if (CommandParseDecodeNumber(user_input + 9, 16, data_buffer, '\n') == 1){
                        requestSingleUser(data_buffer[0]);
                    }
                    else{
                        printf("<> ERROR: Keine korrekte Eingabe\n");
                    }
                    break;
                case DDP_PACKET_CONTENT_EVENT:
                    //get all events
                    if (user_input[9] == '*' && user_input[10] == '\n'){
                        requestAllEvents();
                    }
                    //get single event: extract entered event number
                    else if (CommandParseDecodeNumber(user_input + 9, 10, data_buffer, '\n')){
                        requestSingleEvent(data_buffer[0]);
                    }
                    else{
                        printf("<> ERROR: Keine korrekte Eingabe\n");
                    }
                    break;
                case DDP_PACKET_CONTENT_SETTINGS:
                    //get all settings
                    if (user_input[9] == '*' && user_input[10] == '\n'){
                        requestSettings();
                    }
					else{
						printf("<> ERROR: Keine korrekte Eingabe\n");
					}
                    break;
                default:
                    printf("ERROR: input could not be parsed correctly\n");
                }
                break;

            case COMMAND_PARSER_CMD_DEL:
                switch (content){
                case DDP_PACKET_CONTENT_USER:
                    //delete all users
                    if (user_input[9] == '*' && user_input[10] == ' '){
                        //deleteAllUsers();
                        printf("<> ERROR: - delete all users not implemented -\n");
                    }
                    //delete single user: extract entered hex id
                    else if (CommandParseDecodeNumber(user_input + 9, 16, data_buffer, '\n') == 1){
                        deleteSingleUser(data_buffer[0]);
                    }
                    else{
                        printf("<> ERROR: Keine korrekte Eingabe\n");
                    }
                    break;
                case DDP_PACKET_CONTENT_EVENT:
                    //delete all events
                    if (user_input[9] == '*' && user_input[10] == '\n'){
                        //deleteAllEvents();
                        printf("<> ERROR: - delete all events not implemented -\n");
                    }
                    //delete single event: extract entered event number
                    else if (CommandParseDecodeNumber(user_input + 9, 10, data_buffer, '\n')){
                        deleteSingleEvent(data_buffer[0]);
                    }
                    else{
                        printf("<> ERROR: Keine korrekte Eingabe\n");
                    }
                    break;
                default:
                    printf("<> ERROR: input could not be parsed correctly\n");
                }
                break;

            case COMMAND_PARSER_CMD_SET:
                switch (content){
                case DDP_PACKET_CONTENT_USER:
                    //register new user: extract entered hex id
                    if (CommandParseDecodeNumber(user_input + 9, 16, data_buffer, ' ') == 1){
                        //extract entered name and check for max length
                        temp = CommandParseDecodeString(user_input + 12, char_buffer, ' ', 0);
                        if (!temp){
                            printf("<> ERROR: Keine korrekte Eingabe\n");
                        }
                        else if (temp <= MAX_NAME_LENGTH){
                            //check if * is entered, meaning to use current RFID card on BierAlyzer
                            if (user_input[12 + temp + 1] == '*' && user_input[12 + temp + 2] == '\n'){

                                registerUser(data_buffer[0], char_buffer, 0);
                            }
                            //extract entered RFID ID
                            else if (CommandParseDecodeNumber(user_input + 12 + temp + 1, 16, data_buffer + 1, '\n') == 5){
                                registerUser(data_buffer[0], char_buffer, data_buffer + 1);
                            }
                            else{
                                printf("<> ERROR: Keine korrekte Eingabe\n");
                            }
                        }
                    }
                    break;
                case DDP_PACKET_CONTENT_EVENT:
                    //register new event: extract entered event number
                    if (CommandParseDecodeNumber(user_input + 9, 10, data_buffer, ' ') == 1){
                        //extract entered event description and check for max length
                        temp = CommandParseDecodeString(user_input + 21, char_buffer, '\n', 0);
                        if (temp <= MAX_EVENT_DESC_LENGTH){
                            //if date is entered correctly is tested inside the function below
                            registerEvent(data_buffer[0], char_buffer, user_input + 12);
                        }
                        else{
                            printf("<> ERROR: Keine korrekte Eingabe\n");
                        }
                    }
                    break;
                case DDP_PACKET_CONTENT_SETTINGS:
                    //configure current event id, check if  eventid=XX was entered
                    if (user_input[9] == 'e' && user_input[10] == 'v' && user_input[11] == 'e' && user_input[12] == 'n' && \
                        user_input[13] == 't' && user_input[14] == 'i' && user_input[15] == 'd' && user_input[16] == '='){
                        //extract entered event number
                        if (CommandParseDecodeNumber(user_input + 17, 10, data_buffer, '\n') == 1){
                            setEventID(data_buffer[0]);
                        }
                        else{
                            printf("<> ERROR: Keine korrekte Eingabe\n");
                        }
                    }
                    //sync clock
                    else if (user_input[9] == 'c' && user_input[10] == 'l' && user_input[11] == 'o' && user_input[12] == 'c' && \
                            user_input[13] == 'k' && user_input[14] == 's' && user_input[15] == 'y' && user_input[16] == 'n' && user_input[17] == 'c' && user_input[18] == '\n'){
                        clockSync();
                    }
                    else{
                        printf("<> ERROR: Keine korrekte Eingabe\n");
                    }
                    break;
                default:
                    printf("<> ERROR: input could not be parsed correctly\n");
                }
                break;
            }
        }
    }
}

/**************************************************************************//**
* @brief
*    Parses the command part of the input 
*    First input argument (get, set, del, ...).
*
* @param input
*    The input characters, that contain the command.
*
* @return
*    The parsed command code, zero if error.
*****************************************************************************/
command_parser_cmd_t CommandParseCommand(char *input)
{
    if (input[0] == 'g' && input[1] == 'e' && input[2] == 't' && input[3] == ' '){
        return COMMAND_PARSER_CMD_GET;
    }
    if (input[0] == 's' && input[1] == 'e' && input[2] == 't' && input[3] == ' '){
        return COMMAND_PARSER_CMD_SET;
    }
    if (input[0] == 'd' && input[1] == 'e' && input[2] == 'l' && input[3] == ' '){
        return COMMAND_PARSER_CMD_DEL;
    }
    if (input[0] == 'p' && input[1] == 'i' && input[2] == 'n' && input[3] == 'g' && input[4] == '\n'){ //there are no following parameters
        return COMMAND_PARSER_CMD_PING;
    }
    if (input[0] == 'h' && input[1] == 'e' && input[2] == 'l' && input[3] == 'p' && input[4] == '\n'){ //there are no following parameters
        return COMMAND_PARSER_CMD_HELP;
    }
    if (input[0] == 'q' && input[1] == 'u' && input[2] == 'i' && input[3] == 't' && input[4] == '\n'){ //there are no following parameters
        return COMMAND_PARSER_CMD_QUIT;
    }
    else{
        //!!ZERO TERMINATION NOT THERE
        printf("<> ERROR: Unknown command %s\n", input);
        return (command_parser_cmd_t) 0;
    }
}

/**************************************************************************//**
* @brief
*    Parses the content part of the input
*    Used for the second input argument (user, evnt, stgs).
*
* @param input
*    The input characters, that contain the content.
*
* @return
*    The parsed content code.
*****************************************************************************/
ddp_packet_content_t CommandParseContent(char *input)
{
    if (input[0] == 'u' && input[1] == 's' && input[2] == 'e' && input[3] == 'r' && input[4] == ' '){
        return DDP_PACKET_CONTENT_USER;
    }
    if (input[0] == 'e' && input[1] == 'v' && input[2] == 'n' && input[3] == 't' && input[4] == ' '){
        return DDP_PACKET_CONTENT_EVENT;
    }
    if (input[0] == 's' && input[1] == 't' && input[2] == 'g' && input[3] == 's' && input[4] == ' '){
        return DDP_PACKET_CONTENT_SETTINGS;
    }
    else{
        //!!ZERO TERMINATION NOT THERE
        printf("<> ERROR: Unknown type %s\n", input);
        return (ddp_packet_content_t)0;
    }
}

/**************************************************************************//**
* @brief
*    Parses a various string part of the input, that is terminated with a 
*    termination character to be specified.
*    Used for third input argument (name of the user or event, ...)
*
* @param ascii_input
*    String that was typed in.
*
* @param zero_terminated_output
*    Output pointer of the zero terminated string.
*
* @param termination
*    The character, that indicates the termination of the input.
*
* @param fill_with_space_until
*    The number of characters, the output is supposed to contain. If larger 
*    than the input string length, output is filled with spaces.
*    Set to zero if option is not to be used.
*
* @return
*    The length of the parsed string, zero if error.
*****************************************************************************/
char CommandParseDecodeString(char *ascii_input, char *zero_terminated_output, char termination, int fill_with_space_until)
{
    char length = 0;
    while (ascii_input[length] != termination){
        //check if \n terminated when it is not given as termination char (means error, user entered too less parameters)
        if (ascii_input[length] == '\n'){
            //for safety add zero termination
            zero_terminated_output[length] = 0;
            return 0;
        }
        //check for overlength
        if (length >= MAX_LENGTH_STRING){
            //for safety add zero termination
            zero_terminated_output[length] = 0;
            return 0;
        }
        zero_terminated_output[length] = ascii_input[length];
        length++;
    }
    //shall the string be filled with spaces until specific index?
    if (fill_with_space_until){
        while (length < fill_with_space_until){
            zero_terminated_output[length] = ' ';
            length++;
        }
    }
    zero_terminated_output[length] = 0;
    
    return length;
}

/**************************************************************************//**
* @brief
*    Parses a various number as string of the input
*    Any input argument (number of the event, hex-id, ...)
*
* @param ascii_input
*    String that was typed in.
*
* @param base
*    Base of the input string number, either 10 or 16.
*
* @param binary_output
*    The binary output of the converted ASCII input string.
*
* @param termination
*    The termination character of the input.
*
* @return
*    The length of the parsed string, zero if error.
*****************************************************************************/
char CommandParseDecodeNumber(char *ascii_input, char base, char *binary_output, char termination)
{
    char length = 0;
    if (base == 10)
    {
        //with base 10 only one number (2 digits)
        //first digit (value 10*) check if valid base 10 number
        if (ascii_input[length] >= '0' && ascii_input[length] <= '9'){
            binary_output[length] = (ascii_input[length] - '0') * 10;
            length++;
        }
        else{
            return 0;
        }
        //second digit (value 1*) check if valid base 10 number
        if (ascii_input[length] >= '0' && ascii_input[length] <= '9'){
            binary_output[length-1] += ascii_input[length] - '0';
        }
        else{
            return 0;
        }
        if (ascii_input[length + 1] != termination){
            return 0;
        }
        return length;
    }
    else if (base == 16)
    {
        //with base 16 only multiples of 2 digits
        while (ascii_input[length] != termination){
            //first digit MSB check if valid base 16 number
            if (ascii_input[length] >= '0' && ascii_input[length] <= '9') {
                binary_output[length>>1] = (ascii_input[length] - '0') << 4;
                length++;
            }
            else if (ascii_input[length] >= 'A' && ascii_input[length] <= 'F'){
                binary_output[length>>1] = (ascii_input[length] - 'A' + 0x0A) << 4;
                length++;
            }
            else{
                return 0;
            }
            //second digit (value 1*) check if valid base 10 number
            if (ascii_input[length] >= '0' && ascii_input[length] <= '9') {
                binary_output[length>>1] |= ascii_input[length] - '0';
                length++;
            }
            else if (ascii_input[length] >= 'A' && ascii_input[length] <= 'F'){
                binary_output[length>>1] |= ascii_input[length] - 'A' + 0x0A;
                length++;
            }
            else{
                return 0;
            }
        }
        return length>>1;
    }
    else
    {
        return 0;
    }
}

/**************************************************************************//**
* @brief
*    Prints the help text, that contains all commands.
*
*****************************************************************************/
void CommandParserPrintHelp(void)
{
    printf("\n");
    printf("<> Kommandos fuer AlcoLyzer Console:\n");
    printf("  help                                - Druckt diese Command Liste\n");
    printf("  ping                                - Pingrequest zum BierAlyzer senden\n");
    printf("  quit                                - Beendet den AlcoLyzer\n");
    printf("  get user <HexID>                    - Abfrage eines Trinkers und dessen Daten\n");
    printf("                                      - - Bsp.: get user 0F (zwei hex Digits!)\n");
    printf("  get user *                          - Abfrage aller Trinker (Trinkerliste)\n");
    printf("  del user <HexID>                    - Loeschen eines Trinkers\n");
//  printf("  del user *                          - Loeschen aller Trinker\n");
    printf("  set user <HexID> <Name> <RFID-ID>   - Registrieren eines neuen Trinkers\n");
    printf("                                      - - Bsp.: set user 0F Michi 00AB29AFDE\n");
    printf("  set user <HexID> <Name> *           - s.o., verwendet aufgelegte RFID Karte\n");
    printf("  get evnt <EventID>                  - Abfrage eines Events und dessen Daten\n");
    printf("                                      - - Bsp.: get evnt 08 (zwei Digits!)\n");
    printf("  get evnt *                          - Abfrage aller Events (Eventliste)\n");
    printf("  del evnt <EventID>                  - Loeschen eines Events\n");
//  printf("  del evnt *                          - Loeschen aller Events\n");
    printf("  set evnt <EventID> <Datum> <Beschr> - Registrieren eines neuen Events\n");
    printf("                                      - - Bsp.: set evnt 07 31.01.16 Biertrinken\n");
    printf("  get stgs *                          - Abfrage der Settings des BierAlyzers\n");
    printf("  set stgs clocksync                  - Synchronisation der internen Uhr\n");
    printf("  set stgs eventid=<EventID>          - Einstellen der Event ID (zwei Digits!)\n\n");
}