/* 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/.
 *
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * containers.cpp
 * Created on 3/14/15.
 */

/**
 * \file    query/containers.h
 * \brief   Represents reasoner's solution on [Container implementations](/w/concepts/containers)
 */

#include <transcendlayer.h>
#include "query/containers.h"

using namespace std;
using namespace xreate::containers;
using namespace xreate;

Implementation
Query::queryImplementation(xreate::Symbol const &s) {
  if (Attachments::exists<Implementation>(s)) {
    return Attachments::get<Implementation>(s);
  }

  return  Implementation::create(s);
}

Query::Query() {
  Attachments::init<Implementation>();
}

void
Query::init(TranscendLayer* transcend) {
  if (flagDataIsLoaded) return;

  //Fill implementation data for a data sources:
  auto range = transcend->query(Config::get("containers.id.implementations"));
  if (range.size())
    for(auto atom : range) {
      auto data = TranscendLayer::parse<SymbolPacked, Gringo::Symbol>(atom.second);

      Symbol var  = transcend->unpack(get<0>(data));
      string implStr  = get<1>(data).name().c_str();

      if (implStr == Config::get("containers.impl.solid")) {
        auto size = TranscendLayer::parse<int>(get<1>(data));
        Attachments::put<Implementation>(var,{SOLID, ImplementationRec<SOLID>
          {get<0>(size)}});

      } else if (implStr == Config::get("containers.impl.onthefly")) {
        Attachments::put<Implementation>(var,{ON_THE_FLY, ImplementationRec<ON_THE_FLY>
          {var}});

      } else {
        assert(false && "Unable to determine proper implementation for the symbol");
      }
    }

  flagDataIsLoaded = true;
}

Implementation
Implementation::create(const Symbol &var) {
  //TODO review implementation determination strategy
  Expression varDecl = CodeScope::getDefinition(var);
  switch (varDecl.op) {
  case Operator::LIST_RANGE:
  {
    ImplementationRec<ON_THE_FLY> rec{var};
    return {ON_THE_FLY, rec};
  }

  case Operator::LIST:
  {
    return {SOLID, ImplementationRec<SOLID> {varDecl.getOperands().size()}};
  }

  default: break;
  };

  ImplementationLinkedList ill(var);
  if (ill) {
    return ill.getImplementationData();
  }

  assert(false && "Unable to determine proper implementation for the symbol");
  return Implementation();
}

ImplementationLinkedList::ImplementationLinkedList(const Symbol& source)
:    flagIsValid(false), s(source) {

  const Expression& sourceExpr = CodeScope::getDefinition(source);

  if (sourceExpr.tags.count(Config::get("containers.id.linkedlist"))) {
    flagIsValid = true;

    Expression tagLinkedlist = sourceExpr.tags.at(Config::get("containers.id.linkedlist"));
    assert(tagLinkedlist.operands.size() == 2);
    fieldPointer = tagLinkedlist.operands.at(0).getValueString();
    terminator = tagLinkedlist.operands.at(1);
  }
}

ImplementationLinkedList::operator bool () const {
  return flagIsValid;
}

Implementation
ImplementationLinkedList::getImplementationData() const {
  return {ON_THE_FLY, ImplementationRec<ON_THE_FLY>{s}};
}
