#include "stdafx.h"

#include "xtr/ExteorOptions.h"

#include "resource.h"

namespace xtr {

#pragma warning( push )
#pragma warning( disable : 26495 ) // Do not warn about uninitialized variables in options constructors

BOOL OptionsSection::CreateTable(CWinAppEx* pWinApp) {
  registrySection = mfc::LoadSID(sectionID);
  application = pWinApp;
  InitDefault();
  LoadTable();
  return TRUE;
}

BOOL OptionsSection::Save(EntryID entry, int32_t value) {
  return application->WriteProfileInt(registrySection, mfc::LoadSID(entry), value);
}

BOOL OptionsSection::Save(EntryID entry, const CString value) {
  return application->WriteProfileStringW(registrySection, mfc::LoadSID(entry), value);
}

void OptionsSection::Load(EntryID entry, int32_t& output) const {
  output = application->GetProfileIntW(registrySection, mfc::LoadSID(entry), output);
}

void OptionsSection::Load(EntryID entry, CString& output) const {
  output = application->GetProfileStringW(registrySection, mfc::LoadSID(entry), output);
}

CString OptionsSection::LoadHKLMRegistry(const CString folder, const CString keyName, const CString defaultValue) {
  HKEY key;
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, folder, 0, KEY_READ | KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) {
    return defaultValue;
  }
  DWORD type, size;
  wchar_t data[1024];
  size = sizeof(data);
  if (RegQueryValueEx(key, keyName, 0, &type, (BYTE*)data, &size) != ERROR_SUCCESS) {
    return defaultValue;
  }
  const CString result(data);
  RegCloseKey(key);
  if (result.IsEmpty()) {
    return defaultValue;
  } else {
    return result;
  }
}

BOOL ApplicationOptions::Load(CWinAppEx* pApp) {
  return LoadParameters(pApp);
}

BOOL ApplicationOptions::Save() {
  return SaveParameters();
}

XTROptions& XTROptions::Get() noexcept {
  static XTROptions options{};
  return options;
}

void XTROptions::SaveWindowPlacement(const CWnd& wnd, const CString entryName) {
  WINDOWPLACEMENT wp{};
  wnd.GetWindowPlacement(&wp);
  ::AfxGetApp()->WriteProfileBinary(L"WindowPosition", entryName, reinterpret_cast<LPBYTE>(&wp), sizeof(wp));
}

// do not warn about pos object. See GetProfileBinary docs
#pragma warning( push )
#pragma warning( disable : 26494 26401 )

BOOL XTROptions::LoadWindowPlacement(CWnd& wnd, const CString entryName) {
  WINDOWPLACEMENT* pos{};
  auto size = 0U;
  if (!::AfxGetApp()->GetProfileBinary(L"WindowPosition", entryName, reinterpret_cast<LPBYTE*>(&pos), &size)) {
    return FALSE;
  } else {
    wnd.SetWindowPlacement(pos);
    delete[] pos; // NOLINT(cppcoreguidelines-owning-memory)
    return TRUE;
  }
}

#pragma warning( pop )

BOOL XTROptions::LoadParameters(CWinAppEx* pApp) {
  auto flag = true;

  flag = flag && schema.CreateTable(pApp);

  flag = flag && oss.CreateTable(pApp);
  flag = flag && general.CreateTable(pApp);
  flag = flag && parsing.CreateTable(pApp);
  flag = flag && model.CreateTable(pApp);
  flag = flag && texting.CreateTable(pApp);

  return flag;
}

BOOL XTROptions::SaveParameters() {
  auto flag = true;

  flag = oss.SaveTable() && flag;
  flag = general.SaveTable() && flag;
  flag = parsing.SaveTable() && flag;
  flag = model.SaveTable() && flag;
  flag = texting.SaveTable() && flag;

  flag = schema.SaveTable() && flag;

  return flag;
}

GeneralOptions::GeneralOptions() // NOLINT(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
  : OptionsSection(IXTRO_SECTION_APPLICATION) {}

