#include <stdexcept>
#include <regex>

#include "LayerStackBuilder.hh"

#include "loggers.hh"

LayerStackBuilder * LayerStackBuilder::_instance = NULL;

// static functions
LayerStackBuilder * LayerStackBuilder::GetInstance()
{
  return _instance ? _instance : _instance = new LayerStackBuilder();
}

void LayerStackBuilder::RegisterLayerFactory(const std::string & type, LayerFactory * f)
{
  LayerStackBuilder::GetInstance()->registerLayerFactory(type, f);
}

// member functions
LayerStackBuilder::LayerStackBuilder()
{
}

void LayerStackBuilder::registerLayerFactory(const std::string & type, LayerFactory * f)
{
  _fs[type] = f;
}

Layer* LayerStackBuilder::createLayerStack(const char* s)
{
  loggers::get_instance().log(">>> LayerStackBuilder::createLayerStack: %s", s);

  Layer * entry = NULL; // Initial layer (the first declared)
  Layer * up = NULL; // Upper layer
  // Parse str
  try {
    std::regex rgx ("(\\w+)(\\((.*?)\\))?(\\/|$)");
    std::string str = s;
    std::sregex_iterator begin(str.cbegin(), str.cend(), rgx);
    std::sregex_iterator end = std::sregex_iterator();
    for (std::sregex_iterator it = begin; it != end; ++it) {
      std::smatch m = *it;
      loggers::get_instance().log("LayerStackBuilder::createLayerStack: %d - %s - %s - %s - %s", m.size(), m[0].str().c_str(), m[1].str().c_str(), m[2].str().c_str(), m[3].str().c_str());
      LayerFactoryMap::iterator i = _fs.find(m[1].str());
      if (i == _fs.end()) {
	loggers::get_instance().error("LayerStackBuilder::createLayerStack: %s: Unknown layer type", m[1].str().c_str());
      }
      loggers::get_instance().log("LayerStackBuilder::createLayerStack: Create layer %s, %s", m[1].str().c_str(), m[3].str().c_str());
      Layer * l = i->second->createLayer(m[1].str(), m[3].str());
      if (NULL == l) {
	loggers::get_instance().error("LayerStackBuilder::createLayerStack: %s: Layer creation error", m[1].str().c_str());
      }

      loggers::get_instance().log("LayerStackBuilder::createLayerStack: Setup layers for %s", l->to_string().c_str());
      l->addUpperLayer(up);
      if (entry == NULL) { // Set the first declared layer
	entry = l;
      }
      up = l; // Build the linjed list of layers
    } // End of 'for' statement
  }
  catch(const std::logic_error& e){
    if(up){ // FIXME To be reviewed
      up->deleteLayer();
      up = NULL;
    }
  }
  return entry;
}
