#include "ast.h"
#include "ExternLayer.h"
#include <stdexcept>
#include <iostream>

namespace std{

    std::size_t
    hash<xreate::ScopedSymbol>::operator()(xreate::ScopedSymbol const& s) const
    {return s.id ^ (s.version << 2);}

    bool
    equal_to<xreate::ScopedSymbol>::operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const
    { return __x.id == __y.id && __x.version == __y.version; }
}

using namespace std;

namespace xreate {

    class ExpressionHints {
    public:

        static bool
        isStringValueValid(const Expression& e) {
            switch (e.__state) {
                case Expression::INVALID:
                case Expression::VARIANT:
                    assert(false);

                case Expression::IDENT:
                case Expression::STRING:
                    return true;

                case Expression::NUMBER:
                case Expression::BINDING:
                    return false;

                case Expression::COMPOUND:
                {
                    switch (e.op) {
                        case Operator::CALL:
                            return true;

                        default: return false;
                    }
                }
            }

            return false;
        }

        static bool
        isDoubleValueValid(const Expression& e) {
            switch (e.__state) {
                case Expression::NUMBER:
                    return true;

                case Expression::INVALID:
                case Expression::VARIANT:
                    assert(false);

                case Expression::IDENT:
                case Expression::STRING:
                case Expression::COMPOUND:
                case Expression::BINDING:
                    return false;
            }

            return false;
        }
    };

    class TypesResolver {
    private:
        const AST* ast;
        std::map<std::string, TypeAnnotation> scope;
        std::map<TypeAnnotation, int> signatures;

        ExpandedType expandType(const TypeAnnotation &t, const std::vector<TypeAnnotation> &args = std::vector<TypeAnnotation>()) {
            return TypesResolver(ast, scope, signatures)(t, args);
        }

        std::vector<TypeAnnotation>
        expandOperands(const std::vector<TypeAnnotation>& operands) {
            std::vector<TypeAnnotation> pack;

            pack.reserve(operands.size());
            std::transform(operands.begin(), operands.end(), std::inserter(pack, pack.end()),
                    [this](const TypeAnnotation & t) {
                        return expandType(t);
                    });

            return pack;
        }

    public:

        TypesResolver(const AST* root, const std::map<std::string, TypeAnnotation>& scopeOuter = std::map<std::string, TypeAnnotation>(),
                std::map<TypeAnnotation, int> signaturesOuter = std::map<TypeAnnotation, int>())
        : ast(root), scope(scopeOuter), signatures(signaturesOuter) {
        }

