#include "stdafx.h"

#include "xtr/view/PictView.h"

#include "resource.h"

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

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

namespace xtr::view {

using ccl::semantic::RSForm;
using ccl::ops::EquationOptions;
using ccl::oss::PictID;

struct PictView::TableHeader : ui::Header {
  enum Columns : uint8_t {
    COL_NUMBER = 0,
    COL_1_NAME,
    COL_1_TERM,
    COL_2_NAME,
    COL_2_TERM,
    count
  };

  static constexpr auto widthNumber = 30;
  static constexpr auto widthName = 40;
  static constexpr auto widthTerm = 105;

  TableHeader() {
    AddColumn({ IXTRS_HEADER_NO, Alignment::center, widthNumber });
    AddColumn({ IXTRS_HEADER_NAME, Alignment::center, widthName });
    AddColumn({ IXTRS_HEADER_TERM, Alignment::center, widthTerm });
    AddColumn({ IXTRS_HEADER_NAME, Alignment::center, widthName });
    AddColumn({ IXTRS_HEADER_TERM, Alignment::center, widthTerm });
  }
};

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

IMPLEMENT_DYNCREATE(PictView, CFormView)

BEGIN_MESSAGE_MAP(PictView, CFormView)
  ON_COMMAND(IXTRC_XTR_HELP, &ThisClass::OpenHelp)

  ON_EN_CHANGE(IXTRC_TITLE, &ThisClass::OnChange)
  ON_EN_CHANGE(IXTRC_ALIAS, &ThisClass::OnChange)
  ON_EN_CHANGE(IXTRC_COMMENT, &ThisClass::OnChange)
  ON_EN_CHANGE(IXTRC_VIEW_OSS_NAME, &ThisClass::OnChange)

