#include "utilita.h"
#include "poset.h"

// ***********************************************
// ***********************************************
// ***********************************************

std::shared_ptr<LexicographicProductPOSet> LexicographicProductPOSet::Build(std::shared_ptr<POSet> p1, std::shared_ptr<POSet> p2) {
  std::shared_ptr<LexicographicProductPOSet> result(new LexicographicProductPOSet());

  std::uint_fast64_t elements_size = p1->size() * p2->size();

  std::vector<std::uint_fast64_t> selezionato(2, 0);
  std::vector<std::vector<std::uint_fast64_t>> elements(elements_size, selezionato);

  if (instanceof<FromPOSets>(&(*p1))) {
    auto pp1 = dynamic_cast<const FromPOSets*>(&(*p1));
    result->posets.insert(result->posets.end(), pp1->posets.begin(), pp1->posets.end());
  } else {
    result->posets.push_back(p1);
  }
  if (instanceof<FromPOSets>(&(*p2))) {
    auto pp2 = dynamic_cast<const FromPOSets*>(&(*p2));
    result->posets.insert(result->posets.end(), pp2->posets.begin(), pp2->posets.end());
  } else {
    result->posets.push_back(p2);
  }

  auto buildString = [&](std::vector<std::uint_fast64_t>& s) {
    auto v1 = p1->GetEName(s.at(0));
    auto v2 = p2->GetEName(s.at(1));
    std::string results = v1 + FromPOSets::SEP + v2;
    return results;
  };

  auto builToOriginal = [&](std::string& estring, std::vector<std::uint_fast64_t>& eids) {
    auto estring_split = split(estring, FromPOSets::SEP);
    for (std::uint_fast64_t p = 0; p < estring_split.size(); ++p) {
      auto v1 = result->posets.at(p)->getEID(estring_split.at(p));
      eids.at(p) = v1;
    }
  };

  auto next = [&](std::vector<std::uint_fast64_t>& s) {
    if (s.at(1) < p2->size() - 1) {
      ++s.at(1);
      return true;
    } else if (s.at(0) < p1->size() - 1) {
      ++s.at(0);
      s.at(1) = 0;
      return true;
    }
    return false;
  };
  {
    std::uint_fast64_t k = 0;
    do {
      elements.at(k) = selezionato;
      ++k;
    } while (next(selezionato));
  }
  auto elements_to_string = std::make_shared<std::vector<std::string>>(elements.size(), "");
  auto elements_to_original = std::make_shared<std::vector<std::vector<std::uint_fast64_t>>>(elements.size(), std::vector<std::uint_fast64_t>(result->posets.size()));

  std::vector<std::uint_fast64_t> eids(result->posets.size());
  for (std::uint_fast64_t k = 0; k < elements_to_string->size(); ++k) {
    auto& v_p = elements.at(k);
    auto v = buildString(v_p);
    elements_to_string->at(k) = v;
    builToOriginal(v, elements_to_original->at(k));
  }

  auto comparabilities = std::make_shared<std::list<std::pair<std::string, std::string>>>();
  auto p1_upsets = p1->UpSets();
  auto p2_upsets = p2->UpSets();
  for (std::uint_fast64_t k = 0; k < elements_to_string->size(); ++k) {
    auto& v1_p = elements.at(k);
    auto& v1 = elements_to_string->at(k);
    for (std::uint_fast64_t h = k + 1; h < elements_to_string->size(); ++h) {
      auto& v2_p = elements.at(h);
      auto& v2 = elements_to_string->at(h);

      auto a1 = v1_p.at(0);
      auto b1 = v2_p.at(0);
      auto a2 = v1_p.at(1);
      auto b2 = v2_p.at(1);

      if ((p1_upsets->at(a1)->find(b1) != p1_upsets->at(a1)->end()) ||
          ((a1 == b1 && p2_upsets->at(a2)->find(b2) != p2_upsets->at(a2)->end()))) {
        comparabilities->push_back(std::make_pair(v1, v2));
      } else if ((p1_upsets->at(b1)->find(a1) != p1_upsets->at(b1)->end()) ||
        ((a1 == b1 || p2_upsets->at(b2)->find(a2) != p2_upsets->at(b2)->end()))) {
        comparabilities->push_back(std::make_pair(v2, v1));
      }
    }
  }
  result->elements_to_original = elements_to_original;
  result->FillBaseAttribute(*elements_to_string, *comparabilities, nullptr, false);

    return result;
}
