/*=============================================================================
    Copyright (c) 2001-2011 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CONJURE_COMPILER_HPP)
#define BOOST_SPIRIT_CONJURE_COMPILER_HPP

#include "ast.hpp"
#include "error_handler.hpp"
#include "vm.hpp"
#include <map>

#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/iterator/transform_iterator.hpp>

#include <llvm/DerivedTypes.h>
#include <llvm/Constants.h>
#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/PassManager.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Analysis/Passes.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Support/IRBuilder.h>

namespace client { namespace code_gen
{
    unsigned const int_size = 32;
    struct compiler;
    struct llvm_compiler;

    ///////////////////////////////////////////////////////////////////////////
    //  The Value (light abstraction of an LLVM::Value)
    ///////////////////////////////////////////////////////////////////////////
    struct value
    {
        value();
        value(value const& rhs);

        value& operator=(value const& rhs);
        bool is_lvalue() const;
        bool is_valid() const;
        operator bool() const;

        value& assign(value const& rhs);

        void name(char const* id);
        void name(std::string const& id);

        friend value operator-(value a);
        friend value operator!(value a);
        friend value operator+(value a, value b);
        friend value operator-(value a, value b);
        friend value operator*(value a, value b);
        friend value operator/(value a, value b);
        friend value operator%(value a, value b);

        friend value operator&(value a, value b);
        friend value operator|(value a, value b);
        friend value operator^(value a, value b);
        friend value operator<<(value a, value b);
        friend value operator>>(value a, value b);

        friend value operator==(value a, value b);
        friend value operator!=(value a, value b);
        friend value operator<(value a, value b);
        friend value operator<=(value a, value b);
        friend value operator>(value a, value b);
        friend value operator>=(value a, value b);

    private:

        struct to_llvm_value;
        friend struct to_llvm_value;
        friend struct llvm_compiler;

        value(
            llvm::Value* v,
            bool is_lvalue_,
            llvm::IRBuilder<>* builder);

        llvm::LLVMContext& context() const
        { return llvm::getGlobalContext(); }

        operator llvm::Value*() const;

        llvm::Value* v;
        bool is_lvalue_;
        llvm::IRBuilder<>* builder;
    };

    ///////////////////////////////////////////////////////////////////////////
    //  The Basic Block (light abstraction of an LLVM::BasicBlock)
    ///////////////////////////////////////////////////////////////////////////
    struct function;

    struct basic_block
    {
        basic_block()
          : b(0) {}

        bool has_terminator() const;
        bool is_valid() const;

    private:

        basic_block(llvm::BasicBlock* b)
          : b(b) {}

        operator llvm::BasicBlock*() const
        { return b; }

        friend struct llvm_compiler;
        friend struct function;
        llvm::BasicBlock* b;
    };

    ///////////////////////////////////////////////////////////////////////////
    //  The Function (light abstraction of an LLVM::Function)
    ///////////////////////////////////////////////////////////////////////////
    struct llvm_compiler;

    struct function
    {
    private:

        struct to_value;
        typedef llvm::Function::arg_iterator arg_iterator;
        typedef boost::transform_iterator<
            to_value, arg_iterator>
        arg_val_iterator;

    public:

        typedef boost::iterator_range<arg_val_iterator> arg_range;

        function()
          : f(0), c(c) {}

        std::string name() const;

        std::size_t arg_size() const;
        arg_range args() const;

        void add(basic_block const& b);
        void erase_from_parent();
        basic_block last_block();
        bool empty() const;

        bool is_valid() const;
        void verify() const;

    private:

        function(llvm::Function* f, llvm_compiler* c)
          : f(f), c(c) {}

        operator llvm::Function*() const;

        friend struct llvm_compiler;
        llvm::Function* f;
        llvm_compiler* c;
    };

    ///////////////////////////////////////////////////////////////////////////
    //  The LLVM Compiler. Lower level compiler (does not deal with ASTs)
    ///////////////////////////////////////////////////////////////////////////
    struct llvm_compiler
    {
        llvm_compiler(vmachine& vm)
          : llvm_builder(context())
          , vm(vm)
          , fpm(vm.module())
        { init_fpm(); }

        value val() { return value(); }
        value val(unsigned int x);
        value val(int x);
        value val(bool x);

        value var(char const* name);
        value var(std::string const& name);

        template <typename Container>
        value call(function callee, Container const& args);

        function get_function(char const* name);
        function get_function(std::string const& name);
        function get_current_function();

        function declare_function(
            bool void_return
          , std::string const& name
          , std::size_t nargs);

        basic_block make_basic_block(
            char const* name
          , function parent = function()
          , basic_block before = basic_block());

        basic_block get_insert_block();
        void set_insert_point(basic_block b);

        void conditional_branch(
            value cond, basic_block true_br, basic_block false_br);
        void branch(basic_block b);

        void return_();
        void return_(value v);

        void optimize_function(function f);

    protected:

        llvm::LLVMContext& context() const
        { return llvm::getGlobalContext(); }

        llvm::IRBuilder<>& builder()
        { return llvm_builder; }

    private:

        friend struct function::to_value;

        value val(llvm::Value* v);

        template <typename C>
        llvm::Value* call_impl(
            function callee,
            C const& args);

        void init_fpm();
        llvm::IRBuilder<> llvm_builder;
        vmachine& vm;
        llvm::FunctionPassManager fpm;
    };

    ///////////////////////////////////////////////////////////////////////////
    //  The main compiler. Generates code from our AST.
    ///////////////////////////////////////////////////////////////////////////
    struct compiler : llvm_compiler
    {
        typedef value result_type;

        template <typename ErrorHandler>
        compiler(vmachine& vm, ErrorHandler& error_handler_)
          : llvm_compiler(vm)
        {
            using namespace boost::phoenix::arg_names;
            namespace phx = boost::phoenix;
            using boost::phoenix::function;

            error_handler = function<ErrorHandler>(error_handler_)(
                "Error! ", _2, phx::cref(error_handler_.iters)[_1]);
        }

        value operator()(ast::nil) { BOOST_ASSERT(0); return val(); }
        value operator()(unsigned int x);
        value operator()(bool x);
        value operator()(ast::primary_expr const& x);
        value operator()(ast::identifier const& x);
        value operator()(ast::unary_expr const& x);
        value operator()(ast::function_call const& x);
        value operator()(ast::expression const& x);
        value operator()(ast::assignment const& x);

        bool operator()(ast::variable_declaration const& x);
        bool operator()(ast::statement_list const& x);
        bool operator()(ast::statement const& x);
        bool operator()(ast::if_statement const& x);
        bool operator()(ast::while_statement const& x);
        bool operator()(ast::return_statement const& x);
        bool operator()(ast::function const& x);
        bool operator()(ast::function_list const& x);

    private:

        value compile_binary_expression(
            value lhs, value rhs, token_ids::type op);

        value compile_expression(
            int min_precedence,
            value lhs,
            std::list<ast::operation>::const_iterator& rest_begin,
            std::list<ast::operation>::const_iterator rest_end);

        struct statement_compiler;
        statement_compiler& as_statement();

        function function_decl(ast::function const& x);
        void function_allocas(ast::function const& x, function function);

        boost::function<
            void(int tag, std::string const& what)>
        error_handler;

        bool void_return;
        std::string current_function_name;
        std::map<std::string, value> locals;
        basic_block return_block;
        value return_var;
    };
}}

#endif