        ExpandedType
        operator()(const TypeAnnotation &t, const std::vector<TypeAnnotation> &args = std::vector<TypeAnnotation>()) {
            //assert(args.size() == t.bindings.size()); // invalid number of arguments
            for (size_t i = 0; i < args.size(); ++i) {
                scope[t.bindings.at(i)] = args.at(i);
            }

            switch (t.__operator) {
                case TypeOperator::ARRAY:
                {
                    assert(t.__operands.size() == 1);

                    Expanded<TypeAnnotation> elTy = expandType(t.__operands.at(0));
                    return ExpandedType(TypeAnnotation(tag_array, elTy, 0));
                }

                case TypeOperator::STRUCT:
                {
                    assert(t.__operands.size());

                    std::vector<TypeAnnotation>&& pack = expandOperands(t.__operands);
                    auto tnew = TypeAnnotation(TypeOperator::STRUCT, move(pack));
                    tnew.fields = t.fields;

                    return ExpandedType(move(tnew));
                };

                case TypeOperator::CALL:
                {
                    std::string alias = t.__valueCustom;

                    //find in local scope:
                    TypeAnnotation ty;
                    if (scope.count(alias)) {
                        ty = scope.at(alias);

                    } else if (ast->__indexTypeAliases.count(alias)) {
                        ty = ast->__indexTypeAliases.at(alias);

                    } else {
                        assert(false && "Undefined or external type");
                    }

                    std::vector<TypeAnnotation>&& operands = expandOperands(t.__operands);
                    TypeAnnotation signature(TypeOperator::CALL, move(operands));
                    signature.__valueCustom = alias;

                    if (signatures.count(signature)) {
                        auto link = TypeAnnotation(TypeOperator::LINK,{});
                        link.conjuctionId = signatures.at(signature);

                        return ExpandedType(move(link));
                    }

                    int cid = signatures.size();
                    signatures[signature] = cid;
                    TypeAnnotation tyResult = expandType(ty, operands);
                    tyResult.conjuctionId = cid;

                    return ExpandedType(move(tyResult));
                };

                case TypeOperator::CUSTOM:
                {
                    std::string alias = t.__valueCustom;

                    /*
                    if (signatures.count(alias)) {
                        return ExpandedType(TypeAnnotation(TypeOperator::LINK, {t}));
                    }
                    signatures[alias].emplace(t);
                     */

                    //find in local scope:
                    if (scope.count(alias)) {
                        return expandType(scope.at(alias));
                    }

                    // find in general scope:
                    if (ast->__indexTypeAliases.count(alias)) {
                        return expandType(ast->__indexTypeAliases.at(t.__valueCustom));
                    }

                    //if type is unknown keep it as is.
                    return ExpandedType(TypeAnnotation(t));
                };

                case TypeOperator::ACCESS:
                {
                    std::string alias = t.__valueCustom;
                    ExpandedType tyAlias = ExpandedType(TypeAnnotation());

                    //find in local scope:
                    if (scope.count(alias)) {
                        tyAlias = expandType(scope.at(alias));

                        //find in global scope:
                    } else if ((ast->__indexTypeAliases.count(alias))) {
                        tyAlias = expandType(ast->__indexTypeAliases.at(alias));

                    } else {
                        assert(false && "Undefined or external type");
                    }

                    assert(tyAlias->__operator == TypeOperator::STRUCT);

                    for (const string& field : t.fields) {
                        auto fieldIt = std::find(tyAlias->fields.begin(), tyAlias->fields.end(), field);
                        assert(fieldIt != tyAlias->fields.end() && "unknown field");

                        int fieldId = fieldIt - tyAlias->fields.begin();
                        tyAlias = expandType(tyAlias->__operands.at(fieldId));
                    }

                    return tyAlias;
                }

                case TypeOperator::TUPLE:
                {
                    assert(t.__operands.size());

                    std::vector<TypeAnnotation> pack;
                    pack.reserve(t.__operands.size());

                    std::transform(t.__operands.begin(), t.__operands.end(), std::inserter(pack, pack.end()),
                            [this](const TypeAnnotation & t) {
                                return expandType(t);
                            });

                    return ExpandedType(TypeAnnotation(TypeOperator::TUPLE, move(pack)));
                }

                case TypeOperator::VARIANT:
                {
                    return ExpandedType(TypeAnnotation(t));
                }

                case TypeOperator::NONE:
                {
                    return ExpandedType(TypeAnnotation(t));
                }

                default:
                    assert(false);
            }

            assert(false);
            return ExpandedType(TypeAnnotation());
        }
    };

    TypeAnnotation::TypeAnnotation() {
    }

    TypeAnnotation::TypeAnnotation(const Atom<Type_t> &typ)
    : __value(typ.get()) {
        ;
    }

    TypeAnnotation::TypeAnnotation(TypePrimitive typ)
    : __value(typ) {
    }

    TypeAnnotation::TypeAnnotation(TypeOperator op, std::initializer_list<TypeAnnotation> operands)
    : __operator(op), __operands(operands) {

    }

    TypeAnnotation::TypeAnnotation(TypeOperator op, std::vector<TypeAnnotation>&& operands)
    : __operator(op), __operands(operands) {
    }

