/* 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) override{
      __rawVars[s] = value;
  }

  void
  bindExternalSymb(llvm::Value* value, const Symbol& s) override {
    __rawExternals[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{
    if (Parent::function->isLambda){
        SELF* self = dynamic_cast<SELF*>(Parent::function->getEntry());
        if (self->__rawExternals.count(s)){
          return self->__rawExternals.at(s);
        }
    }

    const CodeScope* scope = s.scope;
    SELF* self = dynamic_cast<SELF*>(Parent::function->getBruteScope(scope));

    if (self->__rawVars.count(s.identifier)){
        return self->__rawVars.at(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();
    __rawExternals.clear();
  }

private:
  std::unordered_map<ScopedSymbol, Expression> __declarationsOverriden;
  std::unordered_map<ScopedSymbol,llvm::Value*> __rawVars;
  std::unordered_map<Symbol,llvm::Value*> __rawExternals;
  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 */

