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

using namespace std;

TypeAnnotation::TypeAnnotation()
{}

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

TypeAnnotation::TypeAnnotation(const TypeOperator &op, const TypeAnnotation &typ)
{}

llvm::Type*
TypeAnnotation::toLLVMType()
{
    switch (__value)
    {
        case Bool:
         return llvm::Type::getInt1Ty(llvm::getGlobalContext());

        case Int:
        case Float:
        case Num:
         return llvm::Type::getDoubleTy(llvm::getGlobalContext());

        default:
            assert(false);
    }

    return NULL;
}



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 &op, std::initializer_list<Expression> params)
    : __state(COMPOUND), __op(op)
{
    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 op)
{
    __op = op;

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

        default:
            __state = COMPOUND;
            break;
    }
}

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

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


AST::AST()
{
}

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

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

    return name;
}

FID
AST::getFunctionsCount() const
{
    return __functions.size();
}

const Function&
AST::getFunctionById(FID id)const
{
    assert(id>=0 && id < __functions.size());
    return __functions[id];
}

void
AST::run(LLVMLayer &l)
{
    llvm::PassManager PM;
    PM.add(llvm::createPrintModulePass(llvm::outs()));
    PM.run(*l.module);
}

Function::Function(const wstring &name)
    :__vCounter(0)
{
    char buffer[1000];
    wcstombs(buffer, name.c_str(), 1000);
    __name = buffer;
}

void
Function::addAnnotation(const wstring& aname, const AnnotationModifier amod)
{
    char buffer[1000];
    wcstombs(buffer, aname.c_str(), 1000);
    std::string name(buffer);

    cout << QString("Function annotation: %1 <- %2").arg(__name.c_str()).arg(name.c_str()).toStdString()<<endl;
    __tags.push_back(std::make_pair(name, amod));
}

const std::vector<std::pair<std::string, AnnotationModifier>>&
Function::getAnnotations() const
{
    return __tags;
}

void
Function::addArg(const wstring &vname, const TypeAnnotation &typ)
{
    char buffer[1000];
    wcstombs(buffer, vname.c_str(), 1000);
    std::string name(buffer);

    VID id = registerVar(name);
    __definitions[id] = typ;
    __args.push_back(name);
}

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

void
Function::setReturnType(const TypeAnnotation &rtyp)
{
    __retType = rtyp;
}

VID
Function::registerVar(const std::string& vname)
{
    __vartable[vname] = ++__vCounter;
    return  __vCounter;
}

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

void
Function::addListDeclaration(const wstring &vname, const TypeAnnotation &typ, const Expression &e)
{}

void
Function::addDeclaration(const wstring &vname, const TypeAnnotation &typ, const Expression &e)
{
    char buffer[1000];
    wcstombs(buffer, vname.c_str(), 1000);

    std::string v(buffer);

    VID id  = registerVar(v);
    __definitions[id] = typ;
    __declarations[id] = e;
}



void
RuleArguments::add(std::string&& name, DomainAnnotation typ)
{
    emplace_back(name, typ);
}

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

void
RuleWarning::compile(ClaspLayer& layer)
{
    layer.addRuleWarning(this);
}


