#pragma once

#include "xtr/viewmodel/vmMeta.hpp"
#include "xtr/ui/KeyboardHandler.h"

#include "tom.h"

namespace xtr::ui {

//! Extended rich edit control
/*
  * Context aware tooltip
  * Formatting items
  * StrRange based API
*/
class BuffRichEdit : public CRichEditCtrl, public viewmodel::IFreezable {
protected:
  struct FormatItem  {
    StrRange cr{};
    CHARFORMAT2W format{};
  };

protected:
  BOOL readOnly{ FALSE };
  BOOL switchLayoutOnCtrl{ FALSE };
  std::vector<FormatItem> formatItems{};

  CString defaultToolTipText{};
  Keyboard::Layout defaultKL{ Keyboard::Layout::ENG };

private:
  BOOL enableTooltip{ FALSE };
  CToolTipCtrl tooltips;

  BOOL enableFormat{ FALSE };
  BOOL alreadyUpdating{ FALSE };
  StrPos cursorFormatPos{ 0 };

  mutable ITextDocument* textInterface{nullptr};
  mutable uint32_t eventOptions{};

  std::unique_ptr<long> freezeUID{nullptr};
  int32_t freezeCount{0};
  StrRange storedSelection{};

public:
  CString Text() const;
  void InitRE(Keyboard::Layout kl = Keyboard::Layout::ENG,
              BOOL enableFormat = FALSE,
              BOOL enableTooltip = FALSE);
  void UpdateFormat(BOOL ignoreSelection = FALSE);

  void SetReadOnly(BOOL readOnlyFlag);
  void LockInput();
  void UnlockInput();

  CPoint GetSubscriptDialogPosition() const;
  StrRange GetSelectionRange() const;

  CString FixSelection();
  void MoveCaretTo(StrPos position);
  void SelectRange(StrRange range);
  void SelectChar(StrPos position);
  void SelectAll();

  CHARFORMAT2W GetSelectionFormat() const;
  CHARFORMAT2W DefaultFormat() const;
  void SelectAndFormat(StrRange range, CHARFORMAT2W format);

protected:
  BOOL PreTranslateMessage(MSG* pMsg) override;

DECLARE_MESSAGE_MAP()
  afx_msg void OnKeyDown(uint32_t nChar, uint32_t nRepCnt, uint32_t nFlags);
  afx_msg void OnKeyUp(uint32_t nChar, uint32_t nRepCnt, uint32_t nFlags);
  afx_msg BOOL OnSetCursor(CWnd* pWnd, uint32_t nHitTest, uint32_t message);

  afx_msg BOOL OnTextChange();
  afx_msg BOOL OnSelChange(NMHDR* pNotifyStruct , LRESULT* result);
  afx_msg void OnSetFocus(CWnd* pOldWnd);
  afx_msg void OnKillFocus(CWnd* pOldWnd);

  afx_msg BOOL OnEnLink(NMHDR* pNMHDR, LRESULT* pResult);

protected:
  void InsertChar(uint32_t ch);
  void InsertString(CString str);
  StrRange ExpandRange(StrRange cr) const;
  void UpdateBackground();

  [[nodiscard]] BOOL IsFrozen() const noexcept { return freezeUID == nullptr; }
  void FreezeUI() override;
  void UnFreezeUI() override;
  void UnFreezeUI(StrRange newSelection);

private:

#pragma warning( push )
#pragma warning( disable : 26440 ) // do not warn about virtual functions noexcept

  virtual void UpdateFormatItems() {}
  virtual void AdditionalFormat() {}
  virtual CString GenerateToolTipText() { return CString{}; }

#pragma warning( pop )

  void DeepCopy();
  void ResetFormatting();
  void ApplyFormatTokens();
  void InitToolTips();
  ITextDocument* GetFixedTextDocumentInterface() const;
};

} // namespace xtr::ui