#pragma once

#include "RE/B/BSFixedString.h"
#include "RE/B/BSPointerHandle.h"
#include "RE/B/BSSimpleList.h"
#include "RE/B/BSString.h"
#include "RE/B/BSTEvent.h"
#include "RE/B/BSTSingleton.h"

#include "REX/W32/BASE.h"

namespace RE
{
	class BGSDialogueBranch;
	class DialogueResponse;
	class MenuOpenCloseEvent;
	class TESQuest;
	class TESTopic;
	class TESTopicInfo;
	struct PositionPlayerEvent;

	class MenuTopicManager :
		public BSTSingletonSDM<MenuTopicManager>,  // 0x10
		public BSTEventSink<MenuOpenCloseEvent>,   // 0x00
		public BSTEventSink<PositionPlayerEvent>   // 0x08
	{
	public:
		inline static constexpr auto RTTI = RTTI_MenuTopicManager;
		inline static constexpr auto VTABLE = VTABLE_MenuTopicManager;

		struct Dialogue
		{
		public:
			// members
			BSString                               topicText;        // 00
			bool                                   unk10;            // 10
			bool                                   unk11;            // 11
			bool                                   unk12;            // 12 - data.topic->formID == 0xFD || data.topic->formID == 0x118
			std::uint8_t                           unk13;            // 13
			std::uint32_t                          unk14;            // 14
			BSSimpleList<DialogueResponse*>        responses;        // 18
			TESQuest*                              parentQuest;      // 28
			TESTopicInfo*                          parentTopicInfo;  // 30
			TESTopic*                              parentTopic;      // 38
			BSSimpleList<DialogueResponse*>::Node* currentResponse;  // 40
			std::uint8_t                           unk48;            // 48
			bool                                   neverSaid;        // 49
			std::uint16_t                          unk4A;            // 4A
			std::uint32_t                          unk4C;            // 4C
			TESTopic*                              unk50;            // 50
		};
		static_assert(sizeof(Dialogue) == 0x58);

		~MenuTopicManager() override;  // 00

		// override (BSTEventSink<MenuOpenCloseEvent>)
		BSEventNotifyControl ProcessEvent(const MenuOpenCloseEvent* a_event, BSTEventSource<MenuOpenCloseEvent>* a_eventSource) override;  // 01

		// override (BSTEventSink<PositionPlayerEvent>)
		BSEventNotifyControl ProcessEvent(const PositionPlayerEvent* a_event, BSTEventSource<PositionPlayerEvent>* a_eventSource) override;  // 01

		static MenuTopicManager* GetSingleton()
		{
			static REL::Relocation<MenuTopicManager**> singleton{ RELOCATION_ID(514959, 401099) };
			return *singleton;
		}

		bool IsCurrentSpeaker(const ObjectRefHandle& a_handle) const
		{
			return menuOpen && speaker == a_handle;
		}

		// members
		BSSimpleList<Dialogue*>::Node* selectedResponseNode;  // 18
		BSSimpleList<Dialogue*>*       dialogueList;          // 20
		std::uint64_t                  unk28;                 // 28
		TESTopicInfo*                  rootTopicInfo;         // 30
		Dialogue*                      lastSelectedDialogue;  // 38
		REX::W32::CRITICAL_SECTION     criticalSection;       // 40
		ObjectRefHandle                speaker;               // 68
		ObjectRefHandle                lastSpeaker;           // 6C - used if the dialogue menu was closed but the NPC is still talking
		TESTopicInfo*                  currentTopicInfo;      // 70 - only valid when the NPC is talking
		TESTopicInfo*                  lastTopicInfo;         // 78 - used if the dialogue menu was closed but the NPC is still talking
		BSTArray<BGSDialogueBranch*>   blockingBranches;      // 80
		BSTArray<BGSDialogueBranch*>   topLevelBranches;      // 98
		bool                           isGreetingPlayer;      // B0
		bool                           menuOpen;              // B1
		bool                           forceGoodbye;          // B2
		bool                           shutMenu;              // B3
		bool                           rumorTopicAdded;       // B4
		bool                           waitingToAdvance;      // B5
		bool                           unkB6;                 // B6
		bool                           unkB7;                 // B7
		bool                           unkB8;                 // B8
		bool                           unkB9;                 // B9
		bool                           canSkip;               // BA
		bool                           unkBB;                 // BB
		std::uint16_t                  padBC;                 // BC
		BSTArray<TESTopic*>            unkC0;                 // C0
	};
	static_assert(sizeof(MenuTopicManager) == 0xD8);
}
