/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * 
 * File:   scopedecorators.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on February 24, 2017, 11:35 AM
 */

/**
 * \file    scopedecorators.h
 * \brief   Basic code block compilation xreate::compilation::IBruteScope  decorators
 */


#ifndef SCOPEDECORATORS_H
#define SCOPEDECORATORS_H

#include "ast.h"
#include "compilation/transformations.h"
#include "analysis/typeinference.h"
#include "compilation/demand.h"
#include "compilation/polymorph.h"
#include "compilation/targetinterpretation.h"

#ifndef XREATE_CONFIG_MIN
  #include "compilation/versions.h"
  #include "compilation/polymorph.h"
#endif

#include <list>

namespace xreate {

class CompilePass;

namespace compilation {

class IBruteScope;
class IBruteFunction;

/**\brief Provides caching ability for code scope compilation
 * \extends xreate::compilation::IBruteScope
 */
template<class Parent>
class CachedScopeDecorator: public Parent{
    typedef CachedScopeDecorator<Parent> SELF;

public:
    CachedScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){}

    Symbol bindArg(llvm::Value* value, std::string&& alias)
    {
        //ensure existence of an alias
        assert(Parent::scope->__identifiers.count(alias));

        //memorize new value for an alias
        ScopedSymbol id{Parent::scope->__identifiers.at(alias), versions::VERSION_NONE};
        __rawVars[id] = value;

        return Symbol{id, Parent::scope};
    }

    void bindArg(llvm::Value* value, const ScopedSymbol& s) {
        __rawVars[s] = value;
    }

    llvm::Value* compile(const std::string& aliasBlock="") override{
        if (__rawVars.count(ScopedSymbol::RetSymbol)){
            return __rawVars[ScopedSymbol::RetSymbol];
        }

        return Parent::compile(aliasBlock);
    }

    llvm::Value*
    processSymbol(const Symbol& s, std::string hintRetVar) override{
        const CodeScope* scope = s.scope;
        SELF* self = dynamic_cast<SELF*>(Parent::function->getBruteScope(scope));

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

        //Declaration could be overriden
        /*
        Expression declaration = CodeScope::getDefinition(s, true);
        if (!declaration.isDefined()){
            assert(__declarationsOverriden.count(s.identifier));
                declaration = __declarationsOverriden[s.identifier];

            } else {
                (false); //in case of binding there should be raws provided.
            }
        }
        */

        llvm::Value* resultRaw = Parent::processSymbol(s, hintRetVar);
        self->__rawVars.emplace(s.identifier, resultRaw);
        return resultRaw;
    }

    void
    overrideDeclarations(std::list<std::pair<Symbol, Expression>> bindings){
        reset();

        for (auto entry: bindings){
            SELF* self = dynamic_cast<SELF*>(Parent::function->getBruteScope(entry.first.scope));
            assert(self == this);

            self->__declarationsOverriden.emplace(entry.first.identifier, entry.second);
        }
    }

    void registerChildScope(std::shared_ptr<IBruteScope> scope){
        __childScopes.push_back(scope);
    }

    void reset(){
        __rawVars.clear();
        __declarationsOverriden.clear();
        __childScopes.clear();
    }

private:
    std::unordered_map<ScopedSymbol, Expression> __declarationsOverriden;
    std::unordered_map<ScopedSymbol,llvm::Value*> __rawVars;
    std::list<std::shared_ptr<IBruteScope>> __childScopes;
};

/** \brief Provides automatic type conversion
 *  \extends xreate::compilation::IBruteScope
 */
template<class Parent>
class TypeConversionScopeDecorator: public Parent {
public:
    TypeConversionScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){}

    llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="", const TypeAnnotation& expectedT = TypeAnnotation()) override {
        llvm::Value* resultR = Parent::process(expr, hintVarDecl, expectedT);

        if(!expr.type.isValid()) {
          return resultR;
        }

        ExpandedType exprT = Parent::pass->man->root->getType(expr);
        llvm::Type* exprTR = Parent::pass->man->llvm->toLLVMType(exprT, expr);
        return typeinference::doAutomaticTypeConversion(resultR, exprTR, Parent::pass->man->llvm->irBuilder);
    }
};

#ifndef XREATE_CONFIG_MIN
/**\brief The default code scope compilation implementation
 * \extends xreate::compilation::IBruteScope
 */
typedef
  CachedScopeDecorator<
    TypeConversionScopeDecorator<
      latex::LatexBruteScopeDecorator<
        polymorph::PolymorphBruteScopeDecorator<
          compilation::TransformationsScopeDecorator<
            interpretation::InterpretationScopeDecorator<
              versions::VersionsScopeDecorator<
                compilation::BasicBruteScope
  >>>>>>>
    DefaultCodeScopeUnit;

  } //end of compilation namespace

  struct CachedScopeDecoratorTag;
  struct VersionsScopeDecoratorTag;

  template<>
  struct DecoratorsDict<CachedScopeDecoratorTag>{
      typedef compilation::CachedScopeDecorator<
              compilation::TypeConversionScopeDecorator<
              latex::LatexBruteScopeDecorator<
              polymorph::PolymorphBruteScopeDecorator<
              compilation::TransformationsScopeDecorator<
              interpretation::InterpretationScopeDecorator<
              versions::VersionsScopeDecorator<
              compilation::BasicBruteScope
  >>>>>>>

    result;
  };

  template<>
  struct DecoratorsDict<VersionsScopeDecoratorTag>{
    typedef
      versions::VersionsScopeDecorator<
        compilation::BasicBruteScope
      >
    result;
  };

#else
  /**\brief The default code scope compilation implementation
   * \extends xreate::compilation::IBruteScope
   */
  typedef
    CachedScopeDecorator<
    TypeConversionScopeDecorator<
    interpretation::InterpretationScopeDecorator<
    demand::DemandBruteScopeDecorator<
    polymorph::PolymorphBruteScopeDecorator<
    compilation::BasicBruteScope
  >>>>>
    DefaultCodeScopeUnit;

  } //end of compilation namespacef

  struct CachedScopeDecoratorTag;

  template<>
  struct DecoratorsDict<CachedScopeDecoratorTag>{
    typedef compilation::CachedScopeDecorator<
    compilation::TypeConversionScopeDecorator<
    interpretation::InterpretationScopeDecorator<
    demand::DemandBruteScopeDecorator<
    polymorph::PolymorphBruteScopeDecorator<
    compilation::BasicBruteScope
    >>>>>
      result;
  };

  typedef
    demand::DemandBruteFnDecorator<
    //polymorph::PolymorphBruteFnDecorator<
    compilation::BasicBruteFunction
  >   BruteFunctionDefault;
#endif
} //end of xreate

#endif /* SCOPEDECORATORS_H */

