#ifndef STATE_H
#define STATE_H

#include <frost_utils.h>

#include "symbol.h"
#include <functional>

namespace Frost {

class State
{
public :

    State();

    void ShowHelp() const;

    void             SetInteractiveMode(s_bool interactive);

    void             ParseLine(const s_str& sLine);
    s_refptr<Symbol> ParseSymbol(const s_str& n, s_str sv, s_str& sError, s_bool bAllowUndefined = false);

    s_refptr<Symbol> CreateEmptySymbol(const s_str& sType, const s_str& sName) const;
    s_bool           TypeExists(const s_str& t) const;
    s_bool           CheckName(const s_str& n, s_str& e) const;
    s_bool           IsExpression(const s_str& s) const;
    s_bool           IsOperator(s_str::const_iterator iter, const s_str& s, s_str& o) const;
    s_bool           IsOperator(const s_str& s) const;
    s_uint           GetOperatorWeight(const s_str& s) const;
    const s_map<s_str, s_uint>& GetOperatorList() const;
    s_refptr<Symbol> BinaryOperator(const s_str& o, s_refptr<Symbol> a, s_refptr<Symbol> b, s_str& e);
    s_refptr<Symbol> LeftUnaryOperator(const s_str& o, s_refptr<Symbol> a, s_str& e);
    s_refptr<Symbol> RightUnaryOperator(const s_str& o, s_refptr<Symbol> a, s_str& e);

    typedef std::function<s_bool(s_str::const_iterator, const s_str&, s_str&)> CutFunction_t;
    s_bool CutExpression(s_ctnr<s_str>& exp, const s_str& sv, CutFunction_t mCutIf, s_str& sError, s_bool bKeepCut = true) const;
    s_bool CutOperators(s_ctnr<s_str>& exp, const s_str& sv, s_str& sError) const;

    template<class T>
    static s_refptr<Symbol> CreateEmptySymbol(const s_str& sName)
    {
        return s_refptr<Symbol>(new T(sName));
    }

    template<class T>
    void RegisterSymbolType()
    {
        lTypeArray[T::TYPE] = &(State::CreateEmptySymbol<T>);
    }

    typedef std::function<s_refptr<Symbol>(State&,s_refptr<Symbol>,s_refptr<Symbol>,s_str&)> BinaryOperator_t;
    template<class T1, class T2>
    void RegisterBinaryOperator(const s_str& o, BinaryOperator_t f)
    {
        lBinaryOperatorArray[o][T1::TYPE][T2::TYPE] = f;
    }

    typedef std::function<s_refptr<Symbol>(State&,s_refptr<Symbol>,s_str&)> UnaryOperator_t;
    template<class T>
    void RegisterLeftUnaryOperator(const s_str& o, UnaryOperator_t f)
    {
        lRightUnaryOperatorArray[o][T::TYPE] = f;
    }

    template<class T>
    void RegisterRightUnaryOperator(const s_str& o, UnaryOperator_t f)
    {
        lRightUnaryOperatorArray[o][T::TYPE] = f;
    }

    typedef std::function<s_bool(const s_str&)> ConstructorMatch_t;
    typedef std::function<s_refptr<Symbol>(State&,const s_str&,const s_str&,s_bool,s_str&)> Constructor_t;
    template<class T>
    void RegisterConstructor(ConstructorMatch_t f1, Constructor_t f2)
    {
        lConstructorMatchArray[T::TYPE] = f1;
        lConstructorArray[T::TYPE] = f2;
    }

    void RegisterOperatorList(const s_map<s_str, s_uint>& lList);

    typedef std::function<s_bool(State&,const s_ctnr<s_refptr<Symbol>>&,s_ctnr<s_refptr<Symbol>>&,s_str&)> Function_t;
    template<typename... Args>
    void RegisterFunction(const s_str& sName, Function_t f)
    {
        RegisterFunction_(sName, "("+Symbol::SerializeTypes<Args...>()+")", f);
    }

private :

    void RegisterFunction_(const s_str& sName, const s_str& sArgs, Function_t f);

    void SetLastResult(s_refptr<Symbol> s);

    s_bool bInteractive;

    s_map<s_str, s_refptr<Symbol>> g;

    s_map<s_str, std::function<s_refptr<Symbol>(const s_str&)>> lTypeArray;
    s_map<s_str, s_map<s_str, s_map<s_str, BinaryOperator_t>>>  lBinaryOperatorArray;
    s_map<s_str, s_map<s_str, UnaryOperator_t>>                 lLeftUnaryOperatorArray;
    s_map<s_str, s_map<s_str, UnaryOperator_t>>                 lRightUnaryOperatorArray;
    s_map<s_str, s_uint>                                        lOperatorList;

    s_map<s_str, ConstructorMatch_t> lConstructorMatchArray;
    s_map<s_str, Constructor_t>      lConstructorArray;

    s_map<s_str, s_map<s_str, Function_t>> lFunctionArray;
};

}

#endif
