/*
 * hal_shell.hpp
 *
 *  Created on: 19 мар. 2019 г.
 *      Author: Lityagin Aleksandr
 *      Author: alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   - Neither the name of ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. *
 */

#ifndef HAL_LIB_HAL_SHELL_HPP_
#define HAL_LIB_HAL_SHELL_HPP_

/*
 * Организация консоли на потоке HAL StdIO_Device.
 *  Консоль предоставляет  метод execute(prompt) для реализации интерфейса строки ввода.
 *  При аварии StdIO_Device, работа execute прекращается.
 *
 * Device_CLI_shell - подключается к потоку StdIO_Device.
 *      дает StdOUT_PrintBuffer для печати в StdOut
 *      использует интерфейс ввода строки CLI_LineInput из StdIn
 *
 * TODO: возможно стоит убрать возможность выхода execute, по аварии io,
 *      во встроенном приложении execute должен быть вечным?
 *
 * App_CLI_shell - дает возможность завершить шелл методом terminate.
 *
 *
 * */



#include "cli.hpp"

// provide: StdOUT_PrintBuffer, StdIO_Device,
// provide  AppShellIO
#include <project-cli.h>

#include "cli_line.hpp"
#include "vt_hal.hpp"

#ifndef CLI_SHELLPRINT_LIMIT
#define CLI_SHELLPRINT_LIMIT (CLI_SHELLLINE_LIMIT*4)
#endif


typedef StdOUT_PrintBuffer<CLI_SHELLPRINT_LIMIT> CLIShellPrinter;

class Device_CLI_shell
    : public CLI_shell
    , public StdOUT_ProxyPrint
    //,  public Device_Observer
{
public:
    typedef CLI_shell inherited;
    Device_CLI_shell();
    Device_CLI_shell(StdIO_Device *device);
    virtual ~Device_CLI_shell();
//    virtual void handleEvent(StdIO_Device_Observable *observable){this->addChar(observable->get_char());}
//    virtual void handleEvent(Observer_Meta *observable){this->handleEvent(((StdIO_Device_Observable * )observable));}

    typedef VTerminalBaseDevice termnal_t;
protected:
    CLIShellPrinter     printer;
    termnal_t           device_terminal;
    StdIO_Device*       device;
    CLI_LineInput       input;
public:
    static const unsigned get_poll_toms = 100;

    void init(StdIO_Device *device);
    StdIO_Device* io(){return device;}
    StdPrint&     out(){return printer;}
    CLI_LineInput& in(){return input;}
    termnal_t&     terminal() {return device_terminal;}

    void clear();
    //  отбрасывает из буффера все что перед курсором
    void dropProcessed();

    // основной цикл работы - набирает в cmdline строку из io,
    // парсит и исполняет ее
    void execute(const char* prompt = ">");

    void addChar(u8 ch);
    // берет из позиии курсора строку (курсор не обязательно может находится в cmdline )
    // если в курсоре нет текста, то читает строку из io()
    virtual int getLine(char* line, unsigned limit, char eol = EOL);
    //* read line in cmdline from io, waits EOL
    virtual int readLine(char* line, unsigned limit);
    //* read string in cmdline from io, with length
    virtual int readStr(char* line, unsigned limit);

    // печатает в device
    virtual int puts(const char* s);
    // puts + EOL
    virtual int putln(const char* s);

    virtual void on_fail(const char* s);
    virtual bool ask_yes(void);
};



// эта шелл прерываться и может завершаться командой exit
class App_CLI_Shell : public Device_CLI_shell
                    , public CLICommand
{
public:
    typedef Device_CLI_shell    inherited;
    typedef CLICommand          cmd_t;
    App_CLI_Shell();
    App_CLI_Shell(StdIO_Device *device, const_cmd shell_name = default_shell_name);

public:
    void execute(const char* prompt);
    void terminate() {terminated = true;}
    bool is_terminated() const {return terminated;}
    //* readline from io
    virtual int readLine(char* line, unsigned limit);
    //* read string in cmdline from io, with length
    //virtual int readStr(char* line, unsigned limit); //TODO

public:
    virtual int cli_process(CLI_shell* shell, const_line line);
    virtual void cli_help(CLI_shell* shell);

public:
    void cli_exit(CLI_shell* shell){
        (void)shell;
        terminate();
    }

    static const_cmd default_shell_name;

protected:
    virtual void init_my_cmd();

    bool terminated;
};



//=============================================================================
template <typename TShell, unsigned HystoryLimit>
class ShellWithHystory : public TShell {
public:
    typedef TShell inherited;

    using typename TShell::const_cmd;
    using TShell::default_shell_name;
    using TShell::in;

    ShellWithHystory()
        : inherited() 
        , hystory_edit(in(), hystory_buf)
    {
        hystory_edit.edit_by( in().edit() );
        in().edit_by(&hystory_edit);
    }

    ShellWithHystory(StdIO_Device *device, const_cmd shell_name = default_shell_name)
        : inherited(device, shell_name) 
        , hystory_edit(in(), hystory_buf)
    {
        hystory_edit.edit_by( in().edit() );
        in().edit_by(&hystory_edit);
    }

public:
    typedef cli::HystoryBuffer< CLIBufLine<HystoryLimit> > HystoryBuf;

    cli::CLIEditHistory hystory_edit;
    HystoryBuf          hystory_buf;
};


#endif /* HAL_LIB_HAL_SHELL_HPP_ */
