/**
 * \file Ifx_Shell.c
 * \brief shell functions.
 *
 * \version disabled
 * \copyright Copyright (c) 2013 Infineon Technologies AG. All rights reserved.
 *
 *
 *                                 IMPORTANT NOTICE
 *
 *
 * Use of this file is subject to the terms of use agreed between (i) you or 
 * the company in which ordinary course of business you are acting and (ii) 
 * Infineon Technologies AG or its licensees. If and as long as no such 
 * terms of use are agreed, use of this file is subject to following:


 * Boost Software License - Version 1.0 - August 17th, 2003

 * Permission is hereby granted, free of charge, to any person or 
 * organization obtaining a copy of the software and accompanying 
 * documentation covered by this license (the "Software") to use, reproduce,
 * display, distribute, execute, and transmit the Software, and to prepare
 * derivative works of the Software, and to permit third-parties to whom the 
 * Software is furnished to do so, all subject to the following:

 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer, must
 * be included in all copies of the Software, in whole or in part, and all
 * derivative works of the Software, unless such copies or derivative works are
 * solely in the form of machine-executable object code generated by a source
 * language processor.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.

 *
 */

//---------------------------------------------------------------------------
#include "Ifx_Shell.h"
#include "_Utilities/Ifx_Assert.h"
#include "Cpu/Std/IfxCpu_Intrinsics.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define IFX_SHELL_LLD              "%lld "
#define IFX_SHELL_LLX              "%llx "
#define IFX_SHELL_LLU              "%llu "

//---------------------------------------------------------------------------
#define IFX_SHELL_MAX_MESSAGE_SIZE 255
//---------------------------------------------------------------------------

/* Macro to detect space character */
#define ISSPACE(c)           (((c) == ' ') || ((c) == '\t'))

/* Macro to only execute parameter if echo is enabled for this shell */
#define IFX_SHELL_IF_ECHO(X) {if (shell->control.echo) {X; }}

/* Macro to write lots of spaces */
#define IFX_SHELL_WRITE_SPACES(X) \
    {int ii; for (ii = 0; ii < (X); ii++) {IfxStdIf_DPipe_print(shell->io, " "); }}

/* Macro to write lots of backspaces */
#define IFX_SHELL_WRITE_BACKSPACES(X) \
    {int ii; for (ii = 0; ii < (X); ii++) {IfxStdIf_DPipe_print(shell->io, "\b"); }}

//---------------------------------------------------------------------------
char Ifx_Shell_cmdBuffer[IFX_CFG_SHELL_CMD_LINE_SIZE * IFX_CFG_SHELL_CMD_HISTORY_SIZE];
//---------------------------------------------------------------------------
void                     Ifx_Shell_execute(Ifx_Shell *shell, pchar commandLine);
void                     Ifx_Shell_cmdEscapeProcess(Ifx_Shell *shell, char EscapeChar1, char EscapeChar2);
const Ifx_Shell_Command *Ifx_Shell_commandListFind(Ifx_Shell *shell, pchar commandLine, pchar *args, Ifx_Shell_CommandListConst *commandList);
static boolean           Ifx_Shell_matchCommand(pchar *argsPtr, pchar *match);

//---------------------------------------------------------------------------
/**
 * \brief Check whether the args is already at the end.
 * \param args The argument null-terminated string
 */
boolean Ifx_Shell_isEndOfLine(pchar args)
{
    return ((args == NULL_PTR) || (*args == IFX_SHELL_NULL_CHAR)) ? TRUE : FALSE;
}


static boolean Ifx_Shell_writeResult(Ifx_Shell *shell, Ifx_SizeT Code)
{
    Ifx_SizeT length = sizeof(Code);
    boolean   result = IfxStdIf_DPipe_write(shell->io, &Code, &length, TIME_INFINITE);

    IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, result != FALSE);

    return result;
}