void GeneralOptions::InitDefault() {
  pathSaveAs = mfc::LoadSID(IXTRS_DEFAULT_CS_OSS_DIR);

  promptVersion = TRUE;
  showStartPage = FALSE;
  disableAutoKeyboard = FALSE;

  enablePython = TRUE;
  pyCrashed = FALSE;
#ifndef _WIN64
  pythonPath = LoadHKLMRegistry(LR"(SOFTWARE\Python\PythonCore\3.12-32\InstallPath)", L"", LR"(C:\Program Files (x86)\Python312-32)");
#else
  pythonPath = LoadHKLMRegistry(LR"(SOFTWARE\Python\PythonCore\3.12\InstallPath)", L"", LR"(C:\Program Files\Python312)");
#endif
  CreateFonts();
}

void GeneralOptions::LoadTable() {
  Load(IXTRO_SAVEAS_PATH, pathSaveAs);
#ifndef _WIN64
  Load(IXTRO_PYPATH32, pythonPath);
#else
  Load(IXTRO_PYPATH64, pythonPath);
#endif
  Load(IXTRO_START_PAGE, showStartPage);

  Load(IXTRO_PYSTART, enablePython);
  Load(IXTRO_DISABLE_KEYBOARD, disableAutoKeyboard);
  Load(IXTRO_PYCRASH, pyCrashed);
}

BOOL GeneralOptions::SaveTable() {
  auto flag = true;

  flag = Save(IXTRO_SAVEAS_PATH, pathSaveAs) && flag;
#ifndef _WIN64
  flag = Save(IXTRO_PYPATH32, pythonPath) && flag;
#else
  flag = Save(IXTRO_PYPATH64, pythonPath) && flag;
#endif

  flag = Save(IXTRO_START_PAGE, showStartPage) && flag;
  flag = Save(IXTRO_PYSTART, enablePython) && flag;
  flag = Save(IXTRO_DISABLE_KEYBOARD, disableAutoKeyboard) && flag;
  flag = Save(IXTRO_PYCRASH, pyCrashed) && flag;

  return flag;
}

BOOL GeneralOptions::CreateFonts() {
  static const CString mathFont = L"DejaVu Sans";
  static const CString textFont = L"Times New Roman";
  static const CString defaultFontFile = L"DejaVu Sans.ttf";

  static constexpr auto mathSize = 20U;
  static constexpr auto mathSizeCC = 18U;
  static constexpr auto mathSizeTT = 16U;
  static constexpr auto tableSize = 20U;
  static constexpr auto plainTextSize = 18U;
  static constexpr auto tooltipSize = 16U;
  static constexpr auto dataSize = 14U;

  ::AddFontResourceExW(defaultFontFile, FR_PRIVATE, nullptr);

  if (InitializeFont(fontMath, mathFont, mathSize) &&
      InitializeFont(fontMathComment, mathFont, mathSizeCC) &&
      InitializeFont(fontMathTT, mathFont, mathSizeTT) &&
      InitializeFont(fontTooltip, mathFont, tooltipSize) &&
      InitializeFont(fontText, textFont, plainTextSize) &&
      InitializeFont(fontTable, mathFont, tableSize) &&
      InitializeFont(fontData, mathFont, dataSize)) {
    return TRUE;
  } else {
    ::AfxMessageBox(IXTRE_CANTLOADFONT, MB_ICONERROR);  
    return FALSE;  
  }
}

#pragma warning( push )
#pragma warning( disable : 26485 ) // ignore array to pointer decay

BOOL GeneralOptions::InitializeFont(CFont& font, const CString name, const uint32_t size) {
  LOGFONT lfInit{};
  ::memset(&lfInit, 0, sizeof(LOGFONT));
  lfInit.lfHeight = size;

  ::wcscpy_s(lfInit.lfFaceName, static_cast<rsize_t>(name.GetLength()) + 1, name); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay, hicpp-no-array-decay)
  return font.CreateFontIndirectW(&lfInit);
}

#pragma warning( pop )

RSFormOptions::RSFormOptions()  // NOLINT(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
  : OptionsSection(IXTRO_SECTION_TRSVIEW) {}

void RSFormOptions::InitDefault() noexcept {
  exportOptions.bTerm = TRUE;
  exportOptions.bRSDef = TRUE;
  exportOptions.bTextDef = TRUE;
  exportOptions.bComment = TRUE;
  exportOptions.bTyping = FALSE;

  splitterPos = 200; // NOLINT

  ResetTableWidth();
}

