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

using namespace std;

namespace xreate{

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 (llvm_array_tag, TypePrimitive typ, int size)
    :TypeAnnotation(TypeOperator::LIST, {typ})
{
    __size=size;
}
/*
TypeAnnotation (struct_tag, std::initializer_list<TypeAnnotation>)
{}
*/

llvm::Type*
TypeAnnotation::toLLVMType()
{
    switch (__operator)
    {
        case TypeOperator::LIST:
        {
            assert(__operands.size());
            TypeAnnotation elTy = __operands.at(0);

            return llvm::ArrayType::get(elTy.toLLVMType(), __size);
        }

        case TypeOperator::NONE: {
            switch (__value) {
                case TypePrimitive::Bool:
                    return llvm::Type::getInt1Ty(llvm::getGlobalContext());

                case TypePrimitive::Int:
                case TypePrimitive::i32:
                case TypePrimitive::Num:
                    return llvm::Type::getInt32Ty(llvm::getGlobalContext());

                case TypePrimitive::Float:
                    return llvm::Type::getDoubleTy(llvm::getGlobalContext());

                default:
                    assert(false);
            }
        }

        default:
            assert(false);
    }

    assert(false);
    return  nullptr;
}



Expression::Expression(const Atom<Number_t>& number)
: __state(NUMBER), op(Operator::NONE), __valueD(number.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);
        return;
    }

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

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)
{
    std::transform(params.begin(), params.end(), std::inserter(bindings, bindings.end()),
        [] (const Atom<Identifier_t> atom){
                return atom.get();
        });
}

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

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

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

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




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


AST::AST()
{
}

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

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

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

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)
{
    assert (__indexFunctions.count(name));
    return ManagedPtr<Function>(__indexFunctions.at(name), &__functions);
}

void
AST::run(LLVMLayer &l)
{
    llvm::PassManager<llvm::Module> PM;
    PM.addPass(llvm::PrintModulePass(llvm::outs(), "banner"));
    PM.run(*l.module);
}

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

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

void
Function::addTag(Expression&& tag, const TagModifier mod)
{
    __tags.emplace_back(tag, mod);
}

const std::vector<Tag>&
Function::getAnnotations() const
{
    return __tags;
}

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

void
CodeScope::addArg(Atom <Identifier_t>&& name, TypeAnnotation&& typ)
{
    VID id = registerVar(std::move(const_cast<std::string&>(name.get())), std::move(typ));
    __args.push_back(name.get());
}

     ;

void
Function::addArg(Atom <Identifier_t>&& name, TypeAnnotation&& typ)
{
    __entry->addArg(move(name), move(typ));
}

void
Function::setReturnType(const TypeAnnotation &rtyp)
{
    __entry->__definitions[0] = rtyp;
}

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

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

CodeScope::~CodeScope()
{}

VID
CodeScope::registerVar(std::string&& name, TypeAnnotation &&typ)
{
    __vartable[name] = ++__vCounter;
    __definitions[__vCounter] = typ;

    return  __vCounter;
}

void
CodeScope::bindArg(llvm::Value* var, std::string&& name)
{
    assert(__vartable.count(name));
    VID id = __vartable.at(name);
    __rawVars[id] = var;
}

void
CodeScope::addDeclaration(const Atom <Identifier_t> &&name, TypeAnnotation &&typ, Expression&& body)
{
    VID id  = registerVar(std::move(const_cast<string&>(name.get())), move(typ));
    __declarations[id] = body;
}

void
CodeScope::setBody(const Expression &body)
{
    __body = body;
}

TypeAnnotation&
CodeScope::findDefinition(const Symbol& symbol)
{
    CodeScope* self = symbol.scope;
    self->__definitions[symbol.identifier];
}

Symbol
CodeScope::findSymbol(const std::string &name, LLVMLayer &l, bool forceCompile)
{
        //search var in current block
    if (__vartable.count(name))
    {
        VID vId = __vartable.at(name);
        Symbol result{vId, this};

        if (!forceCompile)
        {
            return result;
        }

            //search in already compiled vars
        if (__rawVars.count(vId))
        {
            return result;
        }

        if (!__declarations.count(vId)) {
            //error: symbol is uncompiled scope arg
            assert(false);
        }

        const Expression& e = __declarations.at(vId);

        __rawVars[vId] = compileExpression(e, l, name);
        return result;
    }

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

    //exception: Ident not found
    assert(false);
}

bool
CodeScope:: hasDeclaration(const Symbol& symbol)
{
    CodeScope* self = symbol.scope;
    return (self->__declarations.count(symbol.identifier));
}

llvm::Value*
CodeScope::compileExpression(const Symbol& s, LLVMLayer& l, std::string hintRetVar)
{
    CodeScope* self = s.scope;

    if (self->__rawVars.count(s.identifier))
    {
        return self->__rawVars[s.identifier];
    }

    return self->__rawVars[s.identifier] = self->compileExpression(findDeclaration(s), l, hintRetVar);
}

const Expression&
CodeScope::findDeclaration(const Symbol& symbol)
{
    CodeScope* self = symbol.scope;

    if (! self->__declarations.count(symbol.identifier))
    {
            // no declaration exists
        assert(false);
    }

    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)), __condition(condition), __message(message.get())
{}

RuleWarning::~RuleWarning(){}

void
RuleWarning::compile(ClaspLayer& layer)
{
    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);
}
}