//---------------------------------------------------------------------------
boolean Ifx_Shell_showHelpSingle(Ifx_Shell_CommandListConst commandList, IfxStdIf_DPipe *io, boolean briefOnly, boolean singleCommand)
{
    const Ifx_Shell_Command *command          = commandList;
    uint32                   index            = 0;
    pchar                    space            = "";
    pchar                    spaceParam       = "";

    while (command->commandLine != NULL_PTR)
    {
        pchar   help     = command->help;
        pchar   chunk;
        boolean isParam  = FALSE;
        boolean isSyntax = FALSE;
        IfxStdIf_DPipe_print(io, "%s%s", space, command->commandLine);

        while (*help != IFX_SHELL_NULL_CHAR)
        {
            Ifx_SizeT count;
            pchar     matchp     = "/p";
            pchar     matchs     = "/s";
                 spaceParam = "";

            if (Ifx_Shell_matchCommand(&help, &matchp))
            {   /* Parameter description found */
                isParam    = TRUE;
                isSyntax   = FALSE,
                spaceParam = "      ";
            }
            else if (Ifx_Shell_matchCommand(&help, &matchs))
            {   /* Parameter description found */
                isParam    = FALSE;
                isSyntax   = TRUE,
                spaceParam = "    SYNTAX: ";
            }
            else
            {     /* Other text */
                if (isParam)
                { /* Show param details*/
                    spaceParam = "        ";
                }
                else if (isSyntax)
                {   /* Show param details*/
                    spaceParam = "            ";
                }
            }

            /* Split print by ENDL */
            chunk = strstr(help, ENDL);

            if (chunk == NULL_PTR)
            {
                chunk = help + strlen(help);
            }
            else
            {
                if (!briefOnly)
                {
                    chunk += strlen(ENDL);
                }
            }

            count = chunk - help;
            IfxStdIf_DPipe_print(io, "%s%s", space, spaceParam);
            IfxStdIf_DPipe_write(io, (void *)help, &count, TIME_INFINITE);

            help += count;

            if (briefOnly)
            {   /* Print Brief only */
                break;
            }
        }

        IfxStdIf_DPipe_print(io, ENDL);

        if ((command->call == NULL_PTR) && (index == 0))
        {
            index = 1;
            space = "  ";
        }

        command = &command[1];

        if (singleCommand)
        {
            break;
        }
    }

    return TRUE;
}


boolean Ifx_Shell_showHelpSingleCommand(pchar args, void *shellPtr, IfxStdIf_DPipe *io)
{
    Ifx_Shell_CommandListConst commandList;
    const Ifx_Shell_Command   *shellCommand = Ifx_Shell_commandListFind(shellPtr, args, &args, &commandList);

    if (shellCommand != NULL_PTR)
    {
        if (shellCommand->call == NULL_PTR)
        {
            Ifx_Shell_showHelpSingle(commandList, io, FALSE, FALSE);
        }
        else
        {
            if (commandList->call == NULL_PTR)
            {
                IfxStdIf_DPipe_print(io, "%s ", commandList->commandLine);
            }

            Ifx_Shell_showHelpSingle(shellCommand, io, FALSE, TRUE);
        }

        return TRUE;
    }
    else
    {
        IfxStdIf_DPipe_print(io, "unknown command");
        return FALSE;
    }
}


boolean Ifx_Shell_showHelp(pchar args, void *shellPtr, IfxStdIf_DPipe *io)
{
    sint32     i;
    Ifx_Shell *shell  = shellPtr;
    boolean    result = TRUE;

    if (*args == IFX_SHELL_NULL_CHAR)
    {
        for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++)
        {
            if (shell->commandList[i] != NULL_PTR)
            {
                Ifx_Shell_showHelpSingle(shell->commandList[i], io, TRUE, FALSE);
            }
        }
    }
    else
    {
        result = Ifx_Shell_showHelpSingleCommand(args, shellPtr, io);
    }

    return result;
}


boolean Ifx_Shell_protocolStart(pchar args, void *data, IfxStdIf_DPipe *io)
{
    Ifx_Shell *shell  = data;
    boolean    Result = TRUE;

    if (Ifx_Shell_matchToken(&args, "?") != FALSE)
    {
        IfxStdIf_DPipe_print(io, "Syntax     : protocol start" ENDL);
        IfxStdIf_DPipe_print(io, "           > start a protocol" ENDL);
    }
    else if (Ifx_Shell_matchToken(&args, "start") != FALSE)
    {
        if ((shell->protocol.start != NULL_PTR) && (shell->protocol.object != NULL_PTR))
        {
            Result                  = shell->protocol.start(shell->protocol.object, io);
            shell->protocol.started = (Result != FALSE);

            if (shell->protocol.onStart != NULL_PTR)
            {
                shell->protocol.onStart(shell->protocol.object, shell->protocol.onStartData);
            }
        }
        else
        {
            Result = FALSE;
        }
    }
    else
    {}

    return Result;
}


boolean Ifx_Shell_bbProtocolStart(pchar args, void *data, IfxStdIf_DPipe *io)
{
    boolean result = TRUE;

    if (Ifx_Shell_matchToken(&args, "?") != FALSE)
    {
        IfxStdIf_DPipe_print(io, "Syntax     : protocol start" ENDL);
        IfxStdIf_DPipe_print(io, "           > start a protocol" ENDL);
    }
    else if (Ifx_Shell_matchToken(&args, "protocol") != FALSE)
    {
        result = Ifx_Shell_protocolStart(args, data, io);
    }
    else
    {}

    return result;
}


//---------------------------------------------------------------------------
void Ifx_Shell_initConfig(Ifx_Shell_Config *config)
{
    uint32 i;

    for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++)
    {
        config->commandList[i] = NULL_PTR;
    }

    config->standardIo           = NULL_PTR;
    config->echo                 = TRUE;
    config->protocol.execute     = NULL_PTR;
    config->protocol.object      = NULL_PTR;
    config->protocol.onStart     = NULL_PTR;
    config->protocol.onStartData = NULL_PTR;
    config->protocol.start       = NULL_PTR;
    config->protocol.started     = FALSE;
    config->sendResultCode       = FALSE;
    config->showPrompt           = TRUE;
    config->standardIo           = NULL_PTR;
}


