local _, ns = ...

------------------------------------------------------------
-- Xal'atath's Toes — a jokey homage to the Xaltoes addon by
-- Sakreble (which is itself a structured version of a
-- ModelScene one-liner by @Herotherogue).
--
-- Displays Xal'atath's feet on screen. Enabled by default so
-- the joke lands on first install; turn off in:
--   Settings → General → Fun → "Show Xal'atath's toes"
--   or /yh toes off
------------------------------------------------------------

local MODEL_FILE_ID = 5159886
local ANIMATION_ID  = 1138
local SCENE_SIZE    = 512
local ACTOR_X, ACTOR_Y, ACTOR_Z = 2, 0, 0
local MIN_SCALE, MAX_SCALE = 0.5, 2.0

local DEFAULTS = {
    enabled  = true,
    rotation = 0,
    scale    = 1,
    position = { point = "TOP", relativePoint = "TOP", x = 0, y = 0 },
}

local Xaltoes = {}
ns.Xaltoes = Xaltoes

-- Cede the feature to the standalone Xaltoes addon if the user has it
-- installed. Without this guard both addons render their own ModelScene +
-- actor, producing two overlapping toes with independent mover/disable
-- state — disabling ours leaves the standalone's copy behind, which the
-- user correctly described as "can't turn it off".
local function standaloneLoaded()
    local api = C_AddOns and C_AddOns.IsAddOnLoaded or IsAddOnLoaded
    if not api then return false end
    local ok, loaded = pcall(api, "Xaltoes")
    return ok and loaded and true or false
end
local deferred = false

local scene
local actor
local moverEnabled = false
local listeners = {}

------------------------------------------------------------
-- DB
------------------------------------------------------------

local function deepCopy(t)
    if type(t) ~= "table" then return t end
    local out = {}
    for k, v in pairs(t) do out[k] = deepCopy(v) end
    return out
end

local function db()
    YippYappHelperDB = YippYappHelperDB or {}
    local d = YippYappHelperDB.xaltoes
    if type(d) ~= "table" then
        d = deepCopy(DEFAULTS)
        YippYappHelperDB.xaltoes = d
    end
    if type(d.enabled)  ~= "boolean" then d.enabled  = DEFAULTS.enabled  end
    if type(d.rotation) ~= "number"  then d.rotation = DEFAULTS.rotation end
    if type(d.scale)    ~= "number" or d.scale <= 0 then d.scale = DEFAULTS.scale end
    if type(d.position) ~= "table"   then d.position = deepCopy(DEFAULTS.position) end
    for k, v in pairs(DEFAULTS.position) do
        if d.position[k] == nil then d.position[k] = v end
    end
    return d
end

local function notify()
    for _, cb in ipairs(listeners) do pcall(cb) end
end

function Xaltoes:OnChanged(cb) table.insert(listeners, cb) end

------------------------------------------------------------
-- Helpers
------------------------------------------------------------

local function clampScale(v)
    if v < MIN_SCALE then return MIN_SCALE end
    if v > MAX_SCALE then return MAX_SCALE end
    return v
end

local function roundToInt(v)
    if v >= 0 then return math.floor(v + 0.5) end
    return math.ceil(v - 0.5)
end

------------------------------------------------------------
-- Scene construction + viewport rotation
------------------------------------------------------------

local function applyPosition()
    if not scene then return end
    local p = db().position
    scene:ClearAllPoints()
    scene:SetPoint(p.point, UIParent, p.relativePoint, p.x, p.y)
end

local function applyRotation()
    if not scene then return end
    local radians = math.rad((db().rotation or 0) % 360)

    if scene.GetCameraForward and scene.GetCameraRight and scene.GetCameraUp
        and scene.SetCameraOrientationByAxisVectors then
        if not scene._yyhCamBase then
            local fx, fy, fz = scene:GetCameraForward()
            local rx, ry, rz = scene:GetCameraRight()
            local ux, uy, uz = scene:GetCameraUp()
            scene._yyhCamBase = {
                fx = fx, fy = fy, fz = fz,
                rx = rx, ry = ry, rz = rz,
                ux = ux, uy = uy, uz = uz,
            }
        end
        local base = scene._yyhCamBase
        local c, s = math.cos(radians), math.sin(radians)
        local rx = (base.rx * c) + (base.ux * s)
        local ry = (base.ry * c) + (base.uy * s)
        local rz = (base.rz * c) + (base.uz * s)
        local ux = (base.ux * c) - (base.rx * s)
        local uy = (base.uy * c) - (base.ry * s)
        local uz = (base.uz * c) - (base.rz * s)
        scene:SetCameraOrientationByAxisVectors(base.fx, base.fy, base.fz, rx, ry, rz, ux, uy, uz)
        return
    end

    if scene.SetCameraOrientationByYawPitchRoll then
        scene:SetCameraOrientationByYawPitchRoll(0, 0, radians)
    end
end

local function applyScale()
    if not scene then return end
    scene:SetScale(clampScale(db().scale or 1))
end

