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

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:
                    case Operator::INDEX:
                        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(); });
}

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

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

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

Expression::Expression(const Operator &oprt, std::initializer_list<Expression> params)
    : __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)
{}

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()
{
}


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){

//        TODO get rid of findSymbol. Determine symbol while AST parsing. Re-find symbols not found while first pass.
//        *   move to codescope
//        *   use findSymbol to find Symbol
//        *   register var as alias to
//        *   ident __doubleValue holds VID of an alias

	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));
}

void
Function::setReturnType(const TypeAnnotation &rtyp)
{

    __entry->__declarations[0].type = rtyp;
}

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

Symbol
CodeScope::registerIdentifier(Atom <Identifier_t> &&name)
{
    __identifiers.emplace(name.get(), ++__vCounter);
    return {__vCounter, this};
}

void
CodeScope::addBinding(Atom <Identifier_t>&& name, Expression&& argument)
{
    __bindings.push_back(name.get());
    Symbol binding = registerIdentifier(move(name));
    argument.__state = Expression::BINDING;
    __declarations[binding.identifier] = move(argument);
}

void
CodeScope::addDeclaration(Atom <Identifier_t>&& name, Expression&& body)
{
    Symbol s = registerIdentifier(move(name));
    __declarations[s.identifier] = move(body);
}

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

CodeScope::~CodeScope()
{}

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

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

Symbol
CodeScope::findSymbol(const std::string &name)
{
        //search identifier in the current block
    if (__identifiers.count(name))
    {
        VID vId = __identifiers.at(name);
        Symbol result{vId, this};
        return result;
    }

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

    //exception: Ident not found
    std::cout << "Unknown symbol: "<< name << std::endl;
    assert(false && "Symbol not found");
}

const Expression&
CodeScope::findDeclaration(const Symbol& symbol)
{
    CodeScope* self = symbol.scope;
    return self->__declarations[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;
}

}


