#include "stdafx.h"

#include "xtr/view/RSFormView.h"

#include "resource.h"

#include "xtr/Exteor.h"
#include "xtr/cclDescriptor.h"
#include "xtr/xtrDesign.h"
#include "xtr/xtrProcedures.h"
#include "xtr/doc/RSModelDoc.h"

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

#include "ccl/semantic/CstFilters.hpp"

namespace xtr::view {

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

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

IMPLEMENT_DYNCREATE(RSFormView, CFormView)

BEGIN_MESSAGE_MAP(RSFormView, CFormView)
  ON_WM_CONTEXTMENU()

  ON_COMMAND(IXTRC_XTR_HELP, &ThisClass::OpenHelp)
  ON_BN_CLICKED(IXTRC_TRS_SEARCH, &ThisClass::OnSearch)
  ON_BN_CLICKED(IXTRC_COMMENT_B, &ThisClass::OnEditComment)
  ON_BN_CLICKED(IXTRC_TRS_UPDATE_STATUS, &ThisClass::OnUpdateStatus)

  ON_COMMAND_EX(ID_EDIT_COPY, &ThisClass::OnCopy)
  ON_COMMAND_EX(ID_EDIT_PASTE, &ThisClass::OnPaste)
  ON_EN_CHANGE(IXTRC_ALIAS, &ThisClass::OnChange)
  ON_EN_CHANGE(IXTRC_TITLE, &ThisClass::OnChange)

  XTR_COMMAND_UPD(IXTRC_MOVE_UP)
  XTR_COMMAND_UPD(IXTRC_MOVE_DOWN)
  XTR_COMMAND_UPD(IXTRC_SELECT_BASIS)
  XTR_COMMAND_UPD(IXTRC_SELECT_DEPENDENT)
  XTR_COMMAND_UPD(IXTRC_DELETE_CST)
  XTR_COMMAND_UPD(IXTRC_RENAME)
  XTR_COMMAND_UPD(IXTRC_TERM_TO_FUNC)
  XTR_COMMAND_UPD(IXTRC_FUNC_TO_TERM)
  XTR_COMMAND_UPD(IXTRC_STRUCTURE_TO_TERMS)
  XTR_COMMAND_UPD(IXTRC_RESET_NAMES)
  XTR_COMMAND_UPD(IXTRC_RESET_OSSFLAG)
  XTR_COMMAND_UPD(IXTRC_RESET_COLUMNS)

  ON_COMMAND_EX(IXTRC_INSERT_BASE_SET, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_CONST_SET, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_RS, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_AXIOM, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_TERM, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_THEOREMA, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_FUNCTION, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_PREDICATE, &ThisClass::OnInsertCst)
  ON_COMMAND_EX(IXTRC_INSERT_FREE, &ThisClass::OnInsertCst)