boolean Ifx_Shell_init(Ifx_Shell *shell, const Ifx_Shell_Config *config)
{
    sint32 i;
    char **CmdHistory = NULL_PTR;   /* Pointer to array of pointers for command history items */

    /* Ensure state variable is cleared */
    memset(shell, 0, sizeof(*shell));

    shell->protocol               = config->protocol;
    shell->protocol.started       = FALSE;

    shell->io                     = config->standardIo;
    shell->control.showPrompt     = config->showPrompt;
    shell->control.sendResultCode = config->sendResultCode;
    shell->control.echo           = config->echo;
    shell->control.echoError      = TRUE;
    shell->control.enabled        = TRUE;

    shell->locals.escBracketNum   = IFX_SHELL_NULL_CHAR; /* Used to cache number in sequence "ESC [ 1/2/3/4 ~" */
    shell->locals.cmdState        = IFX_SHELL_CMD_STATE_NORMAL;

    /* Copy command line buffer pointer into state variable */
    shell->cmd.cmdStr = shell->locals.cmdStr;

    /* Initialize command history space and cache pointer */
    memset(Ifx_Shell_cmdBuffer, 0, sizeof(Ifx_Shell_cmdBuffer));
    shell->cmdHistory[0] = &Ifx_Shell_cmdBuffer[0];

    for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++)
    {
        shell->commandList[i] = config->commandList[i];
    }

    /* Initialize command history pointers */
    CmdHistory = shell->cmdHistory;

    for (i = 1; i < IFX_CFG_SHELL_CMD_HISTORY_SIZE; i++)
    {
        CmdHistory[i] = &CmdHistory[i - 1][IFX_CFG_SHELL_CMD_LINE_SIZE];    /* Items are just offsets into a large allocated area */
    }

    shell->cmd.historyItem = IFX_SHELL_CMD_HISTORY_NO_ITEM;

    /* Pre-load useful commands into history buffer */
    strcpy(CmdHistory[0], "help");
    //strcpy(CmdHistory[1], "protocol start");

    if (shell->control.showPrompt != 0)
    {
        IfxStdIf_DPipe_print(shell->io, ENDL);
        IfxStdIf_DPipe_print(shell->io, IFX_CFG_SHELL_PROMPT);
    }

    return TRUE;
}


