/*
 * Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

/**
 * @file    shellmatta.h
 * @brief   API definition of the Shellmatta terminal implementation
 * @author  Stefan Strobel <stefan.strobel@shimatta.net>
 */

/**
 * @addtogroup shellmatta_api Shellmatta API description
 * @{
 */

#ifndef _SHELLMATTA_H_
#define _SHELLMATTA_H_

#include <stdint.h>
#include <stdbool.h>

/* global defines */

/**
 * @brief definition of a shellmatta handle
 */
typedef void* shellmatta_handle_t;

/**
 * @brief definition of shellmatta return codes
 */
typedef enum
{
    SHELLMATTA_OK           = 0u,   /**< everything is OK               */
    SHELLMATTA_ERROR            ,   /**< error occured                  */
    SHELLMATTA_CONTINUE         ,   /**< the function is not over       */
    SHELLMATTA_USE_FAULT        ,   /**< parameter error - wrong usage  */
    SHELLMATTA_DUPLICATE        ,   /**< duplicate command              */
    SHELLMATTA_BUSY                 /**< command is busy keep calling   */
} shellmatta_retCode_t;

/**
 * @brief definition of shellmatta insert mode
 */
typedef enum
{
    SHELLMATTA_MODE_INSERT      = 0u,   /**< insert mode    */
    SHELLMATTA_MODE_OVERWRITE   ,       /**< overwrite mode */
} shellmatta_mode_t;

/**
 * @brief definition of shellmatta optionparser agument type
 */
typedef enum
{
    SHELLMATTA_OPT_ARG_NONE     = 0u,   /**< no argument expected   */
    SHELLMATTA_OPT_ARG_REQUIRED,        /**< argument is required   */
    SHELLMATTA_OPT_ARG_OPTIONAL,        /**< argument is optional   */
} shellmatta_opt_argtype_t;

/**
 * @brief definition of shellmatta optionparser agument type
 */
typedef struct
{
    const char                  *paramLong; /**< long parameter string      */
    const char                  paramShort; /**< short parameter char       */
    shellmatta_opt_argtype_t    argtype;    /**< argument type expected     */
} shellmatta_opt_long_t;

/**
 * @brief definition of shellmatta optionparser structure
 */
typedef struct
{
    uint32_t    argStart;   /**< start of the arguments of the command  */
    uint32_t    offset;     /**< current offset of the option parser    */
    uint32_t    nextOffset; /**< offset of the next hunk                */
    uint32_t    len;        /**< length of the current hunk             */
} shellmatta_opt_t;

/**
 * @brief shellmatta command function definition
 * @param[in]   handle      pointer to the instance which is calling the cmd
 * @param[in]   arguments   argument string called to run this command beginning
 *                          with the command itself
 * @param[in]   length      length of the argument string
 */
typedef shellmatta_retCode_t (*shellmatta_cmdFct_t)(const shellmatta_handle_t   handle,
                                                    const char                  *arguments,
                                                    uint32_t                    length);

/**
 * @brief shellmatta write function definition
 * @param[in]   data        data to be written to the output
 * @param[in]   length      length of the data to be written
 */
typedef shellmatta_retCode_t (*shellmatta_write_t)(const char* data, uint32_t length);

/**
 * @brief structure of one shellmatta command
 */
typedef struct shellmatta_cmd
{
    char                    *cmd;       /**< command name                           */
    char                    *cmdAlias;  /**< command alias                          */
    char                    *helpText;  /**< help text to print in "help" command   */
    char                    *usageText; /**< usage text - printed on "help cmd"     */
    shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callback function   */
    struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
} shellmatta_cmd_t;

/**
 * @brief structure of one shellmatta instance
 */