  ON_BN_CLICKED(IXTRC_OSS_COMMENT, &ThisClass::OnOSSComment)
  ON_BN_CLICKED(IXTRC_PICT_LINK, &ThisClass::OnPictLink)
  ON_BN_CLICKED(IXTRC_OSS_FILE, &ThisClass::OnOSSFileName)
  ON_BN_CLICKED(IXTRC_FILE_NAME, &ThisClass::OnPictFileName)
  ON_BN_CLICKED(IXTRC_OPEN_FOLDER, &ThisClass::OnOpenOSSFolder)
  ON_BN_CLICKED(IXTRC_BACKUP, &ThisClass::OnBackup)
END_MESSAGE_MAP()

#pragma warning( pop )

PictView::PictView()
  : CFormView(IXTRD_VIEW_PICT) {}

doc::OSSDoc& PictView::Document() noexcept {
  return dynamic_cast<doc::OSSDoc&>(*m_pDocument);
}

void PictView::OnInitialUpdate() {
  oss = &Document().OSS();

  CFormView::OnInitialUpdate();

  InitMapCtrl();
  InitCommentCtrl();
}

void PictView::InitMapCtrl() {
  static const TableHeader head{};
  optionsCtrl.SetExtendedStyle(optionsCtrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
  ui::InitHeaderFor(optionsCtrl, head);
}

void PictView::InitCommentCtrl() {
  commentCtrl.SetAutoURLDetect();
  commentCtrl.SetWindowTextW(CString{});
  commentCtrl.SetWindowTextW(comment);
  commentCtrl.InitRE(ui::Keyboard::Layout::RUS);
  commentCtrl.SetFont(&XTROptions::App().fontData);
}

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

  ::DDX_Control(pDX, IXTRC_STATUS, opStatusCtrl);
  ::DDX_Control(pDX, IXTRC_OPERATION, operationCtrl);
  ::DDX_Control(pDX, IXTRC_ALIAS, fullNameCtrl);
  ::DDX_Control(pDX, IXTRC_TITLE, shortNameCtrl);
  ::DDX_Control(pDX, IXTRC_COMMENT, commentCtrl);
  ::DDX_Control(pDX, IXTRC_VIEW_OSS_NAME, ossNameCtrl);
  ::DDX_Control(pDX, IXTRC_OTOGDESWLENYA, optionsCtrl);
  ::DDX_Control(pDX, IXTRC_FILE_NAME, changeFileBtn);

  if (!pDX->m_bSaveAndValidate) {
    LoadData();
  }
  ::DDX_Text(pDX, IXTRC_ALIAS, pictAlias);
  ::DDX_Text(pDX, IXTRC_TITLE, pictTitle);
  ::DDX_Text(pDX, IXTRC_COMMENT, comment);
  ::DDX_Text(pDX, IXTRC_OPERAND1, operand1);
  ::DDX_Text(pDX, IXTRC_OPERAND2, operand2);
  ::DDX_Text(pDX, IXTRC_VIEW_OSS_NAME, ossTitle);
  ::DDX_Text(pDX, IXTRC_STATUS, status);
  ::DDX_Text(pDX, IXTRC_OPERATION, opType);
  ::DDX_Text(pDX, IXTRC_SRC_TYPE, dataType);
  
  if (pDX->m_bSaveAndValidate) {
    SaveData();
  } else {
    UpdateUI();
  }
}

void PictView::LoadData() {
  const auto& pict = *(*oss)(Document().selection);

  pictAlias = mfc::ToMFC(pict.alias);
  pictTitle = mfc::ToMFC(pict.title);
  ossTitle = mfc::ToMFC(oss->title);
  comment = mfc::ToMFC(pict.comment);

  dataType = info::Label(oss->Src()(Document().selection)->desc.type);

  LoadOperation();  
}

void PictView::LoadOperation() {
  const auto selectedPict = Document().selection;
  if (const auto* operation = oss->Ops()(selectedPict); operation != nullptr) {
    status = info::Label(oss->Ops().StatusOf(selectedPict));
    opType = info::Label(operation->type);

    const auto parents = oss->Graph().ParentsOf(selectedPict);
    if (ssize(parents) == 2) {
      operand1 = mfc::ToMFC((*oss)(parents.at(0))->title);
      operand2 = mfc::ToMFC((*oss)(parents.at(1))->title);
    } else {
      operand1.Empty();
      operand2.Empty();
    }
  } else {
    status = mfc::LoadSID(IXTRS_PICT_BASIC);
    opType.Empty();
  }
}

void PictView::SaveData() {
  auto& doc = Document();
  const auto selectedPict = doc.selection;
  oss->SetPictAlias(selectedPict, mfc::ToSTL(pictAlias));
  oss->SetPictTitle(selectedPict, mfc::ToSTL(pictTitle));
  oss->SetPictComment(selectedPict, mfc::ToSTL(comment));
  oss->title = mfc::ToSTL(ossTitle);
  doc.UpdateAllViews(this);
}

void PictView::UpdateUI() {
  const auto* src = dynamic_cast<const CDocument*>(oss->Src()(Document().selection)->src);
  const auto hasOpenedDoc = src != nullptr && src->GetFirstViewPosition() != nullptr;

  fullNameCtrl.EnableWindow(!hasOpenedDoc);
  shortNameCtrl.EnableWindow(!hasOpenedDoc);
  commentCtrl.EnableWindow(!hasOpenedDoc);
  changeFileBtn.EnableWindow(!hasOpenedDoc && !std::empty(*oss->Src()(Document().selection)));

  UpdateMapCtrl(Document().selection);
}

void PictView::UpdateMapCtrl(const PictID pid) {
  static const CString errorStr{ L"TBA" };

  optionsCtrl.DeleteAllItems();
  if (const auto* handle = oss->Ops()(pid); handle == nullptr || handle->options == nullptr) {
    return;
  } else if (const auto* equations = dynamic_cast<const EquationOptions*>(handle->options.get()); equations == nullptr) {
    return;
  } else if (const auto parents = oss->Graph().ParentsOf(pid); std::empty(parents)) {
    return;
  } else {
    assert(ssize(parents) == 2);
    const auto* schema1 = ccl::src::CastData<RSForm>(oss->Src().DataFor(parents.at(0)));
    const auto* schema2 = ccl::src::CastData<RSForm>(oss->Src().DataFor(parents.at(1)));
    for (const auto& [key, value] : *equations) {
#pragma warning( push )
#pragma warning( disable : 6387 )
      const auto item = optionsCtrl.InsertItem(LVIF_TEXT | LVIF_STATE, optionsCtrl.GetItemCount(), {}, 0, 0, 0, 0);
#pragma warning( pop )

      CString number{};
      number.AppendFormat(LR"(%d)", item + 1);
      optionsCtrl.SetItemText(item, TableHeader::COL_NUMBER, number);
      if (schema1 != nullptr && schema1->Contains(key)) {
        const auto& cst1 = schema1->GetText(key);
        optionsCtrl.SetItemText(item, TableHeader::COL_1_NAME, mfc::ToMFC(cst1.alias));
        optionsCtrl.SetItemText(item, TableHeader::COL_1_TERM, mfc::ToMFC(cst1.term.Nominal()));
      } else {
        optionsCtrl.SetItemText(item, TableHeader::COL_1_NAME, errorStr);
        optionsCtrl.SetItemText(item, TableHeader::COL_1_TERM, errorStr);
      }
      if (schema2 != nullptr && schema2->Contains(value)) {
        const auto& cst2 = schema2->GetText(value);
        optionsCtrl.SetItemText(item, TableHeader::COL_2_NAME, mfc::ToMFC(cst2.alias));
        optionsCtrl.SetItemText(item, TableHeader::COL_2_TERM, mfc::ToMFC(cst2.term.Nominal()));
      } else {
        optionsCtrl.SetItemText(item, TableHeader::COL_2_NAME, errorStr);
        optionsCtrl.SetItemText(item, TableHeader::COL_2_TERM, errorStr);
      }
    }
  }
}

void PictView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) {
  CScrollView::OnUpdate(pSender, lHint, pHint);
  if (oss != nullptr && pSender != this) {
    UpdateData(FALSE);
  }
}

void PictView::OnChange() { // NOLINT(readability-convert-member-functions-to-static)
  UpdateData(TRUE);
}

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

void PictView::OnOSSComment() { // NOLINT(readability-convert-member-functions-to-static)
  proc::EditComment(Document());
}

void PictView::OnPictLink() { // NOLINT(readability-convert-member-functions-to-static)
  proc::EditSelectedHyperlink(Document());
}

void PictView::OnOSSFileName() { // NOLINT(readability-convert-member-functions-to-static)
  proc::EditOSSFilename(Document());
}

void PictView::OnPictFileName() { // NOLINT(readability-convert-member-functions-to-static)
  proc::EditActiveFilename(Document());
}

void PictView::OnOpenOSSFolder() { // NOLINT(readability-convert-member-functions-to-static)
  ::ShellExecuteW(nullptr, L"open",
                mfc::ToMFC(Document().OSS().Src().ossDomain),
                nullptr, nullptr, SW_SHOW);
}

void PictView::OnBackup() { // NOLINT(readability-convert-member-functions-to-static)
  proc::BackupOSS(Document());
}

} // namespace xtr::view