local W, F, E, L = unpack((select(2, ...))) ---@type WindTools, Functions, ElvUI, LocaleTable
local RM = W:NewModule("RectangleMinimap", "AceEvent-3.0", "AceHook-3.0") ---@class RectangleMinimap : AceModule, AceEvent-3.0, AceHook-3.0
local M = E:GetModule("Minimap")

local _G = _G
local abs = abs
local ceil = ceil
local floor = floor
local format = format
local pairs = pairs
local select = select
local tinsert = tinsert
local tremove = tremove

local InCombatLockdown = InCombatLockdown
local Minimap = _G.Minimap

local C_AddOns_IsAddOnLoaded = C_AddOns.IsAddOnLoaded

function RM:SetHereBeDragonsPinShown(pin, isShown)
	local forceShown = not self.db or not self.db.enable or not self.db.fixHereBeDragons
	if isShown or forceShown then
		if pin.__windHidden then
			pin:SetAlpha(1, true)
			pin.__windHidden = nil
		end
	else
		if not pin.__windHidden then
			pin:SetAlpha(0, true)
			pin.__windHidden = true
		end
	end
end

function RM:HereBeDragonsPinsFix()
	if self.hereBeDragonsPinsFixed or not self.db or not self.db.fixHereBeDragons then
		return
	end

	local lib = _G.LibStub("HereBeDragons-Pins-2.0", true)
	if not lib or not lib.updateFrame or not lib.activeMinimapPins then
		return
	end

	local OnUpdate = lib.updateFrame.GetScript and lib.updateFrame:GetScript("OnUpdate")
	if not OnUpdate then
		return
	end

	self:SecureHookScript(lib.updateFrame, "OnUpdate", function()
		local minimapCenterY = lib.Minimap and select(2, lib.Minimap:GetCenter())
		if not minimapCenterY or not self.effectiveHeight then
			return
		end

		local halfHeightLimit = self.effectiveHeight / 2 - self.db.pinHidingTolerance
		for pin in pairs(lib.activeMinimapPins) do
			if pin and pin.IsObjectType and pin:IsObjectType("Frame") then
				if not self:IsHooked(pin, "SetAlpha") then
					self:SecureHook(pin, "SetAlpha", function(_pin, _, skipFlag)
						if not skipFlag and _pin.__windHidden then
							_pin:SetAlpha(0, true)
						end
					end)
				end

				if not self:IsHooked(pin, "Show") then
					self:SecureHook(pin, "Show", function()
						if pin.__windHidden then
							pin:SetAlpha(0, true)
						end
					end)
				end

				local pinCenterY = select(2, pin:GetCenter())
				if pinCenterY then
					self:SetHereBeDragonsPinShown(pin, abs(pinCenterY - minimapCenterY) <= halfHeightLimit)
				end
			end
		end
	end)

	self.hereBeDragonsPinsFixed = true
end

function RM:ChangeShape()
	if not self.db or InCombatLockdown() then
		return
	end

	local holder = M.MapHolder
	local panel = _G.MinimapPanel

	local fileID = self.db.enable and self.db.heightPercentage and floor(self.db.heightPercentage * 128) or 128
	local texturePath = format([[Interface\AddOns\EllesmereUI_WindTools\Media\Textures\MinimapMasks\%d.tga]], fileID)
	local heightPct = fileID / 128
	local newHeight = E.MinimapSize * heightPct
	local diff = E.MinimapSize - newHeight
	local halfDiff = ceil(diff / 2)

	local mmOffset = E.PixelMode and 1 or 3
	local mmScale = E.db.general.minimap.scale

	-- First, update the size and position of the ElvUI Minimap holder and mover
	self.effectiveHeight = newHeight
	self:UpdateMiniMapHolderAndMover()

	-- Update the size and position of the panel
	if panel:IsShown() then
		panel:ClearAllPoints()
		panel:Point("TOPLEFT", Minimap, "BOTTOMLEFT", -E.Border, (E.PixelMode and 0 or -3) + halfDiff * mmScale)
		panel:Point("BOTTOMRIGHT", Minimap, "BOTTOMRIGHT", E.Border, -23 + halfDiff * mmScale)
	end

	-- Do not allow the Minimap to be dragged off screen because the canvas cannot go off screen
	Minimap:SetClampedToScreen(true)
	Minimap:SetClampRectInsets(0, 0, 0, 0)
	Minimap:ClearAllPoints()
	Minimap:Point("TOPRIGHT", holder, -mmOffset / mmScale, -mmOffset / mmScale + halfDiff)

	-- Update mask
	Minimap:SetMaskTexture(texturePath)
	Minimap:SetHitRectInsets(0, 0, halfDiff, halfDiff)

	-- Update the size and position of the Minimap
	Minimap.backdrop:ClearAllPoints()
	Minimap.backdrop:SetOutside(Minimap, mmOffset, -halfDiff * mmScale + mmOffset)

	-- Update the size and position of the Minimap location text
	if Minimap.location then
		Minimap.location:ClearAllPoints()
		Minimap.location:Point("TOP", Minimap, 0, -4 - halfDiff * mmScale)
	end

	-- HybridMinimap support
	if _G.HybridMinimap then
		local mapCanvas = _G.HybridMinimap.MapCanvas
		local rectangleMask = _G.HybridMinimap:CreateMaskTexture()
		rectangleMask:SetTexture(texturePath)
		rectangleMask:SetAllPoints(_G.HybridMinimap)
		_G.HybridMinimap.RectangleMask = rectangleMask
		mapCanvas:SetMaskTexture(rectangleMask)
		mapCanvas:SetUseMaskTexture(true)
	end

	self:HereBeDragonsPinsFix()
