#include "stdafx.h"

#include "xtr/ui/ElementShell.h"

#include "resource.h"

#include "xtr/Exteor.h"
#include "xtr/ui/UIHelper.h"

namespace xtr::ui {

using ccl::semantic::RSForm;
using ccl::ops::EquationOptions;
using ccl::ops::Equation;

struct EquationsRSLC::EquationsHeader : Header {
  enum Columns : uint8_t {
    OP1_TERM = 0,
    OP1_NAME,
    OP_TYPE,
    OP2_NAME,
    OP2_TERM,
    count
  };

  static constexpr auto widthName = 50;
  static constexpr auto widthTypification = 50;

  explicit EquationsHeader(const int32_t rectWidth) {
    const auto widthText = (rectWidth - widthTypification - s_ScrollWidth - 2 * widthName) / 2;

    AddColumn({ IXTRS_HEADER_TERM, Alignment::left, widthText });
    AddColumn({ IXTRS_HEADER_NAME, Alignment::right, widthName });
    AddColumn({ IXTRS_HEADER_TYPE, Alignment::center, widthTypification });
    AddColumn({ IXTRS_HEADER_NAME, Alignment::left, widthName });
    AddColumn({ IXTRS_HEADER_TERM, Alignment::right, widthText });
  }
};

void EquationsRSLC::InitHeader() {
  EquationsHeader header{ WindowRect(control).Width() };
  InitHeaderFor(control, header);
}

EquationsRSLC::Index EquationsRSLC::InsertItem(const Item& input) {
#pragma warning( push )
#pragma warning( disable : 6387 )
  const auto nInsertPos = control.InsertItem(LVIF_TEXT | LVIF_STATE, control.GetItemCount(), {}, 0, 0, 0, 0);
#pragma warning( pop )
  control.SetItemText(nInsertPos, EquationsHeader::OP1_NAME, input.name1);
  control.SetItemText(nInsertPos, EquationsHeader::OP1_TERM, input.term1);
  control.SetItemText(nInsertPos, EquationsHeader::OP_TYPE, Type2Str(input.type));
  control.SetItemText(nInsertPos, EquationsHeader::OP2_NAME, input.name2);
  control.SetItemText(nInsertPos, EquationsHeader::OP2_TERM, input.term2);

  return nInsertPos;
}

std::optional<EquationsRSLC::Item> EquationsRSLC::GetItem(Index nItem) const {
  if (control.GetItemCount() == 0 || nItem == mfc::noItems) {
    return std::nullopt;
  } else {
    Item result{};
    result.name1 = control.GetItemText(nItem, EquationsHeader::OP1_NAME);
    result.term1 = control.GetItemText(nItem, EquationsHeader::OP1_TERM);
    result.name2 = control.GetItemText(nItem, EquationsHeader::OP2_NAME);
    result.term2 = control.GetItemText(nItem, EquationsHeader::OP2_TERM);
    result.type = Str2Type(control.GetItemText(nItem, EquationsHeader::OP_TYPE));
    return result;
  }
}

std::optional<EquationsRSLC::Item> EquationsRSLC::GetSelectedItem() const {
  return GetItem(control.GetFocusIndex());
}

void EquationsRSLC::LoadTableSynth(const ccl::ops::EquationOptions& aMap, 
                                   const RSForm& ks1, const RSForm& ks2) {
  static const CString ERROR_CST_NAME{ L"ERROR" };

  control.DeleteAllItems();

  CString termin1;
  CString termin2;
  CString name1;
  CString name2;
  for (const auto& [key, value] : aMap) {
    const auto& props = aMap.PropsFor(key);
    if (!ks1.Contains(key)) {
      termin1.Empty();
      name1 = ERROR_CST_NAME;
    } else {
      const auto& cst1 = ks1.GetText(key);
      name1 = mfc::ToMFC(cst1.alias);
      termin1 = props.mode == Equation::Mode::createNew ? mfc::LoadSID(IXTRS_OP_NEW_TERM) : mfc::ToMFC(cst1.term.Nominal());
    }

    if (!ks2.Contains(value)) {
      termin2.Empty();
      name2 = ERROR_CST_NAME;
    } else {
      const auto& cst2 = ks2.GetText(value);
      name2 = mfc::ToMFC(cst2.alias);
      termin2 = props.mode == Equation::Mode::createNew ? mfc::ToMFC(props.arg) : mfc::ToMFC(cst2.term.Nominal());
    }

    InsertItem({ name1, name2, termin1, termin2, props.mode });
  }
}

std::optional<EquationOptions> EquationsRSLC::CreateEquations(const RSForm& schema1, const RSForm& schema2) const {
  EquationOptions synthTable{};
  for (auto i = 0; i < control.GetItemCount(); ++i) {
    const auto name1 = mfc::ToSTL(control.GetItemText(i, EquationsHeader::OP1_NAME));
    const auto name2 = mfc::ToSTL(control.GetItemText(i, EquationsHeader::OP2_NAME));
    const auto type = Str2Type(control.GetItemText(i, EquationsHeader::OP_TYPE));
    const auto termin = type == Equation::Mode::createNew ? mfc::ToSTL(control.GetItemText(i, EquationsHeader::OP2_TERM)) : "";
    const auto uid1 = schema1.Core().FindAlias(name1);
    const auto uid2 = schema2.Core().FindAlias(name2);
    if (!uid1.has_value() || !uid2.has_value()) {
      return std::nullopt;
    } else {
      synthTable.Insert(uid1.value(), uid2.value(), ccl::ops::Equation{ type, termin });
    }
  }
  return synthTable;
}

SetOfEntities EquationsRSLC::Inputs(const RSForm& ks1) const {
  SetOfEntities result{};
  for (auto i = 0; i < control.GetItemCount(); ++i) {
    const auto name1 = mfc::ToSTL(control.GetItemText(i, EquationsHeader::OP1_NAME));
    if (const auto uid = ks1.Core().FindAlias(name1); uid.has_value()) {
      result.insert(uid.value());
    }
  }
  return result;
}

SetOfEntities EquationsRSLC::Outputs(const RSForm& ks2) const {
  SetOfEntities result{};
  for (auto i = 0; i < control.GetItemCount(); ++i) {
    const auto name2 = mfc::ToSTL(control.GetItemText(i, EquationsHeader::OP2_NAME));
    if (const auto uid = ks2.Core().FindAlias(name2); uid.has_value()) {
      result.insert(uid.value());
    }
  }
  return result;
}

Equation::Mode EquationsRSLC::Str2Type(const CString str) noexcept {
  if (str == LR"(<-)") {
    return Equation::Mode::keepHier;
  } else if (str == LR"(->)") {
    return Equation::Mode::keepDel;
  } else {
    return Equation::Mode::createNew;
  }
}

CString EquationsRSLC::Type2Str(Equation::Mode type) {
  switch(type) {
  default:
  case Equation::Mode::keepHier: return LR"(<-)";
  case Equation::Mode::keepDel: return LR"(->)";
  case Equation::Mode::createNew: return LR"(~)";
  }
}

uint32_t CstPicker::Init(const Mode mode, const CString exclude) {
  menuMode = mode;
  m_Exclude = exclude;
  ModifyStyle(0, CBS_SORT);
  UpdateList();
  return GetCount();
}

BOOL CstPicker::IsValid() const {
  if (const auto text = WindowText(*this); text.IsEmpty()) {
    return FALSE;
  } else {
    return FindStringExact(mfc::noItems, text) == CB_ERR;
  }
}

void CstPicker::UpdateList() {
  ResetContent();
  if (menuMode != Mode::DEFAULT) {
    for (const auto& iterList : ExteorApplication::Docs().RSFormList()) {
      CString item{};
      if (menuMode == Mode::TITLE) {
        item = mfc::ToMFC(iterList->title);
      } else {
        item = mfc::ToMFC(iterList->alias);
      }
      if (m_Exclude.Compare(item) != 0 && !item.IsEmpty()) {
        AddString(item);
      }
    }
  }
}

} // namespace xtr::ui