local function build()
    if scene then return end
    scene = CreateFrame("ModelScene", nil, UIParent)
    scene:SetSize(SCENE_SIZE, SCENE_SIZE)

    local overlay = scene:CreateTexture(nil, "OVERLAY")
    overlay:SetAllPoints(scene)
    overlay:SetColorTexture(1, 0.6, 0.9, 0.15)
    overlay:Hide()
    scene._yyhOverlay = overlay

    actor = scene:CreateActor()
    actor:SetModelByFileID(MODEL_FILE_ID)
    actor:SetAnimation(ANIMATION_ID)
    actor:SetPosition(ACTOR_X, ACTOR_Y, ACTOR_Z)
end

------------------------------------------------------------
-- Mover
------------------------------------------------------------

local function disableMoverInternal()
    if not scene then return end
    moverEnabled = false
    scene:EnableMouse(false)
    scene:SetMovable(false)
    scene:RegisterForDrag()
    scene:SetScript("OnDragStart", nil)
    scene:SetScript("OnDragStop", nil)
    if scene._yyhOverlay then scene._yyhOverlay:Hide() end
end

local function enableMoverInternal()
    if not scene then return end
    moverEnabled = true
    scene:EnableMouse(true)
    scene:SetMovable(true)
    scene:RegisterForDrag("LeftButton")
    scene:SetScript("OnDragStart", function(f) f:StartMoving() end)
    scene:SetScript("OnDragStop", function(f)
        f:StopMovingOrSizing()
        local centerX, centerY = f:GetCenter()
        local parentCenterX, parentCenterY
        if UIParent.GetCenter then
            parentCenterX, parentCenterY = UIParent:GetCenter()
        end
        local d = db()
        if centerX and centerY and parentCenterX and parentCenterY then
            d.position = {
                point = "CENTER", relativePoint = "CENTER",
                x = roundToInt(centerX - parentCenterX),
                y = roundToInt(centerY - parentCenterY),
            }
        else
            local point, _, relativePoint, x, y = f:GetPoint(1)
            d.position = {
                point         = point or "CENTER",
                relativePoint = relativePoint or "CENTER",
                x             = roundToInt(x or 0),
                y             = roundToInt(y or 0),
            }
        end
        applyPosition()
        notify()
    end)
    if scene._yyhOverlay then scene._yyhOverlay:Show() end
end

------------------------------------------------------------
-- Public API
------------------------------------------------------------

function Xaltoes:IsEnabled() return db().enabled and true or false end
function Xaltoes:IsMoverEnabled() return moverEnabled end
function Xaltoes:IsDeferred() return deferred end

function Xaltoes:SetEnabled(v)
    db().enabled = v and true or false
    if deferred then
        -- Standalone Xaltoes addon is in charge; we never built a scene and
        -- shouldn't touch state. The SavedVariables flag still persists for
        -- when the user removes the standalone addon.
        notify()
        return
    end
    if not scene then return end
    if db().enabled then
        scene:Show()
    else
        scene:Hide()
        if moverEnabled then disableMoverInternal() end
    end
    notify()
end

function Xaltoes:SetMoverEnabled(v)
    if v then
        if not self:IsEnabled() and scene then scene:Show() end
        enableMoverInternal()
    else
        disableMoverInternal()
        if not self:IsEnabled() and scene then scene:Hide() end
    end
    notify()
end

function Xaltoes:SetRotation(degrees)
    db().rotation = (degrees or 0) % 360
    applyRotation()
    notify()
end

function Xaltoes:GetRotation() return db().rotation or 0 end

function Xaltoes:Flip()
    local cur = db().rotation or 0
    self:SetRotation(cur == 0 and 180 or 0)
end

function Xaltoes:SetScale(v)
    db().scale = clampScale(v or 1)
    applyScale()
    notify()
end

function Xaltoes:GetScale() return db().scale or 1 end

function Xaltoes:ResetPosition()
    db().position = deepCopy(DEFAULTS.position)
    applyPosition()
    notify()
end

function Xaltoes:ResetAll()
    YippYappHelperDB = YippYappHelperDB or {}
    YippYappHelperDB.xaltoes = deepCopy(DEFAULTS)
    applyPosition()
    applyRotation()
    applyScale()
    if db().enabled then
        if scene then scene:Show() end
    else
        if scene then scene:Hide() end
    end
    notify()
end

------------------------------------------------------------
-- Bootstrap
------------------------------------------------------------

local function bootstrap()
    if standaloneLoaded() then
        deferred = true
        print("|cff00ff00YippYapp Helper|r: standalone |cffff69b4Xaltoes|r addon detected — leaving the toes to it.")
        return
    end
    build()
    applyPosition()
    applyRotation()
    applyScale()
    if db().enabled then
        scene:Show()
    else
        scene:Hide()
    end
end

local init = CreateFrame("Frame")
init:RegisterEvent("PLAYER_LOGIN")
init:SetScript("OnEvent", function(self, event)
    if event == "PLAYER_LOGIN" then
        bootstrap()
        self:UnregisterEvent("PLAYER_LOGIN")
    end
end)
