-- KiraUI-Plugin_Crosshair.lua
-- A simple screen-centre crosshair overlay: four
-- arms growing out from a centre gap, with an optional centre dot. Purely
-- cosmetic - no secure frames, so it can show/hide freely in combat.

local addonName, ns = ...
local _G = _G

local CreateFrame = CreateFrame
local UIParent = UIParent
local InCombatLockdown = InCombatLockdown
local ipairs, pairs = ipairs, pairs

ns.hasCrosshair = true

----------------------------------------------------------------------
-- Settings
----------------------------------------------------------------------
local DEFAULTS = {
	enable = false,
	color = { 1, 1, 1, 1 },
	opacity = 1,
	thickness = 2,   -- arm width (px)
	length = 10,     -- arm length (px)
	gap = 4,         -- gap from centre to the start of each arm (px)
	dot = false,     -- centre dot
	dotSize = 2,
	border = true,   -- dark outline around the arms/dot
	borderSize = 1,  -- outline thickness (px)
	borderColor = { 0, 0, 0, 1 },
	combatOnly = false,
	x = 0,           -- offset from screen centre
	y = 0,
}

-- repair a saved colour that an older build left as a non-table
local function Migrate(cfg)
	if type(cfg.color) ~= 'table' then cfg.color = { 1, 1, 1, 1 } end
	if type(cfg.borderColor) ~= 'table' then cfg.borderColor = { 0, 0, 0, 1 } end
end

local function GetCfg() return ns.ModuleCfg('crosshair', DEFAULTS, Migrate) end
ns.CHGetCfg = GetCfg

----------------------------------------------------------------------
-- Frame
----------------------------------------------------------------------
local frame
local arms = {}
local inCombat = false

local function EnsureFrame()
	if frame then return end
	frame = CreateFrame('Frame', 'KiraUICrosshair', UIParent)
	-- MEDIUM keeps the crosshair above the 3D world but reliably *below* the
	-- cooldown pulse (HIGH), so the transient pulse icon is never covered by the
	-- static reticle. Raise this back to 'HIGH' to put the crosshair on top.
	frame:SetFrameStrata('MEDIUM')
	frame:SetSize(10, 10)
	frame:EnableMouse(false)

	-- border on ARTWORK (drawn behind), arm on OVERLAY (drawn in front).
	local function border() return frame:CreateTexture(nil, 'ARTWORK') end
	local function arm()
		local t = frame:CreateTexture(nil, 'OVERLAY')
		arms[#arms + 1] = t
		return t
	end
	frame.topB,    frame.top    = border(), arm()
	frame.bottomB, frame.bottom = border(), arm()
	frame.leftB,   frame.left   = border(), arm()
	frame.rightB,  frame.right  = border(), arm()
	frame.dotB,    frame.dot    = border(), arm()
	frame:Hide()
end

-- Combat state is tracked from the PLAYER_REGEN events (below), not by calling
-- InCombatLockdown() here - that keeps this off the lockdown path and flips the
-- moment you enter / leave combat.
local function ShouldShow()
	local cfg = GetCfg()
	if not cfg.enable then return false end
	if cfg.combatOnly and not inCombat then return false end
	return true
end

local function UpdateVisibility()
	if not frame then return end
	frame:SetShown(ShouldShow())
end

local function Apply()
	EnsureFrame()
	local cfg = GetCfg()
	local c, a = cfg.color or { 1, 1, 1, 1 }, cfg.opacity or 1
	local th, len, gap = cfg.thickness or 2, cfg.length or 10, cfg.gap or 4
	local bOn = cfg.border and true or false
	local b = cfg.borderSize or 1
	local bc = cfg.borderColor or { 0, 0, 0, 1 }

	frame:ClearAllPoints()
	frame:SetPoint('CENTER', UIParent, 'CENTER', cfg.x or 0, cfg.y or 0)

	-- place a coloured texture at an offset from the centre
	local function place(t, point, ox, oy, w, h)
		t:SetColorTexture(c[1] or 1, c[2] or 1, c[3] or 1, a)
		t:SetSize(w, h)
		t:ClearAllPoints()
		t:SetPoint(point, frame, 'CENTER', ox, oy)
		t:Show()
	end
	-- place a border that wraps the matching arm by b px on every side
	local function placeBorder(t, point, ox, oy, w, h)
		if not bOn then t:Hide() return end
		t:SetColorTexture(bc[1] or 0, bc[2] or 0, bc[3] or 0, (bc[4] or 1) * a)
		t:SetSize(w + 2 * b, h + 2 * b)
		t:ClearAllPoints()
		t:SetPoint(point, frame, 'CENTER', ox, oy)
		t:Show()
	end

	place(frame.top,    'BOTTOM',  0,  gap,  th,  len)
	place(frame.bottom, 'TOP',     0, -gap,  th,  len)
	place(frame.left,   'RIGHT', -gap,   0,  len, th)
	place(frame.right,  'LEFT',    gap,   0,  len, th)
	-- borders use the same anchor edge, nudged out by b so the wrap is even
	placeBorder(frame.topB,    'BOTTOM',  0,  gap - b, th,  len)
	placeBorder(frame.bottomB, 'TOP',     0, -gap + b, th,  len)
	placeBorder(frame.leftB,   'RIGHT', -gap + b,   0, len, th)
	placeBorder(frame.rightB,  'LEFT',    gap - b,   0, len, th)

	if cfg.dot then
		local d = cfg.dotSize or 2
		place(frame.dot, 'CENTER', 0, 0, d, d)
		placeBorder(frame.dotB, 'CENTER', 0, 0, d, d)
	else
		frame.dot:Hide()
		frame.dotB:Hide()
	end

	UpdateVisibility()
end
ns.CHApply = Apply

----------------------------------------------------------------------
-- Events
----------------------------------------------------------------------
local evt = CreateFrame('Frame')
evt:RegisterEvent('PLAYER_ENTERING_WORLD')
evt:RegisterEvent('PLAYER_REGEN_DISABLED')
evt:RegisterEvent('PLAYER_REGEN_ENABLED')
evt:SetScript('OnEvent', function(_, event)
	if event == 'PLAYER_ENTERING_WORLD' then
		-- your own combat state is readable (not an enemy/secret value)
		inCombat = (UnitAffectingCombat and UnitAffectingCombat('player')) and true or false
		Apply()
	elseif event == 'PLAYER_REGEN_DISABLED' then
		inCombat = true
		UpdateVisibility()
	elseif event == 'PLAYER_REGEN_ENABLED' then
		inCombat = false
		UpdateVisibility()
	end
end)
