#include "stdafx.h"

#include "xtr/dialog/GenerateStructure.h"

#include "resource.h"

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

#include "xtr/ui/HelpHandler.h"
#include "xtr/ui/KeyboardHandler.h"
#include "xtr/ui/UIHelper.h"

#include "ccl/rslang/RSGenerator.h"

namespace xtr::dialog {

using ccl::semantic::CstType;
using ui::Keyboard;

struct Structure2Terms::GlobalsHeader : ui::Header {
  enum Columns : uint8_t {
    POS_EXPR = 0,
    POS_TYPE,
    count
  };

  static constexpr uint32_t widthExpression = 190;
  static constexpr uint32_t widthTypification = 130;

  GlobalsHeader() {
    AddColumn({ IXTRS_HEADER_EXPRESSION, Alignment::left, widthExpression });
    AddColumn({ IXTRS_HEADER_TYPE, Alignment::left, widthTypification });
  }
};

struct Structure2Terms::ArgsHeader : ui::Header {
  enum Columns : uint8_t {
    POS_EXPR = 0,
    POS_TYPE,
    POS_TEXT,
    count
  };

  static constexpr auto widthExpression = 190;
  static constexpr auto widthTypification = 130;
  static constexpr auto widthText = 233;

  ArgsHeader() {
    AddColumn({ IXTRS_HEADER_EXPRESSION, Alignment::left, widthExpression });
    AddColumn({ IXTRS_HEADER_TYPE, Alignment::left, widthTypification });
    AddColumn({ IXTRS_HEADER_TERM, Alignment::left, widthText });
  }
};

#pragma warning( push )
#pragma warning( disable : 26440 26436 26454 )

BEGIN_MESSAGE_MAP(Structure2Terms, CDialog)
  ON_CBN_SELCHANGE(IXTRC_TERM_CB, &ThisClass::OnSelchangeTermCb)
  ON_BN_CLICKED(IXTRC_RIGHT, &ThisClass::OnBnClickedRight)
  ON_BN_CLICKED(IXTRC_LEFT, &ThisClass::OnBnClickedLeft)
  ON_BN_CLICKED(IXTRC_APPLY, &ThisClass::OnBnClickedApply)

  ON_NOTIFY(NM_DBLCLK, IXTRC_GLOBAL_LIST, &ThisClass::OnDblClickList)
  ON_NOTIFY(LVN_ITEMACTIVATE, IXTRC_ARG_LIST, &ThisClass::OnItemActivateArgList)
END_MESSAGE_MAP()

#pragma warning( pop )

Structure2Terms::Structure2Terms(doc::RSFacade& editor, const EntityUID startID, CWnd* pParent)
  : CDialog(IXTRD_GENERATE_TERM_STRUC, pParent), editor{ editor }, activeUID{ startID } {}

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

  InitControls();
  InitCstNameList();
  InitHeaders();

  UpdateData(FALSE);
  return TRUE;
}

void Structure2Terms::InitControls() {
  inputCtrl.InitRE(Keyboard::Layout::ENG, TRUE, TRUE);
  textCtrl.SetFont(&XTROptions::App().fontText);
}

void Structure2Terms::InitHeaders() {
  static const GlobalsHeader globalsHead{};
  static const ArgsHeader argsHead{};

  ui::InitHeaderFor(globalsCtrl, globalsHead);
  ui::InitHeaderFor(argsCtrl, argsHead);
}

void Structure2Terms::InitCstNameList() {
  const auto selection = nameCtrl.GetCurSel();
  nameCtrl.ResetContent();
  for (const auto uid : editor.List()) {
    const auto& cst = editor.GetRS(uid);
    if (cst.type == CstType::term || cst.type == CstType::structured) {
      nameCtrl.AddString(mfc::ToMFC(cst.alias));
    }
  }
  nameCtrl.SetCurSel(selection);
}

void Structure2Terms::DoDataExchange(CDataExchange* pDX) {
  CDialog::DoDataExchange(pDX);

  ::DDX_Control(pDX, IXTRC_TERM_CB, nameCtrl);
  ::DDX_Control(pDX, IXTRC_EXPR_INPUT, inputCtrl);
  ::DDX_Control(pDX, IXTRC_INTERP, textCtrl);
  ::DDX_Control(pDX, IXTRC_GLOBAL_LIST, globalsCtrl);
  ::DDX_Control(pDX, IXTRC_ARG_LIST, argsCtrl);

  if (!pDX->m_bSaveAndValidate) {
    const auto& cst = editor.GetText(activeUID);
    activeName = mfc::ToMFC(cst.alias);
    inputExpr = info::Label(editor.GetParse(activeUID).TypePtr());
    m_Interp = mfc::ToMFC(cst.definition.Str());
  }

  ::DDX_Text(pDX, IXTRC_EXPR_INPUT, inputExpr);
  ::DDX_Text(pDX, IXTRC_INTERP, m_Interp);
  ::DDX_CBString(pDX, IXTRC_TERM_CB, activeName);

  if (pDX->m_bSaveAndValidate) {
    activeUID = editor.Core().FindAlias(mfc::ToSTL(activeName)).value();
  } else {
    UpdateUI();
  }
}