  ON_COMMAND(IXTRC_OP_MAX_PART, &ThisClass::OnOpMaxPart)
  ON_COMMAND(IXTRC_OP_SELECT_BASIS, &ThisClass::OnOpExtractBasis)
  ON_COMMAND_EX(IXTRC_CONVERT_TO_RSFORM, &ThisClass::OnOperation)
  ON_COMMAND_EX(IXTRC_EXPORT_SCHEMA, &ThisClass::OnOperation)
  ON_COMMAND_EX(IXTRC_EXPORT_GRAPH, &ThisClass::OnOperation)
  ON_COMMAND_EX(IXTRC_SYNTHES, &ThisClass::OnOperation)
  ON_COMMAND_EX(IXTRC_OP_MERGE, &ThisClass::OnOperation)
  ON_COMMAND_EX(IXTRC_RELATION, &ThisClass::OnOperation)
  ON_COMMAND_EX(IXTRC_CST_MERGING, &ThisClass::OnOperation)
  ON_COMMAND_EX(IXTRC_RESET_COLUMNS, &ThisClass::OnOperation)
END_MESSAGE_MAP()

#pragma warning( pop )

RSFormView::RSFormView()
  : CFormView(IXTRD_VIEW_RSFORM) {}

void RSFormView::OnInitialUpdate() {
  editor = doc::RSFacade{ dynamic_cast<doc::RSFormDoc&>(*m_pDocument) };
  schema = &editor.EditSchema();

  CFormView::OnInitialUpdate();

  InitControls();
  InitTooltips();
}

void RSFormView::InitControls() {
  titleCtrl.InitRE(Keyboard::Layout::RUS);
  titleCtrl.SetFont(&XTROptions::App().fontMath);

  aliasCtrl.InitRE(Keyboard::Layout::RUS);
  aliasCtrl.SetFont(&XTROptions::App().fontMath);

  searchCtrl.InitRE(Keyboard::Layout::RUS);
  searchCtrl.SetFont(&XTROptions::App().fontMath);

  cstTable.SetFont(&XTROptions::App().fontMath);
  cstTable.Init(editor);

  rsformStatusCtrl.SetFont(&XTROptions::App().fontMath);
}

void RSFormView::InitTooltips() {
  if (!tooltips.Create(this)) {
    TRACE(L"Unable to create the ToolTip!");
  } else {
    tooltips.SetMaxTipWidth(ui::s_TooltipWidth);
    tooltips.SetFont(&XTROptions::App().fontTooltip);
    tooltips.Activate(TRUE);

    tooltips.AddTool(&rsformStatusCtrl);
  }
}

void RSFormView::DoDataExchange(CDataExchange* pDX) {
  CFormView::DoDataExchange(pDX);

  ::DDX_Control(pDX, IXTRC_TITLE, titleCtrl);
  ::DDX_Control(pDX, IXTRC_ALIAS, aliasCtrl);
  ::DDX_Control(pDX, IXTRC_RSFORM_SEARCH, searchCtrl);
  ::DDX_Control(pDX, IXTRC_RSFORM_LIST, cstTable);
  ::DDX_Control(pDX, IXTRC_RSFORM_STATUS, rsformStatusCtrl);

  if (!pDX->m_bSaveAndValidate) {
    title = mfc::ToMFC(schema->title);
    alias = mfc::ToMFC(schema->alias);
    if (cstTable.IsInit()) {
      cstTable.Update(TRUE);
    }
  }

  ::DDX_Text(pDX, IXTRC_ALIAS, alias);
  ::DDX_Text(pDX, IXTRC_TITLE, title);

  if (pDX->m_bSaveAndValidate) {
    schema->title = mfc::ToSTL(title);
    schema->alias = mfc::ToSTL(alias);
    editor.Document().SetSelection(cstTable.Selection());
    editor.Document().UpdateAllViews(this);
  }
  UpdateStatusBar();
}

BOOL RSFormView::PreTranslateMessage(MSG* pMsg) {
  if (tooltips.m_hWnd != nullptr) {
    tooltips.RelayEvent(pMsg);
  }
  if (pMsg->message == WM_KEYDOWN) {
    if (pMsg->hwnd == cstTable.GetSafeHwnd() && pMsg->wParam == VK_DELETE) {
      OnCommandRun(IXTRC_DELETE_CST);  return TRUE;
    }
    if (pMsg->hwnd == searchCtrl.GetSafeHwnd() && pMsg->wParam == VK_RETURN) {
      OnSearch();  
      return TRUE;
    }
  }
  return CFormView::PreTranslateMessage(pMsg);
}

void RSFormView::OnSearch() {
  if (const auto searchStr = mfc::ToSTL(searchCtrl.Text()); !std::empty(searchStr)) {
    if (const auto foundItems = ccl::semantic::FilterCst(schema->Core(), searchStr);
        !std::empty(foundItems)) {
      cstTable.SelectMultiple({ std::begin(foundItems), std::end(foundItems) });
      cstTable.SetFocus();
    } else {
      ::AfxMessageBox(IXTRS_SEARCH_NO_MATCH, MB_ICONINFORMATION);
    }
  }
}

void RSFormView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) {
  CView::OnUpdate(pSender, lHint, pHint);
  if (pSender != this) {
    UpdateData(FALSE);
  }
}

BOOL RSFormView::OnCmdMsg(uint32_t nID, int32_t nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) {
  if (GetFocus() == &cstTable &&
      cstTable.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) {
    return TRUE;
  } else {
    return CFormView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
  }
}

void RSFormView::OnChange() {
  UpdateData();
}

BOOL RSFormView::OnCopy(const uint32_t /*msgID*/) { // NOLINT(readability-convert-member-functions-to-static)
  if (auto* active = dynamic_cast<ui::RSEdit*>(GetFocus()); active != nullptr) {
    active->Copy();
  }
  return TRUE;
}

BOOL RSFormView::OnPaste(const uint32_t /*msgID*/) { // NOLINT(readability-convert-member-functions-to-static)
  if (auto* active = dynamic_cast<ui::RSEdit*>(GetFocus()); active != nullptr) {
    active->Paste();
  }
  return TRUE;
}

void RSFormView::OpenHelp() { // NOLINT(readability-convert-member-functions-to-static)
  ui::HelpHandler::RunHelp(ui::HelpID::viewRSForm);
}