void Ifx_Shell_process(Ifx_Shell *shell)
{
    Ifx_SizeT          i, j;           /* Loop variables */
    Ifx_SizeT          count;
    Ifx_SizeT          readCount;
    boolean            NormalKeyPress; /* Indicates if this is a normal keypress, i.e. not part of an escape code */

    Ifx_Shell_CmdLine *Cmd         = &shell->cmd;
    char              *inputbuffer = shell->locals.inputbuffer;
    char              *cmdStr      = shell->locals.cmdStr;
    char             **CmdHistory  = shell->cmdHistory;

    if (shell->control.enabled == 0)
    {
        return;
    }

    if ((shell->protocol.object != NULL_PTR) && (shell->protocol.started != FALSE))
    {
        shell->protocol.execute(shell->protocol.object);
    }
    else
    {
        /**** NORMAL MODE ****/

        /********************************************************************************/
        /* Read all characters until enter inclusive.                                   */
        /* If the command is bigger than IFX_SHELL_CMD_SIZE, the command is ignored.    */
        /*                                                                              */
        /* Escape sequences are handled by a state machine.                             */
        /* The following escape sequences (prefix "ESC [") are supported:               */
        /*                                                                              */
        /* A  - up      B - down       C - right     D - left                           */
        /* 1~ - HOME   2~ - INSERT    3~ - DELETE   5~ - END                            */
        /*                                                                              */
        /* Backspace ('\b') is also supported.                                          */
        /********************************************************************************/

        count     = 0;
        readCount = IFX_CFG_SHELL_CMD_LINE_SIZE - count;
        IfxStdIf_DPipe_read(shell->io, &inputbuffer[count], &readCount, TIME_NULL);
        count    += readCount;

        for (i = 0; i < count; i++)
        {
            /* By default, we assume character is part of escape sequence */
            NormalKeyPress = FALSE;

            /* Process key pressed */
            switch (inputbuffer[i])
            {
            /* New line (ENTER) */
            case '\n':
            case '\r':
                /* Print new line to terminal if requested */
                IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, ENDL))

                /* Execute command if length is valid - i.e. not an over-full buffer
                 * (prevents attempted execution of junk) */
                if (Cmd->length < IFX_CFG_SHELL_CMD_LINE_SIZE)
                {
                    cmdStr[Cmd->length] = IFX_SHELL_NULL_CHAR;  /* Terminate cmdStr */

                    if (Cmd->historyAdd != FALSE)
                    {
                        /* Shuffle history up */
                        for (j = IFX_CFG_SHELL_CMD_HISTORY_SIZE - 1; j > 0; j--)
                        {
                            /* Copy text */
                            strncpy(CmdHistory[j], CmdHistory[j - 1], IFX_CFG_SHELL_CMD_LINE_SIZE);
                        }

                        /* Copy in new entry */
                        strncpy(CmdHistory[0], cmdStr, IFX_CFG_SHELL_CMD_LINE_SIZE);
                    }

                    /* Execute command */
                    Ifx_Shell_execute(shell, cmdStr);
                }

                /* Show prompt if in main shell */
                if (shell->control.showPrompt != 0)
                {
                    IfxStdIf_DPipe_print(shell->io, IFX_CFG_SHELL_PROMPT);
                }

                /* Reset command line buffer length */
                Cmd->length = 0;

                /* Reset command line buffer cursor position */
                Cmd->cursor = 0;

                /* Clear flag */
                Cmd->historyAdd = FALSE;

                /* Ensure we're not in command history list */
                Cmd->historyItem = IFX_SHELL_CMD_HISTORY_NO_ITEM;
                break;

            /* Backspace (may occur in middle of text if cursor location is not at end) */
            case '\b':

                if (Cmd->cursor > 0)
                {
                    /* Update on screen */
                    if (shell->control.echo != 0)
                    {
                        /* Move left one character */
                        IfxStdIf_DPipe_print(shell->io, "\b");

                        /* Update line with new characters */
                        for (j = Cmd->cursor; j < Cmd->length; j++)
                        {
                            IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[j]);
                        }

                        /* Write over duplicated character at end */
                        IfxStdIf_DPipe_print(shell->io, " ");
                        IFX_SHELL_WRITE_BACKSPACES((Cmd->length - Cmd->cursor) + 1)
                    }

                    /* Update in command line variable. Shuffle text left */
                    strncpy(&cmdStr[Cmd->cursor - 1], &cmdStr[Cmd->cursor], Cmd->length - Cmd->cursor);

                    /* Terminate string at end of shorter string */
                    cmdStr[Cmd->length - 1] = IFX_SHELL_NULL_CHAR;

                    Cmd->length--;
                    Cmd->cursor--;

                    /* Command line has been modified */
                    Cmd->historyAdd = TRUE;
                }

                break;

            /* Escape character */
            case '\x1B':       /*'\x1B': */
                shell->locals.cmdState = IFX_SHELL_CMD_STATE_ESCAPE;
                break;

            /* '[' - check to see if this is second part of an escape sequence */
            case '[':

                if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE)
                {
                    /* ESC [ pressed */
                    shell->locals.cmdState = IFX_SHELL_CMD_STATE_ESCAPE_BRACKET;
                }
                else
                {
                    NormalKeyPress = TRUE;
                }

                break;

            /* Check for supported characters in escape sequences ( ESC [ A/B/C/D ) */
            case 'A':
            case 'B':
            case 'C':
            case 'D':

                if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE_BRACKET)
                {
                    /* Process arrow keys */
                    Ifx_Shell_cmdEscapeProcess(shell, inputbuffer[i], 0);

                    /* End of escape sequence */
                    shell->locals.cmdState = IFX_SHELL_CMD_STATE_NORMAL;
                }
                else
                {
                    NormalKeyPress = TRUE;
                }

                break;

            /* Check for supported characters in escape sequences (ESC [ 2/4/5 ~) */
            case '1':
            case '2':
            case '3':
            case '4':

                if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE_BRACKET)
                {
                    /* Store number for use once complete escape sequence is confirmed (below) */
                    shell->locals.escBracketNum = inputbuffer[i];
                    shell->locals.cmdState      = IFX_SHELL_CMD_STATE_ESCAPE_BRACKET_NUMBER;
                }
                else
                {
                    NormalKeyPress = TRUE;
                }

                break;

            /* Check for supported characters in escape sequences (ESC [ 2/4/5 ~) */
            case '~':

                if (shell->locals.cmdState == IFX_SHELL_CMD_STATE_ESCAPE_BRACKET_NUMBER)
                {
                    /* Process home/delete/end */
                    Ifx_Shell_cmdEscapeProcess(shell, shell->locals.escBracketNum, '~');

                    /* End of escape sequence */
                    shell->locals.cmdState = IFX_SHELL_CMD_STATE_NORMAL;
                }
                else
                {
                    NormalKeyPress = TRUE;
                }

                break;

            /* Normal character - add to command string */
            default:
                NormalKeyPress = TRUE;
                break;
            }

            IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, Cmd->length >= Cmd->cursor);    /* Sanity check */

            /* If this was a normal key press (not part of an escape sequence),
             * add it to the command string */
            if (NormalKeyPress != FALSE)
            {
                /* Ensure state machine is reset */
                shell->locals.cmdState = IFX_SHELL_CMD_STATE_NORMAL;

                /* If not filled buffer, add in this character */
                if (Cmd->length < (IFX_CFG_SHELL_CMD_LINE_SIZE - 1))
                {
                    /* Command line has been modified */
                    Cmd->historyAdd = TRUE;

                    /* Copy into command line */
                    cmdStr[Cmd->cursor] = inputbuffer[i];
                    Cmd->cursor++;

                    /* Update length of buffer */
                    Cmd->length = __max(Cmd->length, Cmd->cursor);

                    if (shell->control.echo != 0)
                    {
                        /* echo character to shell output if requested */
                        shell->locals.echo[0] = inputbuffer[i];
                        IfxStdIf_DPipe_print(shell->io, shell->locals.echo);
                    }
                }
                else
                {
                    /* Line too long - ignore further characters */
                    Cmd->historyAdd = FALSE;    /* Invalid command line */
                }
            }
        }
    }
}