void RSFormOptions::ResetTableWidth() noexcept {
  nameWidth = 70; // NOLINT
  typeWidth = 150; // NOLINT
  expWidth = 250; // NOLINT
  termWidth = 150; // NOLINT
  interWidth = 220; // NOLINT
  conventionWidth = 220; // NOLINT
}

void RSFormOptions::LoadTable() {
  Load(IXTRO_TRSV_NAME_WIDTH, nameWidth);
  Load(IXTRO_TRSV_TYPE_WIDTH, typeWidth);
  Load(IXTRO_TRSV_EXP_WIDTH, expWidth);
  Load(IXTRO_TRSV_TERM_WIDTH, termWidth);
  Load(IXTRO_TRSV_INTER_WIDTH, interWidth);
  Load(IXTRO_TRSV_COMMENT_WIDTH, conventionWidth);

  Load(IXTRO_TRSV_EXPT_TERM, exportOptions.bTerm);
  Load(IXTRO_TRSV_EXPT_FORM, exportOptions.bRSDef);
  Load(IXTRO_TRSV_EXPT_INTER, exportOptions.bTextDef);
  Load(IXTRO_TRSV_EXPT_COM, exportOptions.bComment);
  Load(IXTRO_TRSV_EXPT_TYPE, exportOptions.bTyping);

  Load(IXTRO_TRSV_SPLITTER_POS, splitterPos);
}

BOOL RSFormOptions::SaveTable() {
  auto flag = true;
  flag = Save(IXTRO_TRSV_NAME_WIDTH, nameWidth) && flag;
  flag = Save(IXTRO_TRSV_TYPE_WIDTH, typeWidth) && flag;
  flag = Save(IXTRO_TRSV_EXP_WIDTH, expWidth) && flag;
  flag = Save(IXTRO_TRSV_TERM_WIDTH, termWidth) && flag;
  flag = Save(IXTRO_TRSV_INTER_WIDTH, interWidth) && flag;
  flag = Save(IXTRO_TRSV_COMMENT_WIDTH, conventionWidth) && flag;

  flag = Save(IXTRO_TRSV_EXPT_TERM, exportOptions.bTerm) && flag;
  flag = Save(IXTRO_TRSV_EXPT_FORM, exportOptions.bRSDef) && flag;
  flag = Save(IXTRO_TRSV_EXPT_INTER, exportOptions.bTextDef) && flag;
  flag = Save(IXTRO_TRSV_EXPT_COM, exportOptions.bComment) && flag;
  flag = Save(IXTRO_TRSV_EXPT_TYPE, exportOptions.bTyping && flag);

  flag = Save(IXTRO_TRSV_SPLITTER_POS, splitterPos) && flag;
  return flag;
}

ossOptions::ossOptions() // NOLINT(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
  : OptionsSection(IXTRO_SECTION_OSS) {}

void ossOptions::InitDefault() noexcept {
  zoom = 1;
  splitterPos = 300; // NOLINT
  runOpAfterEdit = TRUE;
  showGrid = TRUE;
  showLegend = TRUE;
}

void ossOptions::LoadTable() {
  Load(IXTRO_ZOOM_VISIBLE, zoom);
  Load(IXTRO_OSS_SPLITTER, splitterPos);
  Load(IXTRO_RUN_AFTER_OK, runOpAfterEdit);
  Load(IXTRO_OSS_GRID, showGrid);
  Load(IXTRO_OSS_LEGEND, showLegend);
}

BOOL ossOptions::SaveTable() {
  auto flag = true;
  flag = Save(IXTRO_ZOOM_VISIBLE, zoom) && flag;
  flag = Save(IXTRO_OSS_SPLITTER, splitterPos) && flag;
  flag = Save(IXTRO_RUN_AFTER_OK, runOpAfterEdit) && flag;
  flag = Save(IXTRO_OSS_GRID, showGrid) && flag;
  flag = Save(IXTRO_OSS_LEGEND, showLegend) && flag;
  return flag;
}

ParsingOptions::ParsingOptions() // NOLINT(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
  : OptionsSection(IXTRO_SECTION_ANALYZER) {}

void ParsingOptions::InitDefault() noexcept {
  autoCheck = FALSE;
  colorBrackets = TRUE;
  showAST = TRUE;
  enableNavigator = TRUE;
  indexing.global = index::Digits::subText;
  indexing.local = index::Digits::subText;
  indexing.operators = index::Digits::subText;
}

