#ifndef QCLI_HPP
#define QCLI_HPP
/*
 * ru utf 8
 *      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. *
*/

/*
 * Опишу подмножество классов CLI заточенных на работы с QT
 * ----------------------------------------------------------------------------
 * Основное нововведение поле dummy в описалове суб-команды кастится как QEvent
 * что позволит использовать систему сообщений QT в качестве акторов суб-команд
 * 
 * QCLICommandWithSub - работает с картой команд, в которой можно актором задать
 *              QEvent. это событие асинхронно постится в приложение, и не должно
 *              создавать проблем с синхронизацией ниток
 * 
 * QCLICommandWithSub:QEvent - событие, которое способно себя зарегистрировать когда надо.
 *              Оно в помощ, если лень назначать коды.
 * QCLICommandWithSub.registeer_dummy_events - помогает автоматично
 *          зарегистрирования используемые события.
*/

#include <QEvent>
#include <QObject>
#include <QString>
#include <QChar>
#include "cli.hpp"


namespace cli {

// takes 1st word from s to w, and return position after word
// @arg s - source string
// @return  NULL - cant parse value
//          w -   updates to new position after word
    const char*     pass_word(QString* w, const char* s);
    const QChar*    pass_word(QString* w, const QChar* s);

    int space_len(const QChar* s);
    int space_len(const QChar* s, unsigned len);
    int word_len(const QChar* s, unsigned len);

    //* word_len + check that after word is space or EOL
    //* \return -1 if ccheck fails
    int name_len(const QChar* s, unsigned len);

    //* look to EOL or Z
    int line_len(const QChar* s, unsigned len);

    //* \return < 0 - no eol
    //* \returm = 0 - Z ended
    //* \return >0  - len of EOL token
    int is_eol(const QChar* s);

    void print(CLI_shell* shell, QStringList x);
};
//-----------------------------------------------------------------------------
namespace cli {

// this event can be self-registered. use it to provide dummy events
class QEvent : public ::QEvent {
public:
    typedef ::QEvent inherited;
    QEvent():inherited(inherited::None){}
    explicit QEvent(Type type):inherited(type){}
    QEvent(const QEvent &other):inherited(other){}
    QEvent(QEvent &other):inherited(other){
        //предполагается динамическая инициализация событий в системе. 
        //это позволит более надежно использовать события для CLI
        if (type() == inherited::None){
            other.register_me();
            t = other.type();
        }
    }

    void register_me(){
        t = (Type)registerEventType();
    }

    // used this to provide event from cmds -> dummy_target
    virtual QEvent&& copy(void) {
        return std::move(*(new QEvent(*this)));
    }
};


template<typename T>
class QEventWith : public QEvent{
public:
	typedef QEventWith<T>	type_t;
	typedef T				val_t;
	T	val;

	T& value() {return val;}


	QEventWith():QEvent(){}
	QEventWith(type_t &other)
		:QEvent(other), val(other.val)
	{}

	virtual QEvent&& copy(void) {
		return std::move(*(new type_t(*this)));
	}

	type_t&& dup(void) {
		return std::move(*(new type_t(*this)));
	}

	type_t& assign(const T&	x) &{
		val = x;
		return *this;
	}

	type_t&& assign(const T&	x) && {
		val = x;
		return std::move(*this);
	}
};

using IntEvent = QEventWith<long>;

template <typename T>
class EnumEvent: public IntEvent{
	public:
		typedef T val_t;

	T value() {return (T)val;}

	EnumEvent<T>&& dup(void) {
		return std::move(*(EnumEvent<T>*)(new IntEvent(*this)));
	}


	EnumEvent<T>& assign(T	x) &{
		val = (long)x;
		return *this;
	}

	EnumEvent<T>&& assign(T	x) && {
		val = (long)x;
		return std::move(*this);
	}
};




template <typename T>
using QCmdItem = struct CmdItem<T, QEvent*>;

template <typename T>
using QCommandsVarDef = QCmdItem<T>[];
template <typename T>
using QCommandsDef = const QCmdItem<T>[];

template <typename T>
using QCommandVarSet = QCmdItem<T>*;
template <typename T>
using QCommandSet = const QCmdItem<T>*;

// this type use as aliased pointer to local CommandSet
typedef QCommandVarSet<CLICommandBase>   QBaseVarCommands;
typedef QCommandSet<CLICommandBase>      QBaseCommands;

}; //namespace cli



class QCLIDeviceCommands : public CLIDeviceCommands
{
public:
    typedef CLIDeviceCommands inherited;

    QCLIDeviceCommands(QObject* parent)
        :inherited(), dummy_target(parent) {}
    QCLIDeviceCommands(const_cmd _cmd, QObject* parent)
        :inherited(_cmd ), dummy_target(parent) {}
    template< typename T>
    QCLIDeviceCommands(const_cmd _cmd, QObject* parent, cli::QCommandSet<T> cmds)
        :inherited(_cmd, (cli::CommandSet<T>)cmds), dummy_target(parent) {}


    virtual bool cli_process_dummy(CLI_shell* shell, const cli::CmdItem<CLICommandBase>& cmd);

    using inherited::cli_process;

    template<typename T>
    int cli_process(CLI_shell* shell, const_line line, cli::QCommandSet<T> _cmds){
        return cli_process(shell, line, (cmds_t) _cmds);
    }

    using inherited::print_brief;
    using inherited::print_full;
    template <typename T>
    void print_brief(CLI_shell* shell, cli::QCommandSet<T> x){
        print_brief(shell, (cmds_t)x);
    }
    template <typename T>
    void print_full(CLI_shell* shell, cli::QCommandSet<T> x){
        print_full(shell, (cmds_t)x);
    }

public:
    typedef cli::QEvent QEvent;
    // registers sub_cmds events, if they are not registered
    void register_dummy_events();

    virtual void emit_event(QEvent&& x);
    virtual void emit_event(QObject* target, QEvent&& x);

protected:
    QObject* dummy_target;
};



#endif // QCLI_HPP
