#include "stdafx.h"

#include "xtr/io/OSSLegacyLoader.h"

using ccl::oss::GridIndex;
using ccl::oss::GridPosition;
using ccl::oss::Pict;
using ccl::ops::EquationOptions;
using ccl::ops::Equation;
using ccl::meta::UniqueCPPtr;

namespace xtr::io {

void OSSLegacyLoader::LoadVersion() {
  const auto versionIntro = ar.Load<CString>();
  if (versionIntro != LR"(__@OSS_VERSION@__)") {
    oss.comment = mfc::ToSTL(versionIntro);
    version = OSSVersion::old;
  } else {
    version = OSSVersion{ OSSVersion::VersionID{ ar.Load<uint16_t>() } };
    ar.Skip<CString>();
  }
}

void OSSLegacyLoader::DoLoad() {
  if (version.v == OSSVersion::old) {
    LoadFirstGen();
  } else if (version.v < OSSVersion::r1800) {
    LoadSecondGen();
  } else {
    LoadThirdGen();
  }
}

void OSSLegacyLoader::LoadFirstGen() {
  oss.title = ar.Load<std::string>();
  auto pictCount = ar.Load<uint16_t>();
  while (pictCount--) {
    LoadPictGen1();
  }
}

void OSSLegacyLoader::LoadPictGen1() {
  GridPosition pos{};
  pos.row = GridIndex{ ar.Load<int32_t>() - 1 };
  pos.column = GridIndex{ ar.Load<int32_t>() - 1 };
  ar.Skip<CRect>();
  ar.Skip<CRect>();

  ccl::src::Handle srcHandle{};
  const auto scrPath = ar.Load<CString>();
  if (!scrPath.IsEmpty()) {
    srcHandle.desc = ccl::src::Descriptor{ ccl::src::SrcType::rsDoc, mfc::ToUTF8(scrPath) };
  }

  Pict pict{ 0 };
  pict.alias = ar.Load<std::string>();
  pict.title = ar.Load<std::string>();

  const auto isActive = ar.Load<int32_t>();

  GridPosition posParent1{};
  posParent1.row = GridIndex{ ar.Load<int32_t>() - 1 };
  posParent1.column = GridIndex{ ar.Load<int32_t>() - 1 };
  const auto parent1 = oss.Grid()(posParent1);

  GridPosition posParent2{};
  posParent2.row = GridIndex{ ar.Load<int32_t>() - 1 };
  posParent2.column = GridIndex{ ar.Load<int32_t>() - 1 };
  const auto parent2 = oss.Grid()(posParent2);

  const auto countMapItems = ar.Load<int32_t>();
  if (countMapItems != 0) {
    CMapStringToString* loadMap{};
    ar >> loadMap;
    delete loadMap; // NOLINT(cppcoreguidelines-owning-memory)
  }

  ar.Skip<uint16_t>();
  ar.Skip<uint16_t>();

  const auto status = LoadPictStatus(ar.Load<uint16_t>());
  const auto operationType = LoadOperationType(ar.Load<uint16_t>());
  auto operation = std::unique_ptr<ccl::oss::OperationHandle>{ nullptr };
  if (status >= static_cast<uint32_t>(ccl::ops::Status::undefined)) {
    operation = std::make_unique<ccl::oss::OperationHandle>();
    operation->type = operationType;
  }
  
  const auto& loaded = oss.LoadPict(std::move(pict), pos, srcHandle, std::move(operation));
  if (parent1.has_value()) {
    oss.Graph().LoadParent(loaded.uid, parent1.value());
  }
  if (parent2.has_value()) {
    oss.Graph().LoadParent(loaded.uid, parent2.value());
  }
  if (isActive) {
    selection = loaded.uid;
  }
}

uint32_t OSSLegacyLoader::LoadPictStatus(const uint32_t code) noexcept {
  switch (code) {
  default: case 0: return 0; // NOLINT
  case 1: case 2: return 1; // NOLINT
  case 3: case 5: return 2; // NOLINT
  case 4: return 3; // NOLINT
  case 6: return 4; // NOLINT
  case 7: return 5; // NOLINT
  case 8: return 6; // NOLINT
  }
}

ccl::ops::Type OSSLegacyLoader::LoadOperationType(const uint32_t code) noexcept {
  if (code >= 5 && code <= 7) {
    return ccl::ops::Type::rsSynt;
  } else {
    return ccl::ops::Type{ code };
  }
}

void OSSLegacyLoader::LoadSecondGen() {
  oss.comment = ar.Load<std::string>();
  oss.title = ar.Load<std::string>();
  auto pictCount = ar.Load<uint16_t>();
  ar.Skip<uint32_t>();
  while (pictCount--) {
    LoadPictGen2();
  }
  ar.Skip<uint32_t>();

  const auto activeID = ccl::oss::PictID{ ar.Load<uint32_t>() };
  selection = oss(activeID) != nullptr ? activeID : std::begin(oss)->uid;
  LoadDependencies();
}

void OSSLegacyLoader::LoadPictGen2() {
  Pict pict{};
  GridPosition pos{};
  ccl::src::Handle srcHandle{};
  auto operHandle = std::make_unique<ccl::oss::OperationHandle>();

  pict.uid = ar.Load<uint32_t>();
  pos.row = GridIndex{ ar.Load<int32_t>() - 1 };
  pos.column = GridIndex{ ar.Load<int32_t>() - 1 };
  srcHandle.coreHash = ar.Load<int32_t>();
  srcHandle.fullHash = ar.Load<int32_t>();
  ar.Skip<int32_t>();
  const auto status = ccl::ops::Status{ ar.Load<uint32_t>() };
  srcHandle.desc = ccl::src::Descriptor{ ccl::src::SrcType::rsDoc, mfc::ToUTF8(ar.Load<CString>()) };
  pict.lnk.address = ar.Load<std::string>();
  pict.lnk.subAddr = ar.Load<std::string>();
  pict.comment = ar.Load<std::string>();
  operHandle->type = LoadOperationType(ar.Load<uint32_t>());
  pict.alias = ar.Load<std::string>();
  pict.title = ar.Load<std::string>();

  operHandle->options = LoadOptionsGen2();
  operHandle->translations = LoadTranslationsGen2();
  operHandle->outdated = status == ccl::ops::Status::outdated;
  if (status < ccl::ops::Status::undefined) {
    operHandle = nullptr;
  }

  oss.LoadPict(std::move(pict), pos, srcHandle, std::move(operHandle));
}

UniqueCPPtr<ccl::ops::Options> OSSLegacyLoader::LoadOptionsGen2() {
  auto equations = std::make_unique<EquationOptions>();
  auto countMapItems = ar.Load<uint32_t>();  
  while (countMapItems--) {
    const auto key = EntityUID{ ar.Load<uint32_t>() };
    const auto value = EntityUID{ ar.Load<uint32_t>() };
    Equation::Mode type{};
    if (version.v < OSSVersion::r1483) {
      type = Equation::Mode::keepHier;
    } else {
      type = static_cast<Equation::Mode>(ar.Load<int32_t>());
      if (type == Equation::Mode::keepHier) {
        type = Equation::Mode::keepDel;
      } else if (type ==  Equation::Mode::keepDel) {
        type = Equation::Mode::keepHier;
      }
    }
    if (version.v >= OSSVersion::r1553) {
      ar.Skip<CString>();
    }
    equations->Insert(key, value, Equation{ type, ""});
  }
  return UniqueCPPtr<ccl::ops::Options>{ std::move(equations) };
}

UniqueCPPtr<ccl::ops::TranslationData> OSSLegacyLoader::LoadTranslationsGen2() {
  auto opCount = ar.Load<uint32_t>();
  if (opCount == 0) {
    return nullptr;
  }

  UniqueCPPtr<ccl::ops::TranslationData> result = std::make_unique<ccl::ops::TranslationData>();
  while (opCount--) {
    auto elementCount = ar.Load<uint32_t>();
    ccl::EntityTranslation element{};
    while (elementCount--) {
      const auto key = EntityUID{ ar.Load<uint32_t>() };
      const auto value = EntityUID{ ar.Load<uint32_t>() };
      element.Insert(key, value);
      if (version.v >= OSSVersion::r1483) {
        ar.Skip<int32_t>();
      }
      if (version.v >= OSSVersion::r1553) {
        ar.Skip<CString>();
      }
    }
    result->emplace_back(element);
  }
  return result;
}

void OSSLegacyLoader::LoadDependencies() {
  auto edgeCount = ar.Load<uint32_t>();
  while (edgeCount--) {
    const auto child = ar.Load<int32_t>();
    const auto parent = ar.Load<int32_t>();
    oss.Graph().LoadParent(child, parent);
  }
}

void OSSLegacyLoader::LoadThirdGen() {
  oss.title = ar.Load<std::string>();
  oss.comment = ar.Load<std::string>();

  auto pictCount = ar.Load<uint32_t>();
  while (pictCount--) {
    LoadPictGen3();
  }

  LoadDependencies();
  selection = ccl::oss::PictID{ ar.Load<uint32_t>() };
}

void OSSLegacyLoader::LoadPictGen3() {
  Pict pict{};
  pict.uid = ccl::oss::PictID{ ar.Load<uint32_t>() };
  pict.dataType = ccl::src::DataType{ ar.Load<uint32_t>() };
  pict.title = ar.Load<std::string>();
  pict.alias = ar.Load<std::string>();
  pict.comment = ar.Load<std::string>();
  pict.lnk.address = ar.Load<std::string>();
  pict.lnk.subAddr = ar.Load<std::string>();

  GridPosition pos{};
  pos.row = GridIndex{ ar.Load<int32_t>() };
  pos.column = GridIndex{ ar.Load<int32_t>() };

  ccl::src::Handle srcHandle{};
  srcHandle.desc.name = mfc::ToUTF8(ar.Load<CString>());
  srcHandle.desc.type = ccl::src::SrcType{ ar.Load<uint32_t>() };
  srcHandle.coreHash = ar.Load<uint32_t>();
  srcHandle.fullHash = ar.Load<uint32_t>();

  std::unique_ptr<ccl::oss::OperationHandle> opHandle{ nullptr };
  if (ar.Load<bool>()) {
    opHandle = std::make_unique<ccl::oss::OperationHandle>();
    opHandle->type = ccl::ops::Type{ ar.Load<uint32_t>() };
    if (ar.Load<bool>()) {
      opHandle->options = LoadOptionsGen3();
    }
    if (ar.Load<bool>()) {
      opHandle->translations = LoadTranslationsGen3();
    }
    opHandle->broken = ar.Load<bool>();
    opHandle->outdated = ar.Load<bool>();
  }

  oss.LoadPict(std::move(pict), pos, srcHandle, std::move(opHandle));
}

UniqueCPPtr<ccl::ops::Options> OSSLegacyLoader::LoadOptionsGen3() {
  auto equations = std::make_unique<EquationOptions>();
  auto countMapItems = ar.Load<uint32_t>();
  while (countMapItems--) {
    const auto key = EntityUID{ ar.Load<uint32_t>() };
    const auto value = EntityUID{ ar.Load<uint32_t>() };
    Equation options{};
    auto mode = ar.Load<uint32_t>();
    if (mode == 0) {
      mode = 1;
    }
    options.mode = static_cast<ccl::ops::Equation::Mode>(mode);
    options.arg = ar.Load<std::string>();
    equations->Insert(key, value, options);
  }
  return UniqueCPPtr<ccl::ops::Options>{ std::move(equations) };
}

UniqueCPPtr<ccl::ops::TranslationData> OSSLegacyLoader::LoadTranslationsGen3() {
  UniqueCPPtr<ccl::ops::TranslationData> result = std::make_unique<ccl::ops::TranslationData>();
  auto equationCount = ar.Load<uint32_t>();
  while (equationCount--) {
    ccl::EntityTranslation element{};
    auto elementCount = ar.Load<uint32_t>();
    while (elementCount--) {
      const auto key = EntityUID{ ar.Load<uint32_t>() };
      const auto value = EntityUID{ ar.Load<uint32_t>() };
      element.Insert(key, value);
    }
    result->emplace_back(element);
  }
  return result;
}

} // namespace xtr::io