void ParsingOptions::LoadTable() {
  Load(IXTRO_CST_AUTOCHECK, autoCheck);
  Load(IXTRO_SA_COLORPAR, colorBrackets);
  Load(IXTRO_SA_AST, showAST);
  Load(IXTRO_SA_NAVIGATOR, enableNavigator);
  LoadAs<int32_t>(IXTRO_RSFORM_INDEXCST, indexing.global);
  LoadAs<int32_t>(IXTRO_RSFORM_INDEXLOCAL, indexing.local);
  LoadAs<int32_t>(IXTRO_RSFORM_INDEXPR, indexing.operators);
}

BOOL ParsingOptions::SaveTable() {
  auto flag = true;
  flag = Save(IXTRO_CST_AUTOCHECK, autoCheck) && flag;
  flag = Save(IXTRO_SA_COLORPAR, colorBrackets) && flag;
  flag = Save(IXTRO_SA_AST, showAST) && flag;
  flag = Save(IXTRO_SA_NAVIGATOR, enableNavigator) && flag;
  flag = SaveAs<int32_t>(IXTRO_RSFORM_INDEXCST, indexing.global) && flag;
  flag = SaveAs<int32_t>(IXTRO_RSFORM_INDEXLOCAL, indexing.local) && flag;
  flag = SaveAs<int32_t>(IXTRO_RSFORM_INDEXPR, indexing.operators) && flag;
  return flag;
}

ModelOptions::ModelOptions() // NOLINT(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
  : OptionsSection(IXTRO_SECTION_MODEL) {}

void ModelOptions::InitDefault() noexcept {
  m_DataMode = TRUE;
  splitterPos = 200; // NOLINT
  ResetTableWidth();
}

void ModelOptions::ResetTableWidth() noexcept {
  nameWidth = 70; // NOLINT
  typeWidth = 150; // NOLINT
  expWidth = 220; // NOLINT
  valWidth = 60; // NOLINT
  termWidth = 150; // NOLINT
  interWidth = 220; // NOLINT
  conventionWidth = 220; // NOLINT
}

void ModelOptions::LoadTable() {
  Load(IXTRO_TRSV_DATAMODE, m_DataMode);
  Load(IXTRO_TRSV_NAME_WIDTH, nameWidth);
  Load(IXTRO_TRSV_TYPE_WIDTH, typeWidth);
  Load(IXTRO_TRSV_EXP_WIDTH, expWidth);
  Load(IXTRO_RSM_VAL_WIDTH, valWidth);
  Load(IXTRO_TRSV_TERM_WIDTH, termWidth);
  Load(IXTRO_TRSV_INTER_WIDTH, interWidth);
  Load(IXTRO_TRSV_COMMENT_WIDTH, conventionWidth);
  Load(IXTRO_TRSV_SPLITTER_POS, splitterPos);
}

BOOL ModelOptions::SaveTable() {
  BOOL flag = TRUE;
  flag = Save(IXTRO_TRSV_DATAMODE, m_DataMode) && flag;
  flag = Save(IXTRO_TRSV_NAME_WIDTH, nameWidth) && flag;
  flag = Save(IXTRO_TRSV_TYPE_WIDTH, typeWidth) && flag;
  flag = Save(IXTRO_TRSV_EXP_WIDTH, expWidth) && flag;
  flag = Save(IXTRO_RSM_VAL_WIDTH, valWidth) && flag;
  flag = Save(IXTRO_TRSV_INTER_WIDTH, interWidth) && flag;
  flag = Save(IXTRO_TRSV_TERM_WIDTH, termWidth) && flag;
  flag = Save(IXTRO_TRSV_COMMENT_WIDTH, conventionWidth) && flag;
  flag = Save(IXTRO_TRSV_SPLITTER_POS, splitterPos) && flag;
  return flag;
}

TextOptions::TextOptions() // NOLINT(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
  : OptionsSection(IXTRO_SECTION_TEXT) {}

void TextOptions::InitDefault() noexcept {
  editReference = FALSE;
}

void TextOptions::LoadTable() {
  Load(IXTRO_TXT_EDITREF, editReference);
}

BOOL TextOptions::SaveTable() {
  auto flag = true;

  flag = flag && Save(IXTRO_TXT_EDITREF, editReference);

  return flag;
}

#pragma warning( pop )

} // namespace xtr