#include "stdafx.h"

#include "ccl/rslang/RSGenerator.h"
#include "xtr/dialog/CstDataPage.h"

#include "resource.h"

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

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

#include "xtr/dialog/EditConstituenta.h"

namespace xtr::dialog {

using ccl::semantic::ParsingStatus;
using ccl::semantic::CstType;
using ccl::rslang::TokenID;
using ccl::rslang::ValueClass;
using ui::Keyboard;

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

BEGIN_MESSAGE_MAP(CstDataPage, CPropertyPage)
  ON_WM_DESTROY()

  ON_EN_SETFOCUS(IXTRC_NAME, &ThisClass::OnSetfocusName)
  ON_EN_KILLFOCUS(IXTRC_EXPRESSION, &ThisClass::OnKillfocusExpression)
  ON_EN_CHANGE(IXTRC_EXPRESSION, &ThisClass::OnChangeExpression)
  ON_EN_CHANGE(IXTRC_INTERPRETATION, &ThisClass::OnChangeInterpretation)
  ON_EN_CHANGE(IXTRC_COMMENT_KS, &ThisClass::OnChangeComment)
  ON_EN_CHANGE(IXTRC_NAME, &ThisClass::OnChangeName)
  ON_EN_CHANGE(IXTRC_TERM, &ThisClass::OnChangeTerm)
  ON_CBN_SELCHANGE(IXTRC_NAME_CB, &ThisClass::OnSelchangeName)

  ON_BN_CLICKED(IXTRC_ALLOW_EDIT, &ThisClass::OnAllowEdit)
  ON_BN_CLICKED(IXTRC_PREV, &ThisClass::OnPrev)
  ON_BN_CLICKED(IXTRC_NEXT, &ThisClass::OnNext)
  ON_BN_CLICKED(IXTRC_INPUT_HELP, &ThisClass::OnHelpInput)
  ON_BN_CLICKED(IXTRC_ANALYSE, &ThisClass::OnAnalyse)
  ON_BN_CLICKED(IXTRC_CALCULATE, &ThisClass::OnCalculate)
  ON_BN_CLICKED(IXTRC_SHOW_SSYNTAX, &ThisClass::OnSimpleSyntax)

  ON_COMMAND_EX(IXTRC_BOOLEAN, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_BOOL, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_PR_S, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_PR_L, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_FILTER, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_CARD, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_DEBOOL, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_RED, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_CARTPROD, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_NEGATION, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_UNION, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_INTERSECT, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_OR, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_BELONG, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_IMPLICATION, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_INEQ, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_NULLSET, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_QUANTEXIST, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_QUANTUNIVERSAL, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_LOGIC_EQUAL, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_EQUAL, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_LOGIC_AND, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_SET_MINUS, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_NOTELEMENT, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_SUBSET, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_NOTSUBSET, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_SUBSET_OR_EQ, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_DOUBLEPAR, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_DOUBLESQR, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_GREATER_OR_EQ, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_LESSER_OR_EQ, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_SET_SYMMINUS, &ThisClass::OnInsertBtn)