BOOL Structure2Terms::PreTranslateMessage(MSG* pMsg) {
  if (pMsg->message == WM_KEYDOWN) {
    if (pMsg->wParam == VK_F1)  {
      ui::HelpHandler::RunHelp(ui::HelpID::generateCst);
      return TRUE;
    }
  }
  return CDialog::PreTranslateMessage(pMsg);
}

void Structure2Terms::OnSelchangeTermCb() {
  UpdateData(TRUE);
  UpdateData(FALSE);
}

void Structure2Terms::OnOK() {
  AddTerms();
  CDialog::OnOK();
}

void Structure2Terms::OnBnClickedApply() {
  if (AddTerms() != 0) {
    ::AfxMessageBox(IXTRS_GENERATE_TERM, MB_OK | MB_ICONINFORMATION);
  }
}

void Structure2Terms::OnBnClickedRight() {
  globalsCtrl.MoveSelectedItemsTo(argsCtrl, ArgsHeader::count);
}

void Structure2Terms::OnBnClickedLeft() {
  argsCtrl.MoveSelectedItemsTo(globalsCtrl, GlobalsHeader::count);
}

void Structure2Terms::OnItemActivateArgList(NMHDR *pNMHDR, LRESULT *pResult) {
  const auto* activePtr = reinterpret_cast<const tagNMITEMACTIVATE*>(pNMHDR);
  *pResult = FALSE;

  if (const auto item = activePtr->iItem; item >= 0 && item < argsCtrl.GetItemCount()) {
    GetInterpDialog dialog{ argsCtrl.GetItemText(item, ArgsHeader::Columns::POS_TEXT) };
    if (dialog.DoModal() == IDOK) {
      argsCtrl.SetItemText(item, ArgsHeader::Columns::POS_TEXT, dialog.textDef);
    }
  }
}

void Structure2Terms::OnDblClickList(NMHDR* /*pNMHDR*/, LRESULT* pResult) {
  OnBnClickedRight();
  *pResult = TRUE;
}

int Structure2Terms::AddTerms() {
  UpdateData(TRUE);

  auto count = 0;
  for (auto i = 0; i < argsCtrl.GetItemCount(); ++i) {
    if (const auto expr = mfc::ToSTL(argsCtrl.GetItemText(i, ArgsHeader::Columns::POS_EXPR));
        !editor.RSLang().FindExpr(expr).has_value()) {
      const auto termText = mfc::ToSTL(argsCtrl.GetItemText(i, ArgsHeader::Columns::POS_TEXT));
      const auto termID = editor.Emplace(CstType::term);
      editor.SetExpressionFor(termID, expr);
      editor.SetTermFor(termID, termText);
      addedCst.emplace_back(termID);
      ++count;
    }
  }

  argsCtrl.DeleteAllItems();
  UpdateUI();
  return count;
}

void Structure2Terms::UpdateUI() {
  inputCtrl.Init(editor.Core(), activeUID);
  Clear();
  InputArguments();
  inputCtrl.UpdateFormat();
}

void Structure2Terms::InputArguments() {
  using ccl::rslang::Generator;
  const auto* type = editor.GetParse(activeUID).Typification();
  if (type == nullptr) {
    return;
  }

  const auto structure = Generator::StructureFor(mfc::ToSTL(activeName), *type);
  auto line = 0;
  for (auto i = 0; i < ssize(structure); ++i) {
    const auto& expr = structure.at(i).first;
    const auto& copy = editor.RSLang().FindExpr(expr);
    auto typification = structure.at(i).second.ToString();
    if (!copy.has_value()) {
      line = globalsCtrl.GetItemCount();
      globalsCtrl.InsertItem(line, mfc::ToMFC(expr));
      globalsCtrl.SetItemText(line, GlobalsHeader::Columns::POS_TYPE, mfc::ToMFC(typification));
    } else {
      line = argsCtrl.GetItemCount();
      argsCtrl.InsertItem(line, mfc::ToMFC(editor.GetRS(copy.value()).definition));
      argsCtrl.SetItemText(line, ArgsHeader::Columns::POS_TYPE, mfc::ToMFC(typification));
      auto termtext = mfc::ToMFC(editor.GetText(copy.value()).term.Nominal());
      if (termtext.IsEmpty()) {
        termtext = mfc::ToMFC(editor.GetText(copy.value()).definition.Str());
      }
      argsCtrl.SetItemText(line, ArgsHeader::Columns::POS_TEXT, termtext);
    }
  }
}

void Structure2Terms::Clear() {
  globalsCtrl.DeleteAllItems();
  argsCtrl.DeleteAllItems();
}

GetInterpDialog::GetInterpDialog(const CString text, CWnd* pParent) 
  : CDialog(IXTRD_EDIT_TERM, pParent), textDef{ text } {}

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

  Keyboard::Instance().SetLayout(Keyboard::Layout::RUS);
  input.InitRE(Keyboard::Layout::RUS);
  input.SetFont(&XTROptions::App().fontText);
  return TRUE;
}

void GetInterpDialog::DoDataExchange(CDataExchange* pDX) {
  CDialog::DoDataExchange(pDX);

  ::DDX_Control(pDX, IXTRC_INTER, input);
  ::DDX_Text(pDX, IXTRC_INTER, textDef);
}

} // namespace xtr::dialog