end

function RM:UpdateMiniMapHolderAndMover()
	local panel = _G.MinimapPanel
	local holder = M.MapHolder
	local mover = _G.MinimapMover
	local scale = E.db.general.minimap.scale

	local mWidth, mHeight = _G.Minimap:GetWidth(), self.effectiveHeight
	local bWidth, bHeight = E:Scale(E.PixelMode and 2 or 6), E:Scale(E.PixelMode and 2 or 8)
	local panelSize, joinPanel = (panel:IsShown() and panel:GetHeight()) or E:Scale(E.PixelMode and 1 or -1), E:Scale(1)
	local HEIGHT, WIDTH = (mHeight * scale) + (panelSize - joinPanel), mWidth * scale

	holder:Size(WIDTH + bWidth, HEIGHT + bHeight)
	mover:Size(WIDTH + bWidth, HEIGHT + bHeight)
end

function RM:SetUpdateHook()
	if not self.initialized and M.Initialized then
		self:SecureHook(M, "SetGetMinimapShape", "ChangeShape")
		self:SecureHook(M, "UpdateSettings", "ChangeShape")
		self.initialized = true
	end

	self:ChangeShape()
end

function RM:Blizzard_Minimap_Loaded()
	self:SetUpdateHook()
end

function RM:Blizzard_HybridMinimap_Loaded()
	self:SetUpdateHook()
end

function RM:Initialize()
	self.db = E.db.WT.maps.rectangleMinimap
	if not self.db or not self.db.enable or not M.Initialized then
		return
	end

	self.addonLoadedCallbacks = {}
	if not C_AddOns_IsAddOnLoaded("Blizzard_Minimap") then
		tinsert(self.addonLoadedCallbacks, { "Blizzard_Minimap", self.Blizzard_Minimap_Loaded })
	end

	if not C_AddOns_IsAddOnLoaded("Blizzard_HybridMinimap") then
		tinsert(self.addonLoadedCallbacks, { "Blizzard_HybridMinimap", self.Blizzard_HybridMinimap_Loaded })
	end

	if #self.addonLoadedCallbacks > 0 then
		self:RegisterEvent("ADDON_LOADED")
	end

	_G.MinimapBackdrop.StaticOverlayTexture:SetAlpha(0)

	F.TaskManager:AfterLogin(self.SetUpdateHook, self)
end

function RM:ADDON_LOADED(_, addon)
	for i = 1, #self.addonLoadedCallbacks do
		local callback = self.addonLoadedCallbacks[i]
		if callback[1] == addon then
			callback[2](self)
			tremove(self.addonLoadedCallbacks, i)
			break
		end
	end

	if #self.addonLoadedCallbacks == 0 then
		self:UnregisterEvent("ADDON_LOADED")
	end
end

function RM:ProfileUpdate()
	self.db = E.db.WT.maps.rectangleMinimap

	if not self.db then
		return
	end

	if self.db.enable then
		self:SetUpdateHook()
	elseif self.initialized then
		self:ChangeShape()
	end
end

W:RegisterModule(RM:GetName())