void Ifx_Shell_deinit(Ifx_Shell *shell)
{
    (void)shell;                /* ignore compiler warning; */
    // tbd free necessary memory
}


pchar Ifx_Shell_skipWhitespace(pchar args)
{
    if (args != NULL_PTR)
    {
        while ((*args != IFX_SHELL_NULL_CHAR) && (ISSPACE(*args)))
        {
            args = &args[1];
        }
    }

    return args;
}


boolean Ifx_Shell_matchToken(pchar *argsPtr, pchar token)
{
    pchar   savedArguments = *argsPtr;
    char    buffer[256];
    boolean result         = FALSE;

    if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) != FALSE)
    {
        if (strcmp(token, buffer) == 0)
        {
            result = TRUE;
        }
    }

    if (result == FALSE)
    {
        // No match: don't advance pointer
        *argsPtr = savedArguments;
    }

    return result;
}


static boolean Ifx_Shell_matchCommand(pchar *argsPtr, pchar *match)
{
    boolean result         = FALSE;
    pchar   savedArguments = *argsPtr;
    pchar   savedMatch     = *match;
    char    buffer0[256];
    char    buffer1[256];

    if ((Ifx_Shell_parseToken(argsPtr, buffer0, Ifx_COUNTOF(buffer0)) != FALSE)
        && (Ifx_Shell_parseToken(match, buffer1, Ifx_COUNTOF(buffer1)) != FALSE))
    {
        if (strcmp(buffer1, buffer0) == 0)
        {
            result = TRUE;
        }
    }

    if (result == FALSE)
    {
        // No match: don't advance pointer
        *argsPtr = savedArguments;
        *match   = savedMatch;
    }

    return result;
}


boolean Ifx_Shell_parseToken(pchar *argsPtr, char *tokenBuffer, int bufferLength)
{
    int   mindex = 0;
    pchar args   = Ifx_Shell_skipWhitespace(*argsPtr);

    tokenBuffer[0] = IFX_SHELL_NULL_CHAR;

    if (args == NULL_PTR)
    {
        return FALSE;
    }

    if (*args == '\"')
    {
        args = &args[1];

        while ((*args != IFX_SHELL_NULL_CHAR) && (*args != '\"'))
        {
            if (mindex < bufferLength)
            {
                tokenBuffer[mindex] = *args;
                mindex++;
            }

            args = &args[1];
        }

        // error if no closing quote
        if (*args != '\"')
        {
            return FALSE;
        }

        args = &args[1];
    }
    else
    {
        // don't allow unquoted empty tokens
        if (*args == IFX_SHELL_NULL_CHAR)
        {
            return FALSE;
        }

        while ((*args != IFX_SHELL_NULL_CHAR) && (!ISSPACE(*args)))
        {
            if (mindex < bufferLength)
            {
                tokenBuffer[mindex] = *args;
                mindex++;
            }

            args = &args[1];
        }
    }

    // make sure string is zero terminated
    if (bufferLength > 0)
    {
        tokenBuffer[__min(mindex, bufferLength - 1)] = IFX_SHELL_NULL_CHAR;
    }

    *argsPtr = Ifx_Shell_skipWhitespace(args);

    return TRUE;
}


boolean Ifx_Shell_parseAddress(pchar *argsPtr, void **address)
{
    char    buffer[32];
    boolean result;

    *address = 0;

    if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE)
    {
        result = FALSE;
    }
    else
    {
        result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, "%x ", (unsigned int *)address) == 1);
    }

    return result;
}


boolean Ifx_Shell_parseSInt32(pchar *argsPtr, sint32 *value)
{
    sint64  value64;
    boolean result;

    *value = 0;

    if (Ifx_Shell_parseSInt64(argsPtr, &value64) == FALSE)
    {
        result = FALSE;
    }
    else
    {
        *value = (sint32)value64;
        result = TRUE;
    }

    return result;
}


boolean Ifx_Shell_parseUInt32(pchar *argsPtr, uint32 *value, boolean hex)
{
    uint64  value64;
    boolean result;

    *value = 0;

    if (Ifx_Shell_parseUInt64(argsPtr, &value64, hex) == FALSE)
    {
        result = FALSE;
    }
    else
    {
        *value = (uint32)value64;
        result = TRUE;
    }

    return result;
}


