#include "stdafx.h"

#include "xtr/dialog/InsertCstDlg.h"

#include "resource.h"

#include "xtr/ExteorOptions.h"
#include "xtr/cclDescriptor.h"

#include "ccl/semantic/CstFilters.hpp"

namespace xtr::dialog {

using ccl::semantic::RSCore;

InsertCstDlg::InsertCstDlg(const RSCore& context, const CPoint pos,
                           const CString hint, CWnd* pParent)
  : CDialog(IXTRD_AUTOCOMPLETE, pParent),
  context{ context }, position{ pos }, hint{ hint } {}

void InsertCstDlg::DoDataExchange(CDataExchange* pDX) {
  CDialog::DoDataExchange(pDX);
  ::DDX_Control(pDX, IXTRC_AC_LIST1, pickerControl);
}

BOOL InsertCstDlg::OnInitDialog() {
  CDialog::OnInitDialog();

  if (!hint.IsEmpty()) {
    UpdateContextNameList();
  }
  if (std::empty(typifications)) {
    UpdateTypingNameList();
  } else {
    state = DS_CSTS;
  }
  pickerControl.SetFont(&XTROptions::App().fontMathTT);
  pickerControl.SetCurSel(0);

  FitDialogSize();
  return TRUE;
}

BOOL InsertCstDlg::PreTranslateMessage(MSG* pMsg) {
  if (pMsg->hwnd == pickerControl.GetSafeHwnd()) {
    switch (state) {
    case DS_DEFAULT:
      if (pMsg->message == WM_LBUTTONDBLCLK ||
        (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)) {
        OnSelectTyping();  return TRUE;
      }
      break;

    case DS_CSTS:
      if (pMsg->message == WM_LBUTTONDBLCLK ||
        (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)) {
        OnSelectCst(); return TRUE;
      }
      break;
    }
  }
  return CDialog::PreTranslateMessage(pMsg);
}

void InsertCstDlg::OnSelectTyping() {
  const auto selected = pickerControl.GetCurSel();
  choice = typifications.at(selected);
  VectorOfEntities filteredIDs{};
  if (selected == 0) {
    std::copy(std::begin(context.List()), std::end(context.List()), std::back_inserter(filteredIDs));
  } else {
    const auto filter = [&typeStr = this->choice, &schema = this->context] (const auto uid)
     -> std::optional<EntityUID> {
      const auto* cstType = schema.GetParse(uid).TypePtr();
      if (cstType != nullptr && typeStr == info::Label(cstType)) {
        return uid;
      } else {
        return std::nullopt;
      }
    };
    filteredIDs = ccl::semantic::FilterCst(context, filter);
  }

  typifications.clear();
  pickerControl.ResetContent();

  CString listItem{};
  for (const auto uid : filteredIDs)  {
    const auto& rsCst = context.GetRS(uid);
    typifications.emplace_back(mfc::ToMFC(rsCst.alias));

    listItem = mfc::ToMFC(rsCst.alias);
    listItem += LR"(: )";
    listItem += mfc::ToMFC(context.GetText(uid).term.Nominal());
    pickerControl.AddString(listItem);
  }

  pickerControl.SetCurSel(0);
  pickerControl.SetFocus();

  FitDialogSize();
  state = DS_CSTS;
}

void InsertCstDlg::OnSelectCst() {
  cstName = typifications.at(pickerControl.GetCurSel());
  OnOK();
}

void InsertCstDlg::FitDialogSize() {
  static constexpr auto MAX_WIDTH = 600;

  auto h = 0;
  auto width1 = 0;
  CString str{};
  CSize sz{};

  auto* pDC1 = pickerControl.GetDC();
  pDC1->SelectObject(pickerControl.GetFont());

  for (auto i = 0; i < pickerControl.GetCount(); ++i) {
    if (i < 10) {
      h += pickerControl.GetItemHeight(i);
    }
    pickerControl.GetText(i, str);
    sz = pDC1->GetTextExtent(str);
    if (sz.cx > width1) {
      width1 = sz.cx;
    }
  }
  pickerControl.ReleaseDC(pDC1);

  width1 = std::min(width1 + 5, MAX_WIDTH) + GetSystemMetrics(SM_CXVSCROLL);
  h += 4;
  SetWindowPos(nullptr, position.x, position.y, width1, h, SWP_SHOWWINDOW);
  pickerControl.SetWindowPos(&wndTop, 0, 0, width1, h, SWP_SHOWWINDOW);
  pickerControl.SetColumnWidth(width1);
}

void InsertCstDlg::UpdateTypingNameList() {
  static auto selectAll = mfc::LoadSID(IXTRS_SELECT_ALL);

  pickerControl.ResetContent();
  typifications.clear();
  typifications.emplace_back(selectAll);
  pickerControl.AddString(selectAll);

  std::set<CString> steps{};
  for (const auto uid : context.List()) {
    steps.insert(info::Label(context.GetParse(uid).TypePtr()));
  }

  for (const auto& aName : steps) {
    typifications.emplace_back(aName);
    pickerControl.AddString(aName);
  }
}

void InsertCstDlg::UpdateContextNameList() {
  pickerControl.ResetContent();
  typifications.clear();

  const auto inputs = context.Texts().Context().MatchingTerms(mfc::ToSTL(hint));
  CString listItem{};
  for (const auto uid : context.List()) {
    const auto& alias = context.GetText(uid).alias;
    if (inputs.contains(alias)) {
      const auto& title = inputs.at(alias);
      typifications.emplace_back(mfc::ToMFC(alias));
      listItem = mfc::ToMFC(alias);
      listItem += LR"(: )";
      listItem += mfc::ToMFC(title);
      pickerControl.AddString(listItem);
    }
  }
}

} // namespace xtr::dialog