typedef struct
{
    uint32_t            magic;              /**< magic number to check if initialized   */
    char                *buffer;            /**< input buffer                           */
    uint32_t            bufferSize;         /**< size of the input buffer               */
    uint32_t            inputCount;         /**< offset of the current write operation  */
    uint32_t            byteCounter;        /**< counter used to loop over input data   */
    uint32_t            lastNewlineIdx;     /**< index of the lest newline              */
    uint32_t            cursor;             /**< offset where the cursor is at          */
    uint32_t            stdinIdx;           /**< start index of stdin in buffer         */
    uint32_t            stdinLength;        /**< length of the stdin data               */
    char                *historyBuffer;     /**< buffer to store the last commands      */
    uint32_t            historyBufferSize;  /**< size of the history buffer             */
    uint32_t            historyStart;       /**< index of the oldest stored command     */
    uint32_t            historyEnd;         /**< index of the newest stored command     */
    uint32_t            historyRead;        /**< index of the current search            */
    bool                historyReadUp;      /**< flag to show the last history dir      */
    uint32_t            tabCounter;         /**< counts the tabulator key presses       */
    uint32_t            escapeCounter;      /**< counts the characters of an escape seq */
    char                escapeChars[4u];    /**< buffer to save the escape characters   */
    uint32_t            hereStartIdx;       /**< heredoc start of "<<"                  */
    uint32_t            hereDelimiterIdx;   /**< heredoc delimiter index in input       */
    uint32_t            hereLength;         /**< length of the heredoc delimiter        */
    bool                echoEnabled;        /**< if true the input is printed           */
    bool                dirty;              /**< dirty flag to show changes             */
    const char          *prompt;            /**< prompt is printed after every command  */
    char                delimiter;          /**< delimiter (return) to terminate a cmd  */
    shellmatta_mode_t   mode;               /**< mode of the shell                      */
    shellmatta_write_t  write;              /**< pointer to write function              */
    shellmatta_cmd_t    helpCmd;            /**< help command structure                 */
    shellmatta_cmd_t    *cmdList;           /**< pointer to the first command           */
    shellmatta_cmd_t    *continuousCmd;     /**< command to be called continuously      */
    shellmatta_cmd_t    *busyCmd;           /**< command to be polled (busy mode)       */
    bool                cmdListIsConst;     /**< true if the #cmdList was passed during
                                                 initialization                         */
    shellmatta_opt_t    optionParser;       /**< option parser sructure                 */
} shellmatta_instance_t;


shellmatta_retCode_t shellmatta_doInit( shellmatta_instance_t   *inst,
                                        shellmatta_handle_t     *handle,
                                        char                    *buffer,
                                        uint32_t                bufferSize,
                                        char                    *historyBuffer,
                                        uint32_t                historyBufferSize,
                                        const char              *prompt,
                                        const shellmatta_cmd_t  *cmdList,
                                        shellmatta_write_t      writeFct);

shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle,
                                            bool                printPrompt);

shellmatta_retCode_t shellmatta_addCmd(     shellmatta_handle_t handle,
                                            shellmatta_cmd_t    *cmd);

shellmatta_retCode_t shellmatta_removeCmd(  shellmatta_handle_t handle,
                                            shellmatta_cmd_t    *cmd);

shellmatta_retCode_t shellmatta_configure(  shellmatta_handle_t handle,
                                            shellmatta_mode_t   mode,
                                            bool                echoEnabled,
                                            char                delimiter);

shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
                                            char                *data,
                                            uint32_t            size);

shellmatta_retCode_t shellmatta_write(      shellmatta_handle_t handle,
                                            char                *data,
                                            uint32_t            length);

shellmatta_retCode_t shellmatta_read(       shellmatta_handle_t handle,
                                            char                **data,
                                            uint32_t            *length);

shellmatta_retCode_t shellmatta_opt(        shellmatta_handle_t handle,
                                            const char          *optionString,
                                            char                *option,
                                            char                **argument,
                                            uint32_t            *argLen);

shellmatta_retCode_t shellmatta_opt_long(   shellmatta_handle_t         handle,
                                            const shellmatta_opt_long_t *longOptions,
                                            char                        *option,
                                            char                        **argument,
                                            uint32_t                    *argLen);

#ifndef SHELLMATTA_STRIP_PRINTF
shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
                                            const char          *fmt,
                                            ...);
#endif

#endif

/** @} */