boolean Ifx_Shell_parseSInt64(pchar *argsPtr, sint64 *value)
{
    char    buffer[64];
    boolean result;

    *value = 0;

    if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE)
    {
        result = FALSE;
    }
    else
    {
        result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, IFX_SHELL_LLD, value) == 1);
    }

    return result;
}


boolean Ifx_Shell_parseUInt64(pchar *argsPtr, uint64 *value, boolean hex)
{
    char    buffer[64];
    boolean result;

    *value = 0;

    if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE)
    {
        result = FALSE;
    }
    else
    {
        char *bufferPointer = buffer;

        if ((buffer[0] == '0') && (buffer[1] == 'x'))
        {
            bufferPointer = &bufferPointer[2];
            hex           = TRUE;
        }

        if (hex != FALSE)
        {
            result = (bufferPointer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(bufferPointer, IFX_SHELL_LLX, value) == 1);
        }
        else
        {
            result = (bufferPointer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(bufferPointer, IFX_SHELL_LLU, value) == 1);
        }
    }

    return result;
}


boolean Ifx_Shell_parseFloat64(pchar *argsPtr, float64 *value)
{
    char    buffer[64];
    boolean result;

    *value = 0;

    if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE)
    {
        result = FALSE;
    }
    else
    {
        result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, "%lf ", value) == 1);
    }

    return result;
}


boolean Ifx_Shell_parseFloat32(pchar *argsPtr, float32 *value)
{
    char    buffer[64];
    boolean result;

    *value = 0;

    if (Ifx_Shell_parseToken(argsPtr, buffer, Ifx_COUNTOF(buffer)) == FALSE)
    {
        result = FALSE;
    }
    else
    {
        result = (buffer[0] != IFX_SHELL_NULL_CHAR) && (sscanf(buffer, "%f ", value) == 1);
    }

    return result;
}


const Ifx_Shell_Command *Ifx_Shell_commandFind(Ifx_Shell_CommandListConst commandList, pchar commandLine, pchar *args, uint32 *match)
{
    const Ifx_Shell_Command *command     = commandList;
    const Ifx_Shell_Command *result      = NULL_PTR;
    pchar                    cmdLineTemp = commandLine;
    uint32                   matchInit   = 0;
    uint32                   index       = 0;

    boolean                  hasPrefix;

    hasPrefix = (command->commandLine != NULL_PTR) && (command->call == NULL_PTR);
    *match    = 0;

    while (command->commandLine != NULL_PTR)
    {
        pchar  commandTemp     = command->commandLine;
        pchar  commandLineTemp = cmdLineTemp;
        char   buffer[256];
        uint32 matchCount      = matchInit;

        while (Ifx_Shell_matchCommand(&commandLineTemp, &commandTemp) != FALSE)
        {
            matchCount++;
        }

        if ((matchCount > *match) && (Ifx_Shell_parseToken(&commandTemp, buffer, Ifx_COUNTOF(buffer)) == FALSE))
        {
            if ((index == 0) && (command->call == NULL_PTR))
            {   /* List has a prefix */
                cmdLineTemp = commandLineTemp;
                matchInit   = matchCount;
            }

            *match = matchCount;
            *args  = commandLineTemp;
            result = command;
        }
        else
        {
            if ((index == 0) && hasPrefix)
            {
                /* Prefix does not match */
                break;
            }
        }

        command = &command[1];

        index++;
    }

    return result;
}


const Ifx_Shell_Command *Ifx_Shell_commandListFind(Ifx_Shell *shell, pchar commandLine, pchar *args, Ifx_Shell_CommandListConst *commandList)
{
    int                      i;
    const Ifx_Shell_Command *shellCommand = NULL_PTR;
    const Ifx_Shell_Command *Command      = NULL_PTR;
    uint32                   matchMax     = 0;
    uint32                   match;

    for (i = 0; i < IFX_CFG_SHELL_COMMAND_LISTS; i++)
    {
        if (shell->commandList[i] != NULL_PTR)
        {
            shellCommand = Ifx_Shell_commandFind(shell->commandList[i], commandLine, args, &match);

            if ((shellCommand != NULL_PTR) && (match > matchMax))
            {
                matchMax     = match;
                Command      = shellCommand;
                *commandList = shell->commandList[i];
            }
        }
    }

    return Command;
}


