#include "stdafx.h"

#include "xtr/io/RSForm2DOCX.h"

#include "resource.h"

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

namespace xtr::io {

namespace {

struct WordHeader {
  enum Columns : uint8_t {
    POS_NAME = 1,
    POS_EXPR,
    POS_TYPIFICATION,
    POS_TERM,
    POS_INTERP
  };
};

void ApplyParamsTo(MSO::Word::WTable& outTable, const SchemaOptions& params) {
  static constexpr float PREFERED_WIDHT = 100;
  if (!params.bTextDef) {
    outTable.get_Columns()[WordHeader::POS_INTERP].Delete();
  }
  if (!params.bTerm) {
    outTable.get_Columns()[WordHeader::POS_TERM].Delete();
  }
  if (!params.bTyping) {
    outTable.get_Columns()[WordHeader::POS_TYPIFICATION].Delete();
  }
  if (!params.bRSDef) {
    outTable.get_Columns()[WordHeader::POS_EXPR].Delete();
  }
  outTable.put_PreferredWidth(PREFERED_WIDHT);
}

} // anonymous namespace

BOOL RSForm2DOCX::Export(const SchemaOptions& exportParams, const CString fileName) {
  params = exportParams;
  try {
    if (!LoadApplication() ||
        !CreateDocument() ||
        !WriteContent()) {
      return FALSE;
    } else {
      if (!fileName.IsEmpty()) {
        SaveFile(fileName);
      }
      return TRUE;
    }
  } catch (_com_error& /*er*/) {
    return FALSE;
  }
}

BOOL RSForm2DOCX::LoadApplication() {
  if (!wordApplication.CreateDispatch(LR"(Word.Application)")) {
    ::AfxMessageBox(IXTRE_WORD_APPL, MB_ICONERROR);
    return FALSE;
  } else {
    wordApplication.put_Visible(TRUE);
    wordApplication.Activate();
    docs = MSO::Word::WDocuments{ wordApplication.get_Documents() };
    return TRUE;
  }
}

BOOL RSForm2DOCX::CreateDocument() {
  if (const auto maybePath = GetPathToTemplate(); maybePath.has_value()) {
    COleVariant temapltePath{ maybePath.value() };
    workingDocument = docs.Add(&temapltePath, &MSO::ovOptional, &MSO::ovOptional, &MSO::ovTrue);
    return TRUE;
  } else {
    return FALSE;
  }
}

std::optional<CString> RSForm2DOCX::GetPathToTemplate() {
  const auto pathToTemplate = g_Exteor.GetModuleFolder() + mfc::LoadSID(IXTRS_PATH_RSDOC_TEMPLATE);
  if (!::PathFileExistsW(pathToTemplate)) {
    ::AfxMessageBox(mfc::FormatSID(IXTRE_TEMPLATE_NOT_FOUND, pathToTemplate.GetString()), MB_ICONERROR);
    return std::nullopt;
  } else {
    return pathToTemplate;
  }
}

BOOL RSForm2DOCX::WriteContent() {
  static constexpr auto rowStart = 2;
  if (!WriteHeaderFooter()) {
    return FALSE;
  } else {
    auto outTable = workingDocument.get_Tables()[1];
    auto nRow = rowStart;
    for (const auto uid : source.List()) {
      outTable.get_Rows().Add(&vtMissing);
      WriteConstituenta(uid, outTable, nRow);
      ++nRow;
    }
    outTable.get_Rows()[outTable.get_Rows().get_Count()].Delete();
    ApplyParamsTo(outTable, params);
    return TRUE;
  }
}

BOOL RSForm2DOCX::WriteHeaderFooter() {
  return PutTextIntoBookmark(L"ToName", mfc::ToMFC(source.title))  
    && PutTextIntoBookmark(L"ToShortName", mfc::ToMFC(source.alias));
}

BOOL RSForm2DOCX::PutTextIntoBookmark(const CString bookmarkName, const CString text) {
  auto bookmarks = workingDocument.get_Bookmarks();
  try {
    auto namedRange = bookmarks[bookmarkName].get_Range();
    namedRange.put_Text(text);
    return TRUE;
  }
  catch (_com_error& /*er*/) {
    ::AfxMessageBox(mfc::FormatSID(IXTRE_WORD_BOOKMARK_MISSING, bookmarkName.GetString()), MB_ICONEXCLAMATION);
    return FALSE;
  }
}

void RSForm2DOCX::WriteConstituenta(const EntityUID target,
                                    MSO::Word::WTable& outTable, const long nRow) {
  const auto& rsCst = source.GetRS(target);
  const auto& textCst = source.GetText(target);

  const auto cstname = mfc::ToMFC(rsCst.alias);
  const auto expression = mfc::ToMFC(rsCst.definition);
  const auto typification = info::Label(source.GetParse(target).TypePtr());
  const auto convention = mfc::ToMFC(rsCst.convention);
  const auto interpret = mfc::ToMFC(textCst.definition.Str());
  const auto term = mfc::ToMFC(textCst.term.Nominal());

  outTable.Cell(nRow, WordHeader::POS_NAME).get_Range().InsertAfter(cstname);
  outTable.Cell(nRow, WordHeader::POS_EXPR).get_Range().InsertAfter(expression);
  outTable.Cell(nRow, WordHeader::POS_TYPIFICATION).get_Range().InsertAfter(typification);
  outTable.Cell(nRow, WordHeader::POS_TERM).get_Range().InsertAfter(term);
  if (!interpret.IsEmpty() && params.bTextDef) {
    outTable.Cell(nRow, WordHeader::POS_INTERP).get_Range().InsertAfter(interpret);
  }
  if (!convention.IsEmpty() && params.bComment) {
    if (ccl::semantic::IsBaseNotion(rsCst.type)) {
      if (!interpret.IsEmpty()) {
        outTable.Cell(nRow, WordHeader::POS_INTERP).get_Range().InsertAfter(L"\n");
      }
      outTable.Cell(nRow, WordHeader::POS_INTERP).get_Range().InsertAfter(L"Конвенция: ");
      outTable.Cell(nRow, WordHeader::POS_INTERP).get_Range().InsertAfter(convention);
    } else {
      auto nameCellRange = outTable.Cell(nRow, WordHeader::POS_NAME).get_Range();
      nameCellRange.get_Comments().Add(nameCellRange, COleVariant{ convention });
    }
  }
}

void RSForm2DOCX::SaveFile(const CString fileName) {
  COleVariant vFile{ fileName };
  workingDocument.SaveAs(&vFile, &vtMissing, &vtMissing,
    &vtMissing, &vtMissing, &vtMissing, &vtMissing,
    &vtMissing, &vtMissing, &vtMissing,
    &vtMissing, &vtMissing, &MSO::ovTrue,
    &vtMissing, &vtMissing, &vtMissing);
}

} // namespace xtr::io