/* 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:   targetabstract.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on July 2, 2016, 1:25 PM
 */

/**
 * \file    
 * \brief   Compilation targets infrastructure 
 */


#ifndef TARGETABSTRACT_H
#define TARGETABSTRACT_H

class Function;

#include "ast.h"
#include "pass/ipass.h"
//#include "targetinterpretation.h"
#include <boost/optional.hpp>
#include <map>
#include <list>

namespace xreate{
namespace compilation{

template <typename ConcreteTargetTag>
struct TargetInfo{
//public:
//  typedef Result
//  typedef Function
//  typedef Scope
};

template<typename ConcreteTarget>
class Function;

template<typename ConcreteTarget>
class Target;

template<typename ConcreteTargetTag>
class Scope{
  typedef typename TargetInfo<ConcreteTargetTag>::Scope Self;

public:
  const CodeScope* scope;
  Function<ConcreteTargetTag>* function = 0;

  typename TargetInfo<ConcreteTargetTag>::Result
  processSymbol(const Symbol& s){
    const CodeScope* scope = s.scope;
    Self* self = function->getScope(scope);

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

    const Expression& declaration = CodeScope::getDefinition(s, true);
    if(!declaration.isDefined()){
      assert(false); //for bindings there should be result already
    }

    return self->__bindings[s.identifier] = self->process(declaration);
  }

  typename TargetInfo<ConcreteTargetTag>::Result
  processScope(){
    return process(scope->getBody());
  }

  virtual typename TargetInfo<ConcreteTargetTag>::Result
  process(const Expression& expression) = 0;

  Scope(const CodeScope* codeScope, Function<ConcreteTargetTag>* f)
  : scope(codeScope), function(f){ }

  virtual
  ~Scope(){ }

  void
  overrideBindings(std::list<std::pair<typename TargetInfo<ConcreteTargetTag>::Result, std::string>> bindings){
    std::list < std::pair<typename TargetInfo<ConcreteTargetTag>::Result, ScopedSymbol>> bindingsSymbols;

    for(auto entry: bindings){
      assert(scope->__identifiers.count(entry.second));
      ScopedSymbol id{scope->__identifiers.at(entry.second), versions::VERSION_NONE};
      bindingsSymbols.push_back(std::make_pair(entry.first, id));
    }

    overrideBindings(bindingsSymbols);
  }

  void
  overrideBindings(std::list<std::pair<typename TargetInfo<ConcreteTargetTag>::Result, ScopedSymbol>> bindings){
    reset();

    for(auto entry: bindings){
      __bindings[entry.second] = entry.first;
    }
  }

  bool
  isBindingDefined(const ScopedSymbol& id){
    return __bindings.count(id);
  }

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

  void
  reset(){
    __bindings.clear();
    __childScopes.clear();
  }

protected:
  std::map<ScopedSymbol, typename TargetInfo<ConcreteTargetTag>::Result> __bindings;
  std::list<std::shared_ptr<Self>> __childScopes;
};

template<typename ConcreteTargetTag>
class Function{
  typedef typename TargetInfo<ConcreteTargetTag>::Result Result;
  typedef typename TargetInfo<ConcreteTargetTag>::Scope ConcreteScope;

public:
  Function(const ManagedFnPtr& function, Target<ConcreteTargetTag>* target)
    : __pass(target), __function(function){ }
  virtual  ~Function(){ };
  virtual Result process(const std::vector<Result>& args) = 0;

  ConcreteScope*
  getScope(const CodeScope * const scope){
    if(__scopes.count(scope)){
      auto result = __scopes.at(scope).lock();

      if(result){
        return result.get();
      }
    }

    std::shared_ptr<ConcreteScope> unit(new ConcreteScope(scope, this));

    if(scope->__parent != nullptr){
      getScope(scope->__parent)->registerChildScope(unit);

    } else{
      assert(!__entryScope);
      __entryScope = unit;
    }

    if(!__scopes.emplace(scope, unit).second){
      __scopes[scope] = unit;
    }

    return unit.get();
  }

  Target<ConcreteTargetTag>* __pass = 0;

protected:

  ManagedFnPtr __function;
  std::map<const CodeScope*, std::weak_ptr<ConcreteScope>> __scopes;
  std::shared_ptr<ConcreteScope> __entryScope;
};

/** \brief Similar to xreate::IPass */
template<typename ConcreteTargetTag>
class Target: public IPass{
  typedef typename TargetInfo<ConcreteTargetTag>::Function ConcreteFunction;

public:
  Target(PassManager *manager): IPass(manager){ }
  virtual ~Target(){
    for(const auto& entry: __functions){
      delete entry.second;
    }
  }
  virtual void run() override {}

  ConcreteFunction*  getFunction(const ManagedFnPtr& function){
    unsigned int id = function.id();

    if(!__functions.count(id)){
      ConcreteFunction* unit = new ConcreteFunction(function, this);
      __functions.emplace(id, unit);
      return unit;
    }

    return __functions.at(id);
  }

protected:
  std::map<unsigned int, ConcreteFunction*> __functions;
};

}
}

#endif /* TARGETABSTRACT_H */