void Ifx_Shell_execute(Ifx_Shell *shell, pchar commandLine)
{
    pchar                      args         = NULL_PTR;
    Ifx_Shell_CommandListConst commandList;
    const Ifx_Shell_Command   *shellCommand = Ifx_Shell_commandListFind(shell, commandLine, &args, &commandList);

    if (shellCommand != NULL_PTR)
    {
        if (shellCommand->call == NULL_PTR)
        {
            Ifx_Shell_showHelp(commandLine, shell, shell->io);
        }
        else if (shellCommand->call(args, shellCommand->data, shell->io) != FALSE)
        {
            if (shell->control.sendResultCode != 0)
            {
                Ifx_Shell_writeResult(shell, Ifx_Shell_ResultCode_ok);
            }
        }
        else
        {
            if (shell->control.sendResultCode != 0)
            {
                Ifx_Shell_writeResult(shell, Ifx_Shell_ResultCode_nok);
            }
            else if (shell->control.echoError != 0)
            {
                IfxStdIf_DPipe_print(shell->io, "\r\nShell command error: %s" ENDL, commandLine);
            }
            else
            {}
        }
    }
    else
    {
        if (commandLine[0] != IFX_SHELL_NULL_CHAR)
        {
            if (shell->control.sendResultCode != 0)
            {
                Ifx_Shell_writeResult(shell, Ifx_Shell_ResultCode_unknown);
            }
            else if (shell->control.echoError != 0)
            {
                IfxStdIf_DPipe_print(shell->io, "\r\nUnknown command: %s" ENDL, commandLine);
            }
            else
            {}
        }
    }
}


