#pragma once

#include "xtr/core/rslIndex.hpp"
#include "xtr/io/OptionsIO.hpp"

namespace xtr {

//! Abstract options section
class OptionsSection {
  uint32_t sectionID;
  CString registrySection{};
  CWinAppEx* application{ nullptr };

public:
  virtual ~OptionsSection() = default;
  OptionsSection(const OptionsSection&) = delete;
  OptionsSection& operator=(const OptionsSection&) = delete;
  OptionsSection(OptionsSection&&) noexcept = default;
  OptionsSection& operator=(OptionsSection&&) noexcept = default;

  explicit OptionsSection(uint32_t sectionID) noexcept
    : sectionID{ sectionID } {}

public:
  virtual void InitDefault() = 0;
  virtual void LoadTable() = 0;
  virtual BOOL SaveTable() = 0;
  virtual BOOL CreateTable(CWinAppEx *pWinApp);

protected:
  using EntryID = uint32_t;
  BOOL Save(EntryID entry, int32_t value);
  BOOL Save(EntryID entry, CString value);

  void Load(EntryID entry, int32_t& output) const;
  void Load(EntryID entry, CString& output) const;

  CString LoadHKLMRegistry(CString folder, CString keyName, CString defaultValue);

  template <typename Item, typename Object>
  BOOL SaveAs(EntryID entry, const Object& obj) {
    return Save(entry, static_cast<Item>(obj));
  }
  template<typename Item, typename Object>
  void LoadAs(EntryID entry, Object& output) {
    Item result = static_cast<Item>(output);
    Load(entry, result);
    output = static_cast<Object>(result);
  }
};

//! Application options
class GeneralOptions final : public OptionsSection {
public:
  using SilentGuard = ccl::types::CounterGuard<uint32_t>;

  CString pathSaveAs;        // полный путь директории сохранения
  CString pythonPath;        // Путь к Python

  BOOL promptVersion;        // Показывать сообщение при загрузка старых файлов
  BOOL showStartPage;        // Отображать стартовую страницу
  BOOL disableAutoKeyboard;  // Отключить автоматическое переключючение раскладки

  BOOL enablePython;        // Загружать Python Module
  BOOL pyCrashed;            // Флаг креша Python

  CFont fontMath;            // Математический шрифт (DejaVu Sans)
  CFont fontMathComment;    // Математический шрифт (DejaVu Sans) для комментов
  CFont fontMathTT;          // Математический шрифт (DejaVu Sans) для подсказок
  CFont fontTable;          // Математический шрифт (DejaVu Sans) для таблиц
  CFont fontData;            // Математический шрифт (DejaVu Sans) для данных
  CFont fontTooltip;        // Шрифт для всплывающих подсказок
  CFont fontText;            // Стандартный шрифт для текста

private:
  uint32_t silentCount{ 0 };

public:
  GeneralOptions();

public:
  void InitDefault() override;
  void LoadTable() override;
  BOOL SaveTable() override;

  [[nodiscard]] BOOL IsSilent() const noexcept {
    return silentCount > 0;
  }
  [[nodiscard]] SilentGuard GuardSilence() noexcept {
    return SilentGuard(silentCount);
  }

private:
  BOOL CreateFonts();
  static BOOL InitializeFont(CFont& font, CString name, uint32_t size);
};

//! RSForm options
struct RSFormOptions final : OptionsSection {
  BOOL inlineFunctionOnGenerate; // Генерировать полное выражение терма из терм-функции

  int32_t nameWidth; // Ширина колонки с именем
  int32_t typeWidth; // Ширина колонки с типизацией
  int32_t expWidth; // Ширина колонки с формальным выражением
  int32_t termWidth; // Ширина колонки с термином выражением
  int32_t interWidth; // Ширина колонки с интерпретацией
  int32_t conventionWidth; // Ширина колонки с комментарием

  int32_t splitterPos; // Положение сплиттера

  io::SchemaOptions exportOptions;

public:
  RSFormOptions();

public:
  void ResetTableWidth() noexcept;
  void InitDefault() noexcept override;
  void LoadTable() override;
  BOOL SaveTable() override;
};

//! OSS options
struct ossOptions final : OptionsSection {
  static constexpr auto zoomMin = 1U;
  static constexpr auto zoomMax = 3U;

  int32_t zoom; // коэфициент уменьшения 1, 2, 3
  int32_t splitterPos; // Положение сплиттера

  BOOL runOpAfterEdit;
  BOOL showGrid;
  BOOL showLegend;

public:
  ossOptions();

public:
  void InitDefault() noexcept override;
  void LoadTable() override;
  BOOL SaveTable() override;
};

//! RS parsing options
struct ParsingOptions final : OptionsSection {
  BOOL autoCheck;    // Проверять ФВ при изменении
  BOOL colorBrackets;    // Форматировать скобки цветом по уровню
  BOOL showAST;      // Отображать дерево разбора
  BOOL enableNavigator;    // Навигация по ФВ
  index::IndexingParams indexing; // Параметры индексации

public:
  ParsingOptions();

public:
  void InitDefault() noexcept override;
  void LoadTable() override;
  BOOL SaveTable() override;
};

//! RSModel options
struct ModelOptions final : OptionsSection {
  BOOL m_DataMode; // Режим отображения данных интерпретации

  int32_t nameWidth;    // Ширина колонки с именем
  int32_t typeWidth;    // Ширина колонки с типизацией
  int32_t expWidth;      // Ширина колонки с формальным выражением
  int32_t valWidth;      // Ширина колонки с мощностью интерпретации
  int32_t termWidth;    // Ширина колонки с термином выражением
  int32_t interWidth;    // Ширина колонки с интерпретацией
  int32_t conventionWidth;    // Ширина колонки с комментарием

  int32_t splitterPos;    // Положение сплиттера

public:
  ModelOptions();

public:
  void ResetTableWidth() noexcept;
  void InitDefault() noexcept override;
  void LoadTable() override;
  BOOL SaveTable() override;
};

//! Text processing options
struct TextOptions final : OptionsSection {
  BOOL editReference;

public:
  TextOptions();

public:
  void InitDefault() noexcept override;
  void LoadTable() override;
  BOOL SaveTable() override;
};

//! Abstract options object
struct ApplicationOptions : CObject {
  virtual BOOL LoadParameters(CWinAppEx* app) = 0;
  virtual BOOL SaveParameters() = 0;

  BOOL Load(CWinAppEx* pApp);
  BOOL Save();
};

//! Exteor options
struct XTROptions final : ApplicationOptions {
  RSFormOptions schema;
  ossOptions oss;
  GeneralOptions general;
  ParsingOptions parsing;
  ModelOptions model;
  TextOptions texting;

private:
  XTROptions() = default;

public:
  [[nodiscard]] static XTROptions& Get() noexcept;
  [[nodiscard]] static RSFormOptions& RS() noexcept { return Get().schema; }
  [[nodiscard]] static ossOptions& OSS() noexcept { return Get().oss; }
  [[nodiscard]] static GeneralOptions& App() noexcept { return Get().general; }
  [[nodiscard]] static ParsingOptions& ParseRSL() noexcept { return Get().parsing; }
  [[nodiscard]] static ModelOptions& Model() noexcept { return Get().model; }
  [[nodiscard]] static TextOptions& Text() noexcept { return Get().texting; }

public:
  static void SaveWindowPlacement(const CWnd& wnd, CString entryName);
  static BOOL LoadWindowPlacement(CWnd& wnd, CString entryName);
  BOOL LoadParameters(CWinAppEx *pApp) override;
  BOOL SaveParameters() override;
};

} // namespace xtr