-- ------------------------------------------------------------------------------ --
--                                TradeSkillMaster                                --
--                          https://tradeskillmaster.com                          --
--    All Rights Reserved - Detailed license information included with addon.     --
-- ------------------------------------------------------------------------------ --

local _, TSM = ...
local ProfessionState = TSM.Crafting:NewPackage("ProfessionState")
local Event = TSM.Include("Util.Event")
local Delay = TSM.Include("Util.Delay")
local FSM = TSM.Include("Util.FSM")
local Log = TSM.Include("Util.Log")
local private = {
	fsm = nil,
	updateCallbacks = {},
	isClosed = true,
	craftOpen = nil,
	tradeSkillOpen = nil,
	professionName = nil,
}
local WAIT_FRAME_DELAY = 5



-- ============================================================================
-- Module Functions
-- ============================================================================

function ProfessionState.OnInitialize()
	private.CreateFSM()
end

function ProfessionState.RegisterUpdateCallback(callback)
	tinsert(private.updateCallbacks, callback)
end

function ProfessionState.GetIsClosed()
	return private.isClosed
end

function ProfessionState.IsClassicCrafting()
	return TSM.IsWowClassic() and private.craftOpen
end

function ProfessionState.SetCraftOpen(open)
	private.craftOpen = open
end

function ProfessionState.GetCurrentProfession()
	return private.professionName
end



-- ============================================================================
-- FSM
-- ============================================================================

function private.CreateFSM()
	if TSM.IsWowClassic() and not IsAddOnLoaded("Blizzard_CraftUI") then
		LoadAddOn("Blizzard_CraftUI")
	end
	Event.Register("TRADE_SKILL_SHOW", function()
		private.tradeSkillOpen = true
		private.fsm:ProcessEvent("EV_TRADE_SKILL_SHOW")
		private.fsm:ProcessEvent("EV_TRADE_SKILL_DATA_SOURCE_CHANGING")
		private.fsm:ProcessEvent("EV_TRADE_SKILL_DATA_SOURCE_CHANGED")
	end)
	Event.Register("TRADE_SKILL_CLOSE", function()
		private.tradeSkillOpen = false
		if not private.craftOpen then
			private.fsm:ProcessEvent("EV_TRADE_SKILL_CLOSE")
		end
	end)
	if not TSM.IsWowClassic() then
		Event.Register("GARRISON_TRADESKILL_NPC_CLOSED", function()
			private.fsm:ProcessEvent("EV_TRADE_SKILL_CLOSE")
		end)
		Event.Register("TRADE_SKILL_DATA_SOURCE_CHANGED", function()
			private.fsm:ProcessEvent("EV_TRADE_SKILL_DATA_SOURCE_CHANGED")
		end)
		Event.Register("TRADE_SKILL_DATA_SOURCE_CHANGING", function()
			private.fsm:ProcessEvent("EV_TRADE_SKILL_DATA_SOURCE_CHANGING")
		end)
	else
		Event.Register("CRAFT_SHOW", function()
			private.craftOpen = true
			private.fsm:ProcessEvent("EV_TRADE_SKILL_SHOW")
			private.fsm:ProcessEvent("EV_TRADE_SKILL_DATA_SOURCE_CHANGING")
			private.fsm:ProcessEvent("EV_TRADE_SKILL_DATA_SOURCE_CHANGED")
		end)
		Event.Register("CRAFT_CLOSE", function()
			private.craftOpen = false
			if not private.tradeSkillOpen then
				private.fsm:ProcessEvent("EV_TRADE_SKILL_CLOSE")
			end
		end)
		Event.Register("CRAFT_UPDATE", function()
			private.fsm:ProcessEvent("EV_TRADE_SKILL_DATA_SOURCE_CHANGED")
		end)
	end
	local function ToggleDefaultCraftButton()
		if not CraftCreateButton then
			return
		end
		if private.craftOpen then
			CraftCreateButton:Show()
		else
			CraftCreateButton:Hide()
		end
	end
	local function FrameDelayCallback()
		private.fsm:ProcessEvent("EV_FRAME_DELAY")
	end
	private.fsm = FSM.New("PROFESSION_STATE")
		:AddState(FSM.NewState("ST_CLOSED")
			:SetOnEnter(function()
				private.isClosed = true
				private.RunUpdateCallbacks()
			end)
			:SetOnExit(function()
				private.isClosed = false
				private.RunUpdateCallbacks()
			end)
			:AddTransition("ST_WAITING_FOR_DATA")
			:AddEventTransition("EV_TRADE_SKILL_SHOW", "ST_WAITING_FOR_DATA")
		)
		:AddState(FSM.NewState("ST_WAITING_FOR_DATA")
			:AddTransition("ST_WAITING_FOR_READY")
			:AddTransition("ST_CLOSED")
			:AddEventTransition("EV_TRADE_SKILL_DATA_SOURCE_CHANGED", "ST_WAITING_FOR_READY")
			:AddEventTransition("EV_TRADE_SKILL_CLOSE", "ST_CLOSED")
		)
		:AddState(FSM.NewState("ST_WAITING_FOR_READY")
			:SetOnEnter(function()
				Delay.AfterFrame("PROFESSION_STATE_TIME", WAIT_FRAME_DELAY, FrameDelayCallback, WAIT_FRAME_DELAY)
			end)
			:SetOnExit(function()
				Delay.Cancel("PROFESSION_STATE_TIME")
			end)
			:AddTransition("ST_SHOWN")
			:AddTransition("ST_DATA_CHANGING")
			:AddTransition("ST_CLOSED")
			:AddEvent("EV_FRAME_DELAY", function()
				if TSM.Crafting.ProfessionUtil.IsDataStable() then
					return "ST_SHOWN"
				end
			end)
			:AddEventTransition("EV_TRADE_SKILL_DATA_SOURCE_CHANGING", "ST_DATA_CHANGING")
			:AddEventTransition("EV_TRADE_SKILL_CLOSE", "ST_CLOSED")
		)
		:AddState(FSM.NewState("ST_SHOWN")
			:SetOnEnter(function()
				local name = TSM.Crafting.ProfessionUtil.GetCurrentProfessionName()
				assert(name)
				Log.Info("Showing profession: %s", name)
				private.professionName = name
				if TSM.IsWowClassic() then
					ToggleDefaultCraftButton()
				end
				private.RunUpdateCallbacks()
			end)
			:SetOnExit(function()
				private.professionName = nil
				private.RunUpdateCallbacks()
			end)
			:AddTransition("ST_DATA_CHANGING")
			:AddTransition("ST_CLOSED")
			:AddEventTransition("EV_TRADE_SKILL_DATA_SOURCE_CHANGING", "ST_DATA_CHANGING")
			:AddEventTransition("EV_TRADE_SKILL_CLOSE", "ST_CLOSED")
		)
		:AddState(FSM.NewState("ST_DATA_CHANGING")
			:AddTransition("ST_WAITING_FOR_READY")
			:AddTransition("ST_CLOSED")
			:AddEventTransition("EV_TRADE_SKILL_DATA_SOURCE_CHANGED", "ST_WAITING_FOR_READY")
			:AddEventTransition("EV_TRADE_SKILL_CLOSE", "ST_CLOSED")
		)
		:Init("ST_CLOSED")
end



-- ============================================================================
-- Private Helper Functions
-- ============================================================================

function private.RunUpdateCallbacks()
	for _, callback in ipairs(private.updateCallbacks) do
		callback(private.professionName)
	end
end