/****************************************************************************************/
/* Processes escape sequences, including handling command history.                      */
/* The following escape sequences (prefix "ESC [") are supported:                       */
/* A - up     B - down      C - right     D - left                                      */
/* 1~ - HOME  2~ - INSERT   3~ - DELETE   4~ - END                                      */
/*                                                                                      */
/* Parameters:                                                                          */
/*      EscapeChar1 - First character to follow ESC [                                   */
/*      EscapeChar2 - Second character following ESC [ , if applicable                  */
/*                                                                                      */
/****************************************************************************************/
void Ifx_Shell_cmdEscapeProcess(Ifx_Shell *shell, char EscapeChar1, char EscapeChar2)
{
    Ifx_Shell_CmdLine *Cmd    = NULL_PTR; /* Command line editing state */
    char              *cmdStr = NULL_PTR; /* Cached pointer to command line being edited */
    sint32             i      = 0;        /* Loop variable */

    /* Validate parameters */
    boolean            result = (shell != NULL_PTR);

    IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, result != FALSE);

    if (result == FALSE)
    {
        return;                 /* ERROR CASE - no thread data available! */
    }

    /* Cache command state and command line pointer */
    Cmd    = &shell->cmd;
    cmdStr = Cmd->cmdStr;

    /* Validate command line state */
    IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, cmdStr != NULL_PTR);
    IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, Cmd->cursor < IFX_CFG_SHELL_CMD_LINE_SIZE);
    IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, Cmd->length < IFX_CFG_SHELL_CMD_LINE_SIZE);

    /* Switch on first character after ESC [ */
    switch (EscapeChar1)
    {
    case 'A':                  /* Up arrow */

        if (Cmd->historyItem == IFX_SHELL_CMD_HISTORY_NO_ITEM)
        {
            /* Not using list at the moment - take most recent item [0] */
            Cmd->historyItem = 0;
        }
        else
        {
            if (Cmd->historyItem < (IFX_CFG_SHELL_CMD_HISTORY_SIZE - 1))
            {
                /* If not already at oldest, go back one in list */
                Cmd->historyItem++;
            }
        }

        /* Copy text into buffer */
        strncpy(cmdStr, shell->cmdHistory[Cmd->historyItem], IFX_CFG_SHELL_CMD_LINE_SIZE);

        /* echo to screen if requested */
        if (shell->control.echo != 0)
        {
            IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor)  /* Move cursor back to start */
            IFX_SHELL_WRITE_SPACES(Cmd->length)      /* Overwrite text with spaces */
            IFX_SHELL_WRITE_BACKSPACES(Cmd->length)  /* Move cursor back to start */
            IfxStdIf_DPipe_print(shell->io, cmdStr); /* Copy buffer to screen */
        }

        Cmd->cursor     = (Ifx_SizeT)strlen(cmdStr); /* Store cursor position */
        Cmd->length     = Cmd->cursor;               /* Store command line length */
        Cmd->historyAdd = FALSE;                     /* Don't add back to history unless modified */
        break;

    case 'B':                                        /* Down arrow */

        if ((Cmd->historyItem == IFX_SHELL_CMD_HISTORY_NO_ITEM) || (Cmd->historyItem == 0))
        {
            /* Not using list at the moment, or have dropped off the end - just clear command line */
            if (shell->control.echo != 0)
            {
                IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor)       /* Move cursor back to start */
                IFX_SHELL_WRITE_SPACES(Cmd->length)           /* Overwrite text with spaces */
                IFX_SHELL_WRITE_BACKSPACES(Cmd->length)       /* Move cursor back to start */
            }

            Cmd->length      = 0;                             /* Reset command line length */
            Cmd->cursor      = 0;
            Cmd->historyItem = IFX_SHELL_CMD_HISTORY_NO_ITEM; /* Ensure we are not using list */
        }
        else
        {
            /* Within list - move to more recent entry */
            Cmd->historyItem--;

            /* Copy text into buffer */
            strncpy(cmdStr, shell->cmdHistory[Cmd->historyItem], IFX_CFG_SHELL_CMD_LINE_SIZE);

            if (shell->control.echo != 0)
            {
                IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor)  /* Move cursor back to start */
                IFX_SHELL_WRITE_SPACES(Cmd->length)      /* Overwrite text with spaces */
                IFX_SHELL_WRITE_BACKSPACES(Cmd->length)  /* Move cursor back to start */
                IfxStdIf_DPipe_print(shell->io, cmdStr); /* Copy buffer to screen */
            }

            Cmd->cursor = (Ifx_SizeT)strlen(cmdStr);     /* Store cursor position */
            Cmd->length = Cmd->cursor;                   /* Store command line length */
        }

        Cmd->historyAdd = FALSE;
        break;

    case 'C':                  /* Right arrow */

        if (Cmd->cursor < Cmd->length)
        {
            /* Move cursor one place to right */
            IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[Cmd->cursor])) Cmd->cursor++;
        }

        break;

    case 'D':                  /* Left arrow */

        if (Cmd->cursor > 0)
        {
            /* Move cursor one place to left */
            IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, "\b")) Cmd->cursor--;
        }

        break;

    default:
        break;
    }

    /* If second character after ESC [ is ~ then switch on number */
    if (EscapeChar2 == '~')
    {
        switch (EscapeChar1)
        {
        case '1':              /* HOME - move to start of buffer */

            if (Cmd->cursor > 0)
            {
                IFX_SHELL_WRITE_BACKSPACES(Cmd->cursor) Cmd->cursor = 0;
            }

            break;

        case '2':              /* INSERT - insert blank character at cursor and move all remaining characters right one */

            if ((Cmd->cursor < Cmd->length) && (Cmd->length < (IFX_CFG_SHELL_CMD_LINE_SIZE - 1)))
            {
                /* Update on screen */
                if (shell->control.echo != FALSE)
                {
                    /* write over duplicated character at cursor */
                    IfxStdIf_DPipe_print(shell->io, " ");

                    /* Update line with new characters */
                    for (i = Cmd->cursor; i < Cmd->length; i++)
                    {
                        IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[i]);
                    }

                    /* Move cursor back to new place */
                    IFX_SHELL_WRITE_BACKSPACES((Cmd->length + 1) - Cmd->cursor)
                }

                /* Update in command line variable */
                for (i = Cmd->length; i > Cmd->cursor; i--)
                {
                    cmdStr[i] = cmdStr[i - 1];                 /* Shuffle text right */
                }

                cmdStr[Cmd->length + 1] = IFX_SHELL_NULL_CHAR; /* Terminate string at end of longer string */
                cmdStr[Cmd->cursor]     = ' ';                 /* Blank character at cursor */

                Cmd->length++;                                 /* Now one character longer */
            }

            break;

        case '3':              /* DELETE - delete character to right and move all remaining characters left one */

            if (Cmd->cursor < Cmd->length)
            {
                /* Update on screen */
                if (shell->control.echo != 0)
                {
                    for (i = Cmd->cursor; i < (Cmd->length - 1); i++)
                    {
                        /* Update line with new characters */
                        IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[i + 1]);
                    }

                    /* write over duplicated character at end */
                    IfxStdIf_DPipe_print(shell->io, " ");

                    /* Move cursor back to right place */
                    IFX_SHELL_WRITE_BACKSPACES(Cmd->length - Cmd->cursor)
                }

                /* Update in command line variable. Shuffle text left */
                strncpy(&cmdStr[Cmd->cursor], &cmdStr[Cmd->cursor + 1], Cmd->length - Cmd->cursor - 1);

                cmdStr[Cmd->length - 1] = IFX_SHELL_NULL_CHAR; /* Terminate string at end of shorter string */
                Cmd->length--;                                 /* Now one character shorter */
            }

            break;

        case '4':              /* END - ensure cursor is at end */

            while (Cmd->cursor < Cmd->length)
            {
                IFX_SHELL_IF_ECHO(IfxStdIf_DPipe_print(shell->io, "%c", cmdStr[Cmd->cursor])) Cmd->cursor++;
            }

            break;

        default:
            break;
        }
    }
}


void Ifx_Shell_enable(Ifx_Shell *shell)
{
    // Clear the Rx buffer!
    IfxStdIf_DPipe_clearRx(shell->io);
    // Enable the shell
    shell->control.enabled = 1;
}


void Ifx_Shell_disable(Ifx_Shell *shell)
{
    shell->control.enabled = 0;
}


void Ifx_Shell_printSyntax(const Ifx_Shell_Syntax *syntaxList, IfxStdIf_DPipe *io)
{
    const Ifx_Shell_Syntax *syntax = syntaxList;

    while (syntax->syntax != NULL_PTR)
    {
        IfxStdIf_DPipe_print(io, "Syntax     : %s" ENDL, syntax->syntax);
        IfxStdIf_DPipe_print(io, "           > %s" ENDL, syntax->description);
        syntax = &syntax[1];
    }
}