    TypeAnnotation::TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size)
    : TypeAnnotation(TypeOperator::ARRAY,{typ}) {
        __size = size;
    }

    bool
    TypeAnnotation::operator<(const TypeAnnotation& t) const {
        if (__operator != t.__operator) return __operator < t.__operator;

        if (__operator == TypeOperator::NONE)
            return __value < t.__value;

        if (__operator == TypeOperator::CALL || __operator == TypeOperator::CUSTOM || __operator == TypeOperator::ACCESS) {
            if (__valueCustom != t.__valueCustom)
                return __valueCustom < t.__valueCustom;
        }

        return __operands < t.__operands;
    }

    /*
    TypeAnnotation (struct_tag, std::initializer_list<TypeAnnotation>)
    {}
     */

    void
    TypeAnnotation::addBindings(std::vector<Atom<Identifier_t>>&& params) {
        bindings.reserve(bindings.size() + params.size());

        std::transform(params.begin(), params.end(), std::inserter(bindings, bindings.end()),
                [](const Atom<Identifier_t>& ident) {
                    return ident.get(); });
    }

    void
    TypeAnnotation::addFields(std::vector<Atom<Identifier_t>>&& listFields) {
        fields.reserve(fields.size() + listFields.size());

        std::transform(listFields.begin(), listFields.end(), std::inserter(fields, fields.end()),
                [](const Atom<Identifier_t>& ident) {
                    return ident.get(); });
    }

    unsigned int Expression::nextVacantId = 0;

    Expression::Expression(const Atom<Number_t>& number)
    : Expression() {
        __state=NUMBER; op=Operator::NONE; __valueD=number.get();
    }

    Expression::Expression(const Atom<String_t>& a)
    : Expression(){
        __state=STRING; op=Operator::NONE; __valueS=a.get();
    }

    Expression::Expression(const Atom<Identifier_t> &ident)
    : Expression() {
        __state=IDENT; op=Operator::NONE; __valueS=ident.get();
    }

    Expression::Expression(const Operator &oprt, std::initializer_list<Expression> params)
    : Expression() {
        __state=COMPOUND; op=oprt;

        if (op == Operator::CALL) {
            assert(params.size() > 0);
            Expression arg = *params.begin();

            assert(arg.__state == Expression::IDENT);
            __valueS = std::move(arg.__valueS);

            operands.insert(operands.end(), params.begin() + 1, params.end());
            return;
        }

        operands.insert(operands.end(), params.begin(), params.end());
    }

    void
    Expression::setOp(Operator oprt) {
        op = oprt;

        switch (op) {
            case Operator::NONE:
                __state = INVALID;
                break;

            default:
                __state = COMPOUND;
                break;
        }
    }

    void
    Expression::addArg(Expression &&arg) {
        operands.push_back(arg);
    }

    void
    Expression::addBindings(std::initializer_list<Atom<Identifier_t>> params) {
        addBindings(params.begin(), params.end());
    }

    void
    Expression::bindType(TypeAnnotation t) {
        type = move(t);
    }

    void
    Expression::addBlock(ManagedScpPtr scope) {
        blocks.push_back(scope.operator->());
    }

    const std::vector<Expression>&
    Expression::getOperands() const {
        return operands;
    }

    double
    Expression::getValueDouble() const {
        return __valueD;
    }

    const std::string&
    Expression::getValueString() const {
        return __valueS;
    }

    void
    Expression::setValue(const Atom<Identifier_t>&& v) {
        __valueS = v.get();
    }

    void Expression::setValueDouble(double value) {
        __valueD = value;
    }

    bool
    Expression::isValid() const {
        return (__state != INVALID);
    }

    bool
    Expression::isDefined() const {
        return (__state != BINDING);
    }

    Expression::Expression()
    : __state(INVALID), op(Operator::NONE), id(nextVacantId++)
    {    }

    bool
    Expression::operator==(const Expression& other) const {
        assert(!this->blocks.size());
        assert(!other.blocks.size());

        if (this->__state != other.__state) return false;

        if (ExpressionHints::isStringValueValid(*this)) {
            if (this->__valueS != other.__valueS) return false;
        }

        if (ExpressionHints::isDoubleValueValid(*this)) {
            if (this->__valueD != other.__valueD) return false;
        }

        if (this->__state != Expression::COMPOUND) {
            return true;
        }

        if (this->operands.size() != other.operands.size()) {
            return false;
        }

        for (size_t i = 0; i<this->operands.size(); ++i) {
            if (!(this->operands[i] == other.operands[i])) return false;
        }

        return true;
    }

    AST::AST() {
        Attachments::init<VariableVersion>();
        Attachments::init<Symbol>();
    }

    void
    AST::addInterfaceData(const ASTInterface& interface, Expression&& data) {
        __interfacesData.emplace(interface, move(data));
    }

    void
    AST::addDFAData(Expression &&data) {
        __dfadata.push_back(data);
    }

    void
    AST::addExternData(ExternData &&data) {
        __externdata.insert(__externdata.end(), data.entries.begin(), data.entries.end());
    }

    void
    AST::add(Function* f) {
        __functions.push_back(f);
        __indexFunctions.emplace(f->getName(), __functions.size() - 1);
    }

    void
    AST::add(MetaRuleAbstract *r) {
        __rules.push_back(r);
    }

    void
    AST::add(TypeAnnotation t, Atom<Identifier_t> alias) {
        if (t.__operator == TypeOperator::VARIANT) {
            for (int i = 0, size = t.fields.size(); i < size; ++i) {
                __dictVariants.emplace(t.fields[i], make_pair(t, i));
            }
        }

        __indexTypeAliases.emplace(alias.get(), move(t));
    }

    ManagedScpPtr
    AST::add(CodeScope* scope) {
        this->__scopes.push_back(scope);
        return ManagedScpPtr(this->__scopes.size() - 1, &this->__scopes);
    }

    std::string
    AST::getModuleName() {
        const std::string name = "moduleTest";

        return name;
    }

    ManagedPtr<Function>
    AST::findFunction(const std::string& name) {
        int count = __indexFunctions.count(name);
        if (!count) {
            return ManagedFnPtr::Invalid();
        }

        assert(count == 1);

        auto range = __indexFunctions.equal_range(name);
        return ManagedPtr<Function>(range.first->second, &this->__functions);
    }

    std::list<ManagedFnPtr>
    AST::getAllFunctions() const {
        const size_t size = __functions.size();

        std::list<ManagedFnPtr> result;
        for (size_t i = 0; i < size; ++i) {
            result.push_back(ManagedFnPtr(i, &this->__functions));
        }

        return result;
    }

    //TASK select default  specializations

    std::list<ManagedFnPtr>
    AST::getFunctionVariants(const std::string& name) const {
        auto functions = __indexFunctions.equal_range(name);

        std::list<ManagedFnPtr> result;
        std::transform(functions.first, functions.second, inserter(result, result.end()),
                [this](auto f) {
                    return ManagedFnPtr(f.second, &this->__functions);
                });

        return result;
    }

    template<>
    ManagedPtr<Function>
    AST::begin<Function>() {
        return ManagedPtr<Function>(0, &this->__functions);
    }

    template<>
    ManagedPtr<CodeScope>
    AST::begin<CodeScope>() {
        return ManagedPtr<CodeScope>(0, &this->__scopes);
    }

    template<>
    ManagedPtr<MetaRuleAbstract>
    AST::begin<MetaRuleAbstract>() {
        return ManagedPtr<MetaRuleAbstract>(0, &this->__rules);
    }

    Expanded<TypeAnnotation>
    AST::expandType(const TypeAnnotation &t) const {
        return TypesResolver(this)(t);
    }

    Expanded<TypeAnnotation>
    AST::findType(const std::string& name) {
        // find in general scope:
        if (__indexTypeAliases.count(name))
            return expandType(__indexTypeAliases.at(name));

        //if type is unknown keep it as is.
        TypeAnnotation t(TypeOperator::CUSTOM,{});
        t.__valueCustom = name;
        return ExpandedType(move(t));
    }

    void
    AST::recognizeVariantIdentifier(Expression& identifier) {

        //        *   move to codescope
        //        *   register var as alias to

        assert(identifier.__state == Expression::IDENT);

        std::string name = identifier.getValueString();
        if (__dictVariants.count(name)) {
            auto record = __dictVariants.at(name);
            const TypeAnnotation& typ = record.first;

            identifier.__state = Expression::VARIANT;
            identifier.setValueDouble(record.second);
            identifier.type = typ;
        }
    }

    Function::Function(const Atom<Identifier_t>& name)
    : __entry(new CodeScope(0)) {
        __name = name.get();
    }

    void
    Function::addTag(Expression&& tag, const TagModifier mod) {
        string name = tag.getValueString();
        __tags.emplace(move(name), move(tag));
    }

    const std::map<std::string, Expression>&
    Function::getTags() const {
        return __tags;
    }

    CodeScope*
    Function::getEntryScope() const {
        return __entry;
    }

    void
    Function::addBinding(Atom <Identifier_t>&& name, Expression&& argument) {
        __entry->addBinding(move(name), move(argument));
    }

    const std::string&
    Function::getName() const {
        return __name;
    }

    ScopedSymbol
    CodeScope::registerIdentifier(const Expression& identifier) {
        VariableVersion version = Attachments::get<VariableVersion>(identifier, VERSION_NONE);

        auto result = __identifiers.emplace(identifier.getValueString(), __vCounter);
        if (result.second){
            ++__vCounter;
            return {__vCounter-1, version};
        }

        return {result.first->second, version};
    }

    bool
    CodeScope::recognizeIdentifier(const Expression& identifier) const{
        VariableVersion version = Attachments::get<VariableVersion>(identifier, VERSION_NONE);
        const std::string& name = identifier.getValueString();

        //search identifier in the current block
        if (__identifiers.count(name)){
            VNameId id = __identifiers.at(name);

            Symbol s;
            s.identifier = ScopedSymbol{id, version};
            s.scope = const_cast<CodeScope*>(this);
            Attachments::put<Symbol>(s);

            return true;
        }

        //search in the parent scope
        if (__parent)
        {
            return __parent->recognizeIdentifier(identifier);
        }

        return false;
    }

    void
    AST::postponeIdentifier(CodeScope* scope, const Expression& id) {
        binUnrecognizedIdentifiers.emplace(scope, id);
    }

    void
    AST::recognizePostponedIdentifiers() {
        for(const auto& identifier: binUnrecognizedIdentifiers){
            if (!identifier.first->recognizeIdentifier(identifier.second)){
                            //exception: Ident not found
                std::cout << "Unknown symbol: "<< identifier.second.getValueString() << std::endl;
                assert(false && "Symbol not found");
            }
        }
    }

    void
    CodeScope::addBinding(Expression&& var, Expression&& argument) {
        argument.__state = Expression::BINDING;

        __bindings.push_back(var.getValueString());
        ScopedSymbol binding = registerIdentifier(var);
        __declarations[binding] = move(argument);
    }

    void
    CodeScope::addDeclaration(Expression&& var, Expression&& body) {
        ScopedSymbol s = registerIdentifier(var);
        __declarations[s] = move(body);
    }

    CodeScope::CodeScope(CodeScope* parent)
    : __parent(parent) {
    }

    CodeScope::~CodeScope() {
    }

    void
    CodeScope::setBody(const Expression &body) {
        __declarations[ScopedSymbol::RetSymbol] = body;
    }

    Expression&
    CodeScope::getBody() {
        return __declarations[ScopedSymbol::RetSymbol];
    }

    const Expression&
    CodeScope::findDeclaration(const Symbol& symbol) {
        CodeScope* self = symbol.scope;
        return self->__declarations.at(symbol.identifier);
    }

    void
    RuleArguments::add(const Atom<Identifier_t> &arg, DomainAnnotation typ) {
        emplace_back(arg.get(), typ);
    }

    void
    RuleGuards::add(Expression&& e) {
        push_back(e);
    }

    MetaRuleAbstract::
    MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards)
    : __args(std::move(args)), __guards(std::move(guards)) {
    }

    MetaRuleAbstract::~MetaRuleAbstract() {
    }

    RuleWarning::
    RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom<String_t>&& message)
    : MetaRuleAbstract(std::move(args), std::move(guards)), __message(message.get()), __condition(condition) {
    }

    RuleWarning::~RuleWarning() {
    }

    void
    RuleWarning::compile(ClaspLayer& layer) {
        //TODO restore addRuleWarning
        //layer.addRuleWarning(*this);
    }

    bool operator<(const Symbol& s1, const Symbol& s2) {
        return (s1.scope < s2.scope) || (s1.scope == s2.scope && s1.identifier < s2.identifier);
    }

    bool operator==(const Symbol& s1, const Symbol& s2) {
        return (s1.scope == s2.scope) && (s1.identifier == s2.identifier);
    }

    bool operator<(const Expression&a, const Expression&b) {
        if (a.__state != b.__state) return a.__state < b.__state;
        assert(a.__state != Expression::INVALID);
        switch (a.__state) {
            case Expression::IDENT:
            case Expression::STRING:
            case Expression::VARIANT:
                return a.getValueString() < b.getValueString();

            case Expression::NUMBER:
                return a.getValueDouble() < b.getValueDouble();

            case Expression::COMPOUND:
            {
                assert(a.op == Operator::CALL);
                assert(a.blocks.size() == 0);
                assert(b.blocks.size() == 0);

                if (a.operands.size() != b.operands.size()) {
                    return (a.operands.size() < b.operands.size());
                }

                if (a.getValueString() != b.getValueString()) {
                    return a.getValueString() < b.getValueString();
                }

                for (size_t i = 0; i < a.operands.size(); ++i) {
                    bool result = a.operands[i] < b.operands[i];
                    if (result) return true;
                }

                return false;
            }

            case Expression::BINDING:
            case Expression::INVALID:
                assert(false);
        }

        return false;
    }

    template<>
    struct AttachmentsStorage<Symbol> {

        static Attachments*
        get(const Symbol& s) {
            return &s.scope->findDeclaration(s).tagsInternal;
        }
    };

    template<>
    struct AttachmentsStorage<Expression> {

        static Attachments*
        get(const Expression& e) {
            return &e.tagsInternal;
        }
    };

}