  ON_COMMAND_EX(IXTRC_RS_RECURSIVE, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_RS_DECLARATIVE, &ThisClass::OnInsertBtn)
  ON_COMMAND_EX(IXTRC_RS_IMPERATIVE, &ThisClass::OnInsertBtn)
END_MESSAGE_MAP()

#pragma warning( pop )

CstDataPage::CstDataPage(EditConstituentaDlg& parent)
            : CPropertyPage(IXTRD_CST_EDIT_DATA), parent(parent) {}

BOOL CstDataPage::OnInitDialog() {
  CPropertyPage::OnInitDialog();

  InitControls();
  InitCstNameList();
  UpdateData(FALSE);
  return TRUE;
}

void CstDataPage::InitControls() {
  static constexpr auto PROPERTI_DROPPED_WIDTH = 820U;

  Keyboard::Instance().RegisterIndicator(&languageIndicator);

  expressionCtrl.InitRE(Keyboard::Layout::MATH, TRUE, TRUE);
  typificationCtrl.InitRE(Keyboard::Layout::MATH, TRUE, FALSE);
  definitionCtrl.InitRE(Keyboard::Layout::RUS, FALSE, TRUE);
  termCtrl.InitRE(Keyboard::Layout::RUS, FALSE, TRUE);

  conventionCtrl.SetAutoURLDetect();
  conventionCtrl.InitRE(Keyboard::Layout::RUS);

  logCtrl.SetAutoURLDetect();
  logCtrl.InitRE();

  namePicker.SetDroppedWidth(PROPERTI_DROPPED_WIDTH);

  cstNameCtrl.SetFont(&XTROptions::App().fontMath);
  logCtrl.SetFont(&XTROptions::App().fontTooltip);
  definitionCtrl.SetFont(&XTROptions::App().fontText);
  termCtrl.SetFont(&XTROptions::App().fontText);
  conventionCtrl.SetFont(&XTROptions::App().fontMathComment);
  languageIndicator.SetFont(&XTROptions::App().fontTooltip);
  namePicker.SetFont(&XTROptions::App().fontText);

  if (!parent.editor->IsModel()) {
    GetDlgItem(IXTRC_CALCULATE)->ShowWindow(FALSE);
  }
}

void CstDataPage::DoDataExchange(CDataExchange* pDX) {
  CPropertyPage::DoDataExchange(pDX);

  ::DDX_Control(pDX, IXTRC_NAME, cstNameCtrl);
  ::DDX_Control(pDX, IXTRC_NAME_CB, namePicker);
  ::DDX_Control(pDX, IXTRC_ALLOW_EDIT, allowEditCtrl);
  ::DDX_Control(pDX, IXTRC_CALCULATE, evaluateBtn);
  ::DDX_Control(pDX, IXTRC_EXPRESSION, expressionCtrl);
  ::DDX_Control(pDX, IXTRC_INTERPRETATION, definitionCtrl);
  ::DDX_Control(pDX, IXTRC_TERM, termCtrl);
  ::DDX_Control(pDX, IXTRC_COMMENT_KS, conventionCtrl);
  ::DDX_Control(pDX, IXTRC_TYPIFICATION, typificationCtrl);
  ::DDX_Control(pDX, IXTRC_SYNTAX, logCtrl);
  ::DDX_Control(pDX, IXTRC_LANGUAGE, languageIndicator);

  const auto& core = parent.editor->Core();
  const auto& cst = core.GetRS(parent.activeUID);
  if (!pDX->m_bSaveAndValidate) {
    const auto isInherited = parent.editor->IsModel() ? FALSE : parent.editor->Schema().Mods().IsTracking(parent.activeUID);

    LoadDataFrom(parent.activeUID);

    allowEditCtrl.ShowWindow(isInherited);

    expressionCtrl.Init(core, parent.activeUID);
    expressionCtrl.SetReadOnly(isInherited);

    termCtrl.Initialize(term, *parent.editor, !XTROptions::Text().editReference, FALSE);
    termCtrl.SetReadOnly(!allowEdit);
    definitionCtrl.Initialize(definition, *parent.editor, !XTROptions::Text().editReference);
    definitionCtrl.SetReadOnly(!allowEdit);

    if (ccl::semantic::IsBaseNotion(cst.type)) {
      GetDlgItem(IXTRC_COMMENT_STATIC)->SetWindowTextW(mfc::LoadSID(IXTRS_CONVENTION));
    } else {
      GetDlgItem(IXTRC_COMMENT_STATIC)->SetWindowTextW(mfc::LoadSID(IXTRS_COMMENT));
    }
    conventionCtrl.SetReadOnly(!allowEdit);

    InitCstNameList();
  }

  auto sellectedName = mfc::ToMFC(cst.alias);
  ::DDX_Check(pDX, IXTRC_ALLOW_EDIT, allowEdit);
  ::DDX_Text(pDX, IXTRC_EXPRESSION, expression);
  ::DDX_Text(pDX, IXTRC_TYPIFICATION, typification);
  ::DDX_Text(pDX, IXTRC_COMMENT_KS, convention);
  ::DDX_Text(pDX, IXTRC_NAME, cstName);
  ::DDX_CBString(pDX, IXTRC_NAME_CB, sellectedName);

  if (!pDX->m_bSaveAndValidate) {
    UpdateFormat();
  } else {
    SaveDataFor(parent.activeUID);
    InitCstNameList();
  }
  UpdateStatusBar();
  ClearSyntaxWindow();
}

void CstDataPage::LoadDataFrom(const EntityUID target) {
  allowEdit = TRUE;
  if (!parent.editor->IsModel()) {
    if (parent.editor->Schema().Mods().IsTracking(target)) {
      allowEdit = parent.editor->Schema().Mods()(target)->allowEdit;
    }
  }
  const auto& rsCst = parent.editor->GetRS(target);
  const auto& textCst = parent.editor->GetText(target);

  cstName = mfc::ToMFC(rsCst.alias);
  expression = mfc::ToMFC(rsCst.definition);
  convention = mfc::ToMFC(rsCst.convention);
  
  typification = info::Label(parent.editor->GetParse(target).TypePtr());
  parseStatus = parent.editor->GetParse(target).status;
  valueStatus = parent.editor->GetParse(target).valueClass;

  definition = mfc::ToMFC(textCst.definition.Raw());
  term = mfc::ToMFC(textCst.term.Text().Raw());
}

void CstDataPage::SaveDataFor(const EntityUID uid) {
  auto expr = mfc::ToSTL(expressionCtrl.Text()); 
  // Note: dont use ::DDX_Text value because of line breakes are doubled ('\r\n')
  auto stlComment = mfc::ToSTL(conventionCtrl.Text());
  auto definitionRef = ui::TrimText(definitionCtrl.GetRefStr());
  auto termRef = ui::TrimText(termCtrl.GetRefStr());

  if (!parent.editor->IsModel() && parent.editor->Schema().Mods().IsTracking(uid)) {
    parent.editor->EditSchema().Mods()(uid)->allowEdit = allowEdit;
  }
  parent.editor->SetAliasFor(uid, mfc::ToSTL(cstName));
  parent.editor->SetExpressionFor(uid, expr);
  parent.editor->SetConventionFor(uid, stlComment);
  parent.editor->SetDefinitionFor(uid, mfc::ToSTL(definitionRef));
  parent.editor->SetTermFor(uid, mfc::ToSTL(termRef));
}

BOOL CstDataPage::PreTranslateMessage(MSG* pMsg) {
  if (pMsg->message == WM_KEYDOWN) {
    if (pMsg->wParam == VK_ESCAPE) {  
      OnCancel();  
      return TRUE;
    } else if (pMsg->wParam == VK_F1) {
      ui::HelpHandler::RunHelp(parent.editor->IsModel() ? ui::HelpID::editData : ui::HelpID::editCst);
      return TRUE;
    }
  }
  return CPropertyPage::PreTranslateMessage(pMsg);
}

void CstDataPage::OnSetfocusName() { // NOLINT(readability-convert-member-functions-to-static)
  Keyboard::Instance().SetLayout(Keyboard::Layout::ENG);
}

void CstDataPage::OnKillfocusExpression() {
  if (GetFocus() == nullptr ||
    (GetFocus() != GetDlgItem(IXTRC_ANALYSE) &&
     !GetFocus()->IsKindOf(RUNTIME_CLASS(CButton)))) {
    RunAutocheck();
  }
}

void CstDataPage::OnDestroy() {
  Keyboard::Instance().UnregisterIndicator(&languageIndicator);
  CPropertyPage::OnDestroy();
}

void CstDataPage::OnCancel() {
  CPropertyPage::OnCancel();
  EndDialog(IDCANCEL);
}

BOOL CstDataPage::OnApply() {
  if (!CPropertyPage::OnApply()) {
    return FALSE;
  } else {
    if (parent.GetActivePage() == this) {
      RunAutocheck();
      UpdateData(FALSE);
    }
    return TRUE;
  }
}

void CstDataPage::OnOK() {
  if (parent.GetActivePage() == this) {
    UpdateData(TRUE);
    SetModified(FALSE);
  }
  CPropertyPage::OnOK();
}

void CstDataPage::OnSelchangeName() {
  const auto index = namePicker.GetCurSel();
  assert(index != mfc::noItems);

  OnApply();

  CString name{};
  namePicker.GetLBText(index, name);
  name = mfc::FirstWordOf(name);

  parent.activeUID = parent.editor->Core().FindAlias(mfc::ToSTL(name)).value();
  UpdateData(FALSE);
}

void CstDataPage::OnChangeExpression() {
  if (expressionCtrl.Text() != expression) {
    OnChange();
    UpdateStatus(ParsingStatus::UNKNOWN, ValueClass::invalid);
  }
}

void CstDataPage::OnAllowEdit() {
  UpdateData();
  UpdateData(FALSE);
}

void CstDataPage::OnChange() {
  SetModified();
}

void CstDataPage::OnChangeInterpretation() {
  if (GetFocus() != nullptr && *GetFocus() == definitionCtrl) {
    OnChange();
  }
}

void CstDataPage::OnChangeTerm() {
  if (GetFocus() != nullptr && *GetFocus() == termCtrl) {
    OnChange();
  }
}

void CstDataPage::OnChangeComment() {
  if (GetFocus() != nullptr && *GetFocus() == conventionCtrl) {
    OnChange();
  }
}

void CstDataPage::OnChangeName() {
  if (GetFocus() != nullptr && *GetFocus() == cstNameCtrl) {
    OnChange();
  }
}

void CstDataPage::OnPrev() {
  UpdateData(TRUE);
  parent.StepPrev();
  UpdateData(FALSE);
}

void CstDataPage::OnNext() {
  UpdateData(TRUE);
  parent.StepNext();
  UpdateData(FALSE);
}

void CstDataPage::OnHelpInput() { // NOLINT(readability-convert-member-functions-to-static)
  ui::HelpHandler::RunHelp(ui::HelpID::rslanguage);
}

BOOL CstDataPage::OnInsertBtn(uint32_t command) {
  switch(command) {
  case IXTRC_EQUAL: expressionCtrl.InputToken(TokenID::EQUAL);      break;
  case IXTRC_INEQ: expressionCtrl.InputToken(TokenID::NOTEQUAL);      break;
  case IXTRC_LESSER_OR_EQ: expressionCtrl.InputToken(TokenID::LESSER_OR_EQ);    break;
  case IXTRC_GREATER_OR_EQ: expressionCtrl.InputToken(TokenID::GREATER_OR_EQ);  break;

  case IXTRC_QUANTUNIVERSAL: expressionCtrl.InputToken(TokenID::FORALL);    break;
  case IXTRC_QUANTEXIST: expressionCtrl.InputToken(TokenID::EXISTS);    break;
  case IXTRC_LOGIC_AND: expressionCtrl.InputToken(TokenID::AND);    break;
  case IXTRC_OR: expressionCtrl.InputToken(TokenID::OR);      break;
  case IXTRC_NEGATION: expressionCtrl.InputToken(TokenID::NOT);    break;
  case IXTRC_IMPLICATION: expressionCtrl.InputToken(TokenID::IMPLICATION);break;
  case IXTRC_LOGIC_EQUAL: expressionCtrl.InputToken(TokenID::EQUIVALENT);    break;

  case IXTRC_NULLSET: expressionCtrl.InputToken(TokenID::LIT_EMPTYSET);      break;
  case IXTRC_UNION: expressionCtrl.InputToken(TokenID::UNION);    break;
  case IXTRC_SET_MINUS: expressionCtrl.InputToken(TokenID::SET_MINUS);    break;
  case IXTRC_SET_SYMMINUS: expressionCtrl.InputToken(TokenID::SYMMINUS);    break;
  case IXTRC_INTERSECT: expressionCtrl.InputToken(TokenID::INTERSECTION);  break;
  case IXTRC_BELONG: expressionCtrl.InputToken(TokenID::IN);      break;
  case IXTRC_NOTELEMENT: expressionCtrl.InputToken(TokenID::NOTIN);    break;
  case IXTRC_SUBSET: expressionCtrl.InputToken(TokenID::SUBSET);      break;
  case IXTRC_NOTSUBSET: expressionCtrl.InputToken(TokenID::NOTSUBSET);    break;
  case IXTRC_SUBSET_OR_EQ: expressionCtrl.InputToken(TokenID::SUBSET_OR_EQ);    break;

  case IXTRC_BOOLEAN: expressionCtrl.InputToken(TokenID::BOOLEAN);      break;
  case IXTRC_PR_S: expressionCtrl.InputToken(TokenID::SMALLPR);      break;
  case IXTRC_PR_L: expressionCtrl.InputToken(TokenID::BIGPR);      break;
  case IXTRC_FILTER: expressionCtrl.InputToken(TokenID::FILTER);      break;
  case IXTRC_CARD: expressionCtrl.InputToken(TokenID::CARD);        break;
  case IXTRC_CARTPROD: expressionCtrl.InputToken(TokenID::DECART);      break;

  case IXTRC_BOOL: expressionCtrl.InputToken(TokenID::BOOL);    break;
  case IXTRC_DEBOOL: expressionCtrl.InputToken(TokenID::DEBOOL);    break;
  case IXTRC_RED: expressionCtrl.InputToken(TokenID::REDUCE);      break;
  case IXTRC_RS_DECLARATIVE: expressionCtrl.InputToken(TokenID::DECLARATIVE);  break;
  case IXTRC_RS_RECURSIVE: expressionCtrl.InputToken(TokenID::RECURSIVE);  break;
  case IXTRC_RS_IMPERATIVE: expressionCtrl.InputToken(TokenID::IMPERATIVE);  break;

  case IXTRC_DOUBLEPAR: expressionCtrl.InputParenthesis();  break;
  case IXTRC_DOUBLESQR: expressionCtrl.InputBrackets();  break;
  }
  return TRUE;
}

void CstDataPage::OnAnalyse() {
  auto [str, basePos] = GetAnalyseExpr();
  auto analyser = parent.editor->Core().RSLang().MakeAuditor();
  const ccl::rslang::ExpressionType* type{ nullptr };
  const auto typeOK = analyser->CheckType(str, ccl::rslang::Syntax::MATH);
  if (typeOK) {
    type = &analyser->GetType();
  }
  
  const auto rstype = parent.editor->GetRS(parent.activeUID).type;
  const auto typeConsistent = typeOK
    && (!ccl::semantic::IsBaseSet(rstype) || expressionCtrl.Text().IsEmpty())
    && ccl::semantic::Schema::CheckTypeConstistency(analyser->GetType(), rstype);
  if (!typeConsistent) {
    UpdateStatus(ParsingStatus::INCORRECT, ValueClass::invalid);
  } else {
    if (!analyser->CheckValue()) {
      UpdateStatus(ParsingStatus::VERIFIED, ValueClass::invalid);
    } else {
      UpdateStatus(ParsingStatus::VERIFIED, analyser->GetValueClass());
    }
  }
  
  if (!empty(analyser->Errors().All())) {
    auto errorPosition = std::max(analyser->Errors().FirstErrorPos() - basePos, 0L);
    auto* re = reinterpret_cast<ui::TextEdit*>(GetDlgItem(IXTRC_EXPRESSION));
    re->SetFocus();
    re->SetSel(errorPosition, errorPosition);
  }

  typificationCtrl.SetWindowTextW(info::Label(type));
  auto log = info::DescriptionOf(*analyser, XTROptions::ParseRSL().showAST);
  if (typeOK && !typeConsistent) {
    log = mfc::LoadSID(IXTRS_TYPE_INCONSISTENT) + log;
  }
  logCtrl.SetWindowTextW(log);
}

void CstDataPage::OnCalculate() {
  if (ccl::semantic::IsBaseNotion(parent.editor->GetRS(parent.activeUID).type)) {
    return;
  }
  if (parseStatus != ParsingStatus::VERIFIED) {
    OnAnalyse();
    return;
  }
  if (!OnApply()) {
    return;
  }
  BeginWaitCursor();

  auto[str, basePos] = GetAnalyseExpr();
  ccl::rslang::Interpreter calculator{ 
    parent.editor->Core().RSLang(),
    parent.editor->Core().RSLang().ASTContext(),
    parent.editor->Model().Calculations().Context()
  };
  const auto calcResult = calculator.Evaluate(str, ccl::rslang::Syntax::MATH);
  if (!calcResult.has_value()) {
    auto errorPosition = std::max(calculator.Errors().FirstErrorPos() - basePos, 0L);
    auto* re = reinterpret_cast<ui::TextEdit*>(GetDlgItem(IXTRC_EXPRESSION));
    re->SetFocus();
    re->SetSel(errorPosition, errorPosition);
  }

  parent.editor->EditModel().Calculations().Calculate(parent.activeUID);

  EndWaitCursor();

  UpdateData(FALSE);
  logCtrl.SetWindowTextW(info::DescriptionOf(calculator, calcResult, XTROptions::ParseRSL().showAST));
}

void CstDataPage::OnSimpleSyntax() {
  auto [expr, basePos] = GetAnalyseExpr();
  const auto asciiExpr = ccl::rslang::ConvertTo(expr, ccl::rslang::Syntax::ASCII);
  logCtrl.SetWindowTextW(mfc::ToMFC(asciiExpr));
}

void CstDataPage::SetModified(BOOL bChanged) {
  isModified = bChanged;
  CPropertyPage::SetModified(bChanged);
}

BOOL CstDataPage::IsModified() const noexcept {
  return isModified;
}

std::tuple<std::string, long> CstDataPage::GetAnalyseExpr() const {
  const auto& cst = parent.editor->GetRS(parent.activeUID);
  const auto expr = mfc::ToSTL(expressionCtrl.Text());
  const auto fullExpr = ccl::rslang::Generator::GlobalDefinition(cst.alias, expr,
                                                                 cst.type == CstType::structured);
  return { fullExpr, static_cast<long>(ssize(fullExpr) - ssize(expr)) };
}

void CstDataPage::UpdateStatus(const ParsingStatus status, const ValueClass vclass) {
  parseStatus = status;
  valueStatus = vclass;
  UpdateStatusBar();
}

void CstDataPage::UpdateStatusBar() {
  auto color = design::clrBlack;
  const auto isCalculable = ccl::semantic::IsCalculable(parent.editor->GetRS(parent.activeUID).type);
  evaluateBtn.EnableWindow(isCalculable && parseStatus == ParsingStatus::VERIFIED);
  if (parseStatus != ParsingStatus::VERIFIED) {
    color = design::Color(parseStatus);
  } else if (!isCalculable || !parent.editor->IsModel()) {
    color = design::Color(valueStatus);
  } else if (isModified) {
    color = design::clrYellow;
  } else {
    color = design::Color(parent.editor->Model().Calculations()(parent.activeUID));
  }

  auto* statusRE = reinterpret_cast<CRichEditCtrl*>(GetDlgItem(IXTRC_EXP_STATUS));
  statusRE->SetBackgroundColor(FALSE, color);
}

void CstDataPage::ClearSyntaxWindow() {
  logCtrl.SetWindowTextW({});
}

void CstDataPage::RunAutocheck() {
  if (parseStatus != ParsingStatus::VERIFIED && XTROptions::ParseRSL().autoCheck) {
    Analyse();
  }
}

void CstDataPage::UpdateFormat() {
  expressionCtrl.UpdateFormat();
  typificationCtrl.UpdateFormat();
  definitionCtrl.UpdateFormat();
  termCtrl.UpdateFormat();
}

void CstDataPage::InitCstNameList() {
  namePicker.SetRedraw(FALSE);

  const auto sel = namePicker.GetCurSel();
  namePicker.ResetContent();

  for (const auto uid : parent.editor->List()) {
    const auto& cst = parent.editor->GetText(uid);
    auto str = mfc::ToMFC(cst.alias);
    str += LR"( - )";
    if (!std::empty(cst.term.Nominal())) {
      str += mfc::ToMFC(cst.term.Nominal());
    } else {
      str += mfc::ToMFC(cst.definition.Str());
    }
    str.Replace(L"\n", L" ");
    namePicker.AddString(str);
  }
  namePicker.SetCurSel(sel);
  namePicker.SetRedraw(TRUE);
}

void CstDataPage::Analyse() {
  auto[str, basePos] = GetAnalyseExpr();
  auto analyser = parent.editor->Core().RSLang().MakeAuditor();
  if (!analyser->CheckType(str, ccl::rslang::Syntax::MATH)) {
    UpdateStatus(ParsingStatus::INCORRECT, ValueClass::invalid);
  } else if (!analyser->CheckValue()) {
    UpdateStatus(ParsingStatus::VERIFIED, ValueClass::invalid);
  } else {
    UpdateStatus(ParsingStatus::VERIFIED, analyser->GetValueClass());
  }
  logCtrl.SetWindowTextW(CString{});
}

} // namespace xtr::dialog