void RSFormView::OnOpMaxPart() {
  UpdateData();
  if (const auto res = ccl::ops::OpMaxPart(*schema, Selection()).Execute(); res != nullptr) {
    ExteorApplication::Docs().NewRSFormDoc(*res);
  } else {
    ::AfxMessageBox(IXTRE_OP_APPLICABLE, MB_ICONEXCLAMATION);
  }
}

void RSFormView::OnOpExtractBasis() {
  UpdateData();
  if (const auto res = ccl::ops::OpExtractBasis(*schema, Selection()).Execute(); res != nullptr) {
    ExteorApplication::Docs().NewRSFormDoc(*res);
  } else {
    ::AfxMessageBox(IXTRE_OP_SELECT_BASIS, MB_ICONEXCLAMATION);
  }
}

void RSFormView::OnUpdateStatus() {
  BeginWaitCursor();

  schema->UpdateState();
  UpdateData(FALSE);
  const auto message = info::DescriptionOf(schema->Core());

  EndWaitCursor();

  RedrawWindow();
  ::AfxMessageBox(message, MB_ICONINFORMATION);
}

void RSFormView::OnEditComment() {
  proc::EditComment(editor);
}

void RSFormView::OnContextMenu(CWnd* pWnd, CPoint point) {
  static constexpr auto separator = 0;
  if (pWnd == &cstTable) {
    CMenu popup{};
    popup.LoadMenuW(IXTRAD_RSVIEW_POPUP);
    popup.DeleteMenu(IXTRC_CALCULATE, MF_BYCOMMAND);  // Удаляем пункты, относящиеся к РС-модели

    auto *item0 = popup.GetSubMenu(0);
    for (auto i = 0; i < item0->GetMenuItemCount(); ++i) {
      if (const auto command = item0->GetMenuItemID(i); command != separator) {
        if (!IsContextActive(command)) {
          item0->EnableMenuItem(command, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        } else {
          item0->EnableMenuItem(command, MF_BYCOMMAND | MF_ENABLED);
        }
      }
    }
    item0->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
  }
}

void RSFormView::OnUpdateCommandUI(CCmdUI* pCmdUI) {
  pCmdUI->Enable(IsCommandActive(pCmdUI->m_nID));
}

BOOL RSFormView::IsCommandActive(uint32_t command) const {
  const auto& selected = Selection();
  switch (command) {
  case IXTRC_RESET_NAMES:
  case IXTRC_RESET_OSSFLAG:
  case IXTRC_RESET_COLUMNS:
  default:
    return TRUE;

  case IXTRC_MOVE_UP:
  case IXTRC_MOVE_DOWN:
  case IXTRC_SELECT_BASIS:
  case IXTRC_SELECT_DEPENDENT:
    return cstTable.IsCommandActive(command);

  case IXTRC_STRUCTURE_TO_TERMS:
  case IXTRC_TERM_TO_FUNC:
  case IXTRC_FUNC_TO_TERM:
  case IXTRC_RENAME: return ssize(selected) == 1;
  case IXTRC_DELETE_CST: {
    for (const auto uid : selected) {
      if (schema->Mods().IsTracking(uid)) {
        return FALSE;
      }
    }
    return !std::empty(selected);
  }
  }
}

BOOL RSFormView::OnCommandRun(const uint32_t command) {
  if (!IsCommandActive(command)) {
    return TRUE;
  }

  UpdateData();
  switch (command) {
  case IXTRC_MOVE_UP:
  case IXTRC_MOVE_DOWN:
  case IXTRC_SELECT_BASIS:
  case IXTRC_SELECT_DEPENDENT:
    return cstTable.RunCommand(command);

  case IXTRC_RESET_NAMES: proc::ResetNames(editor); break;
  case IXTRC_RESET_OSSFLAG: proc::ResetClaims(dynamic_cast<doc::RSFormDoc&>(*m_pDocument)); break;
  case IXTRC_RESET_COLUMNS: cstTable.ResetColumns(); break;
  case IXTRC_RENAME: proc::RenameCst(editor); break;
  case IXTRC_DELETE_CST: proc::EraseSelected(dynamic_cast<doc::RSFormDoc&>(*m_pDocument)); break;

  case IXTRC_TERM_TO_FUNC: proc::Term2Function(editor); break;
  case IXTRC_FUNC_TO_TERM: proc::Function2Term(editor); break;
  case IXTRC_STRUCTURE_TO_TERMS: proc::Structure2Terms(editor); break;
  }
  return TRUE;
}

BOOL RSFormView::OnInsertCst(const uint32_t command) {
  const auto nWhere = cstTable.GetNextItem(mfc::noItems, LVNI_FOCUSED);
  switch(command) {
  case IXTRC_INSERT_BASE_SET: InsertNew(CstType::base); break;
  case IXTRC_INSERT_CONST_SET: InsertNew(CstType::constant); break;
  case IXTRC_INSERT_RS: InsertNew(CstType::structured, nWhere);  break;
  case IXTRC_INSERT_AXIOM: InsertNew(CstType::axiom, nWhere);  break;
  case IXTRC_INSERT_TERM: InsertNew(CstType::term, nWhere);  break;
  case IXTRC_INSERT_THEOREMA: InsertNew(CstType::theorem, nWhere); break;
  case IXTRC_INSERT_FUNCTION: InsertNew(CstType::function, nWhere);  break;
  case IXTRC_INSERT_PREDICATE: InsertNew(CstType::predicate, nWhere);  break;
  case IXTRC_INSERT_FREE: InsertFree();  break;
  }
  return TRUE;
}

void RSFormView::InsertNew(const CstType cstType, const int32_t nWhere) {
  UpdateData();
  const auto idWhere = cstTable.UIDForIndex(nWhere);
  const auto newUID = schema->Emplace(cstType);
  if (idWhere.has_value()) {
    schema->MoveBefore(newUID, std::next(schema->List().Find(idWhere.value())));
  }
  editor.Document().ResetSelection();
  editor.Document().Select(newUID);
  editor.Document().UpdateAllViews(nullptr);
}

void RSFormView::InsertFree() {
  if (std::empty(schema->Core())) {
    InsertNew(CstType::base);
  } else {
    if (const auto nItem = cstTable.GetNextItem(mfc::noItems, LVNI_FOCUSED); nItem != mfc::noItems) {
      const auto& cst = schema->GetRS(cstTable.UIDForIndex(nItem).value());
      InsertNew(cst.type, nItem);
    }
  }
}

BOOL RSFormView::OnOperation(uint32_t command) {
  UpdateData();
  switch(command) {
    case IXTRC_EXPORT_SCHEMA:  proc::ExportSchema(editor.Schema(), m_pDocument->GetPathName()); break;
    case IXTRC_EXPORT_GRAPH: proc::ExportCstGraph(editor.Core(), m_pDocument->GetPathName()); break;
    case IXTRC_CONVERT_TO_RSFORM: proc::SpawnModelDocument(*schema); break;
    case IXTRC_SYNTHES: proc::StandaloneSynthes(ccl::ops::Type::rsSynt);  break;
    case IXTRC_OP_MERGE: proc::StandaloneSynthes(ccl::ops::Type::rsMerge); break;
    case IXTRC_RELATION: RunRelation(); break;
    case IXTRC_CST_MERGING: proc::StandaloneEquation(dynamic_cast<doc::RSFormDoc&>(*m_pDocument)); break;
  }
  return TRUE;
}

const SetOfEntities& RSFormView::Selection() const noexcept {
  return cstTable.Selection();
}

BOOL RSFormView::IsContextActive(uint32_t command) const {
  const auto& selected = Selection();
  if (ssize(selected) != 1
      && (command == IXTRC_TERM_TO_FUNC
          || command == IXTRC_FUNC_TO_TERM
          || command == IXTRC_STRUCTURE_TO_TERMS)) {
    return FALSE;
  }
  switch (command) {
  default: return IsCommandActive(command);
  case ID_EDIT_PASTE: return !std::empty(g_Exteor.cstClipboard);
  case IXTRC_TERM_TO_FUNC: {
    const auto selType = schema->GetRS(*std::begin(selected)).type;
    return selType == CstType::term;
  }
  case IXTRC_FUNC_TO_TERM: {
    const auto selType = schema->GetRS(*std::begin(selected)).type;
    return ccl::semantic::IsCallable(selType);
  }
  case IXTRC_STRUCTURE_TO_TERMS: {
    const auto selType = schema->GetRS(*std::begin(selected)).type;
    return ccl::semantic::IsRSObject(selType) && !ccl::semantic::IsBaseSet(selType);
  }
  }
}

void RSFormView::UpdateStatusBar() {
  rsformStatusCtrl.SetBackgroundColor(FALSE, design::Color(info::StatusOf(schema->Core())));
  rsformStatusCtrl.SetWindowTextW(info::IndicatorFor(schema->Core()));
}

void RSFormView::RunRelation() {
  const auto& selected = Selection();
  if (ssize(selected) != 1) {
    ::AfxMessageBox(IXTRE_RELATE_RS, MB_ICONERROR);
    return;
  }
  proc::ExecuteRelation(*schema, *std::begin(selected));
}

} // namespace xtr::view