local MER, F, E, I, V, P, G, L = unpack(ElvUI_MerathilisUI)
local module = MER:GetModule("MER_WorldMap")

local _G = _G
local ceil = ceil
local gsub = gsub
local ipairs = ipairs
local mod = mod
local pairs = pairs
local strsplit = strsplit
local tinsert = tinsert
local tonumber = tonumber

local TexturePool_HideAndClearAnchors = TexturePool_HideAndClearAnchors

local C_AddOns_IsAddOnLoaded = C_AddOns.IsAddOnLoaded
local C_MapExplorationInfo_GetExploredMapTextures = C_MapExplorationInfo.GetExploredMapTextures
local C_Map_GetMapArtID = C_Map.GetMapArtID
local C_Map_GetMapArtLayers = C_Map.GetMapArtLayers

local overlayTextures = {}

function module:HandleMap(map, fullUpdate)
	overlayTextures = {}
	local mapID = _G.WorldMapFrame.mapID
	if not mapID then
		return
	end

	local artID = C_Map_GetMapArtID(mapID)
	if not artID or not module.RevealDatabase[artID] then
		return
	end
	local zone = module.RevealDatabase[artID]

	local TileExists = {}
	local exploredMapTextures = C_MapExplorationInfo_GetExploredMapTextures(mapID)
	if exploredMapTextures then
		for i, tex in ipairs(exploredMapTextures) do
			local key = tex.textureWidth .. ":" .. tex.textureHeight .. ":" .. tex.offsetX .. ":" .. tex.offsetY
			TileExists[key] = true
		end
	end

	map.layerIndex = map:GetMap():GetCanvasContainer():GetCurrentLayerIndex()
	local layers = C_Map_GetMapArtLayers(mapID)
	local layerInfo = layers and layers[map.layerIndex]
	if not layerInfo then
		return
	end
	local TILE_SIZE_WIDTH = layerInfo.tileWidth
	local TILE_SIZE_HEIGHT = layerInfo.tileHeight

	for key, files in pairs(zone) do
		if not TileExists[key] then
			local width, height, offsetX, offsetY = strsplit(":", key)
			local fileDataIDs = { strsplit(",", files) }
			local numTexturesWide = ceil(width / TILE_SIZE_WIDTH)
			local numTexturesTall = ceil(height / TILE_SIZE_HEIGHT)
			local texturePixelWidth, textureFileWidth, texturePixelHeight, textureFileHeight
			for j = 1, numTexturesTall do
				if j < numTexturesTall then
					texturePixelHeight = TILE_SIZE_HEIGHT
					textureFileHeight = TILE_SIZE_HEIGHT
				else
					texturePixelHeight = mod(height, TILE_SIZE_HEIGHT)
					if texturePixelHeight == 0 then
						texturePixelHeight = TILE_SIZE_HEIGHT
					end
					textureFileHeight = 16
					while textureFileHeight < texturePixelHeight do
						textureFileHeight = textureFileHeight * 2
					end
				end
				for k = 1, numTexturesWide do
					local texture = map.overlayTexturePool:Acquire()
					if k < numTexturesWide then
						texturePixelWidth = TILE_SIZE_WIDTH
						textureFileWidth = TILE_SIZE_WIDTH
					else
						texturePixelWidth = mod(width, TILE_SIZE_WIDTH)
						if texturePixelWidth == 0 then
							texturePixelWidth = TILE_SIZE_WIDTH
						end
						textureFileWidth = 16
						while textureFileWidth < texturePixelWidth do
							textureFileWidth = textureFileWidth * 2
						end
					end
					texture:Size(texturePixelWidth, texturePixelHeight)
					texture:SetTexCoord(
						0,
						texturePixelWidth / textureFileWidth,
						0,
						texturePixelHeight / textureFileHeight
					)
					texture:Point(
						"TOPLEFT",
						offsetX + (TILE_SIZE_WIDTH * (k - 1)),
						-(offsetY + (TILE_SIZE_HEIGHT * (j - 1)))
					)
					texture:SetTexture(tonumber(fileDataIDs[((j - 1) * numTexturesWide) + k]), nil, nil, "TRILINEAR")
					texture:SetDrawLayer("ARTWORK", -1)
					texture:Show()
					if fullUpdate then
						map.textureLoadGroup:AddTexture(texture)
					end

					if module.db and module.db.reveal.enable and module.db.reveal.useColor then
						texture:SetVertexColor(
							module.db.reveal.color.r,
							module.db.reveal.color.g,
							module.db.reveal.color.b,
							module.db.reveal.color.a
						)
					end

					tinsert(overlayTextures, texture)
				end
			end
		end
	end
end

-- from Leatrix_Maps
local function TexturePool_ResetVertexColor(pool, texture)
	texture:SetVertexColor(1, 1, 1, 1)
	texture:SetAlpha(1)
	return TexturePool_HideAndClearAnchors(pool, texture)
end

function module:Reveal()
	if not self.db.reveal.enable then
		return
	end

	-- Exclude some areas; Thanks Leatrix_Maps
	if E.myfraction == "Alliance" then
		module.RevealDatabase[556]["223:279:194:0"] = gsub(module.RevealDatabase[556]["223:279:194:0"], "1037663", "")
	elseif E.myfraction == "Horde" then
		module.RevealDatabase[542]["267:257:336:327"] =
			gsub(module.RevealDatabase[542]["267:257:336:327"], "1003342", "")
	end

	module.RevealDatabase[521] = nil -- Throne of Thunder
	module.RevealDatabase[1176] = nil -- The Dredge (Darkshore)
	module.RevealDatabase[67]["453:340:0:0"] = nil -- Veiled Sea (Darkshore)

	for pin in _G.WorldMapFrame:EnumeratePinsByTemplate("MapExplorationPinTemplate") do
		self:SecureHook(pin, "RefreshOverlays", "HandleMap")
		pin.overlayTexturePool.resetterFunc = TexturePool_ResetVertexColor
	end
end

function module:Scale()
	if not self.db.scale.enable then
		return
	end

	_G.WorldMapFrame:SetClampedToScreen(true)
	_G.WorldMapFrame:SetScale(self.db.scale.size)
end

function module:Initialize()
	if not E.private.general.worldMap then
		return
	end

	if C_AddOns_IsAddOnLoaded("Mapster") then
		self.StopRunning = "Mapster"
		return
	end

	self.db = E.db.mui.maps.worldMap

	if not self.db or not self.db.enable then
		return
	end

	_G.QuestMapFrame:SetScript("OnHide", nil) -- fix potential toggle taint with HandyNotes or any other WQ AddOn
	self:Scale()
	self:Reveal()
end

MER:RegisterModule(module:GetName())
