-- Sleek RP - Warlock Pets (PetTalking.lua)
-- Talking UI : portrait rond + anneau custom + icônes custom par pet.
-- Affiche "PetName - PetType" en titre, avec type localisé (FR/EN) et respect du choix Sayaad (Incubus/Succubus).
-- Choix Cartoon/Realistic via DB.portrait_style.

local ADDON_NAME, SRPWL = ...
SRPWL = SRPWL or {}
_G.SRPWL = SRPWL

SleekRP_WLP_DB = SleekRP_WLP_DB or {}
local DB = SleekRP_WLP_DB

-- ========= Chemins =========
local ART_PATH = "Interface\\AddOns\\" .. (ADDON_NAME or "SleekRP_WarlockPets") .. "\\Art\\"

-- ========= Defaults =========
if DB.use_talking_style == nil then DB.use_talking_style = true end
DB.talking_scale        = DB.talking_scale        or 1.0
DB.talking_point        = DB.talking_point        or { "TOP", UIParent, "TOP", 0, -160 }
DB.talking_duration     = DB.talking_duration     or 4.5
DB.talking_lock         = (DB.talking_lock == true)
DB.talking_instsuppress = (DB.talking_instsuppress ~= false)  -- par défaut: ON (masquer en instance)
DB.talking_width        = DB.talking_width        or 444
DB.talking_height       = DB.talking_height       or 102

DB.talking_portrait     = DB.talking_portrait     or 72
DB.talking_ring_scale   = DB.talking_ring_scale   or 0.80
DB.talking_ring_ofsx    = (DB.talking_ring_ofsx ~= nil) and DB.talking_ring_ofsx or -1
DB.talking_ring_ofsy    = DB.talking_ring_ofsy    or 0

DB.global_scale         = DB.global_scale         or 1.0
DB.portrait_style       = DB.portrait_style       or "cartoon"   -- cartoon par défaut

DB.talking_debug        = (DB.talking_debug == true)

-- ========= Utils =========
local function Crisp(tex)
  if not tex then return end
  if tex.SetTexelSnappingBias then tex:SetTexelSnappingBias(0) end
  if tex.SetSnapToPixelGrid then tex:SetSnapToPixelGrid(true) end
end

local function Atlas(tex, atlas, fallback)
  if tex and tex.SetAtlas and atlas and C_Texture and C_Texture.GetAtlasInfo and C_Texture.GetAtlasInfo(atlas) then
    tex:SetAtlas(atlas, true); return true
  end
  if tex and fallback then
    tex:SetTexture(fallback); tex:SetTexCoord(0,1,0,1); return true
  end
end

-- Icônes custom (Cartoon)
local CUSTOM_PET_ICONS = {
  succubus   = ART_PATH .. "Icon_Succubus.tga",
  felguard   = ART_PATH .. "Icon_Felguard.tga",
  voidwalker = ART_PATH .. "Icon_Voidwalker.tga",
  imp        = ART_PATH .. "Icon_Imp.tga",
  felhunter  = ART_PATH .. "Icon_Felhunter.tga",
  sayaad     = ART_PATH .. "Icon_Sayaad.tga",   -- Incubus (cartoon)
}

-- Icônes Realistic (*_02.tga)
local CUSTOM_PET_ICONS_REAL = {
  succubus   = ART_PATH .. "Icon_Succubus_02.tga",
  felguard   = ART_PATH .. "Icon_Felguard_02.tga",
  voidwalker = ART_PATH .. "Icon_Voidwalker_02.tga",
  imp        = ART_PATH .. "Icon_Imp_02.tga",
  felhunter  = ART_PATH .. "Icon_Felhunter_02.tga",
  sayaad     = ART_PATH .. "Icon_Sayaad_02.tga", -- Incubus (realistic)
}

-- Canon dyn. (minuscules)
--  - "sayaad" = Incubus
--  - "succubus" = Succubus
--  - autres: eux-mêmes
local function CanonicalPetKey(petKey, petName)
  local k = (petKey and tostring(petKey):lower()) or (petName and tostring(petName):lower()) or nil
  if not k or k == "" then return nil end
  k = k:gsub("%s+", "")

  -- Groupe Sayaad : on choisit le canon selon la conf utilisateur
  if k == "sayaad" or k == "incubus" then
    return "sayaad" -- on veut afficher Incubus/Incube
  end
  if k == "succubus" then
    return "succubus"
  end

  -- Variantes simples
  if k == "wrathguard" then return "felguard" end

  if k == "imp" or k == "voidwalker" or k == "felhunter" or k == "felguard" then
    return k
  end
  return k
end

-- Fallback Blizzard (minuscules)
local BLIZZ_FALLBACKS = {
  imp        = 136172,
  voidwalker = 136221,
  felhunter  = 136217,
  succubus   = 136220,
  sayaad     = 4641600, -- Incubus
  felguard   = 136216,
}

-- Teinte d’anneau
local RING_TINT = {
  succubus   = {1.00, 0.84, 0.00},
  felguard   = {0.95, 0.35, 0.15},
  voidwalker = {0.45, 0.55, 1.00},
  felhunter  = {0.50, 1.00, 0.50},
  imp        = {1.00, 0.55, 0.20},
  sayaad     = {0.75, 0.65, 1.00}, -- léger violet pour Incubus
}

-- Localisation du type affiché (par canon)
local PET_TYPE_L10N_EN = {
  imp="Imp", voidwalker="Voidwalker", felhunter="Felhunter",
  succubus="Succubus", felguard="Felguard", sayaad="Incubus",
}
local PET_TYPE_L10N_FR = {
  imp="Diablotin", voidwalker="Marcheur du Vide", felhunter="Chasseur corrompu",
  succubus="Succube", felguard="Garde funeste", sayaad="Incube",
}

local function GetLocalizedPetType(canon)
  if not canon or canon == "" then return "Pet" end
  local loc = GetLocale and GetLocale() or "enUS"
  if loc == "frFR" then
    return PET_TYPE_L10N_FR[canon] or PET_TYPE_L10N_EN[canon] or (canon:sub(1,1):upper()..canon:sub(2))
  else
    return PET_TYPE_L10N_EN[canon] or (canon:sub(1,1):upper()..canon:sub(2))
  end
end

-- Sélection texture avec style Cartoon/Realistic
local function GetPetIconTexture(petKey, overrideIcon, petName)
  if overrideIcon and overrideIcon ~= "" then return overrideIcon end
  local canon = CanonicalPetKey(petKey, petName)
  local style = (DB.portrait_style == "realistic") and CUSTOM_PET_ICONS_REAL or CUSTOM_PET_ICONS
  if canon and style[canon] then
    return style[canon]
  end
  if canon and BLIZZ_FALLBACKS[canon] then
    return BLIZZ_FALLBACKS[canon]
  end
  return 136197 -- Question mark
end

-- ========= Construction du frame =========
local Queue, playing = {}, false
local f

local function BuildFrame()
  if f then return end

  f = CreateFrame("Frame", "SRPWL_TalkingFrame", UIParent, "BackdropTemplate")
  _G.SRPWL_TalkingFrame = f
  f:SetSize(DB.talking_width, DB.talking_height)
  f:SetClampedToScreen(true)
  f:SetFrameStrata("DIALOG")

  -- Position & drag
  f:ClearAllPoints()
  f:SetPoint(unpack(DB.talking_point))
  f:EnableMouse(true)
  f:SetMovable(true)
  f:RegisterForDrag("LeftButton")
  f:SetScript("OnDragStart", function(self) if not DB.talking_lock then self:StartMoving() end end)
  f:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    local p, rel, rp, x, y = self:GetPoint(1)
    DB.talking_point = { p, rel, rp, math.floor(x+0.5), math.floor(y+0.5) }
  end)

  -- Fond + bord (atlas Retail si dispo, fallback texture Classic)
  f.bg = f:CreateTexture(nil, "BACKGROUND")
  Atlas(f.bg, "TalkingHeads-TextBackground", "Interface\\DialogFrame\\UI-DialogBox-Background-Dark")
  f.bg:SetAllPoints()

  f.edge = f:CreateTexture(nil, "BORDER")
  Atlas(f.edge, "TalkingHeads-TextEdge", nil)
  if f.edge:GetTexture() then f.edge:SetAllPoints() end

  -- Portrait + anneau
  f.portraitContainer = CreateFrame("Frame", nil, f)
  f.portraitContainer:SetSize(DB.talking_portrait, DB.talking_portrait)
  f.portraitContainer:SetPoint("TOPLEFT", 12, -14)

  f.portrait = f.portraitContainer:CreateTexture(nil, "ARTWORK", nil, 1)
  f.portrait:SetAllPoints()
  f.portrait:SetTexCoord(0.06, 0.94, 0.06, 0.94)
  Crisp(f.portrait)

  f.pMask = f.portraitContainer:CreateMaskTexture(nil, "ARTWORK", nil, 2)
  f.pMask:SetTexture("Interface\\CharacterFrame\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
  f.pMask:SetAllPoints()
  f.portrait:AddMaskTexture(f.pMask)

  f.ring = f.portraitContainer:CreateTexture(nil, "OVERLAY", nil, 3)
  f.ring:SetTexture(ART_PATH .. "Ring.tga")
  f.ring:SetPoint("CENTER")
  Crisp(f.ring)

  local function UpdateRing()
    local d = DB.talking_portrait
    local s = DB.talking_ring_scale or 1.0
    local ox, oy = DB.talking_ring_ofsx or 0, DB.talking_ring_ofsy or 0
    f.ring:ClearAllPoints()
    f.ring:SetPoint("CENTER", ox, oy)
    local ringSize = math.floor(d * 1.28 * s + 0.5)
    f.ring:SetSize(ringSize, ringSize)
  end

  function f:UpdatePortraitSize()
    local d = DB.talking_portrait
    f.portraitContainer:SetSize(d, d)
    UpdateRing()
  end
  f:UpdatePortraitSize()

  -- Titres & texte
  f.name = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  f.name:SetPoint("TOPLEFT", f.portraitContainer, "TOPRIGHT", 12, -2)
  f.name:SetJustifyH("LEFT")

  f.action = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  f.action:SetPoint("TOPLEFT", f.name, "BOTTOMLEFT", 0, -2)
  f.action:SetJustifyH("LEFT")
  f.action:SetTextColor(1, 0.82, 0)

  f.text = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
  f.text:SetPoint("TOPLEFT", f.action, "BOTTOMLEFT", 0, -6)
  f.text:SetPoint("RIGHT", -16, 0)
  f.text:SetJustifyH("LEFT")
  f.text:SetSpacing(3)

  -- Animations (avec fallback)
  local supportsAnim = (type(f.CreateAnimationGroup) == "function")
  local ticket = 0

  if supportsAnim then
    f.t = f:CreateAnimationGroup()
    if not f.t or type(f.t.CreateAnimation) ~= "function" then
      supportsAnim = false
      f.t = nil
    end
  end

  if supportsAnim then
    f.t.fadeIn  = f.t:CreateAnimation("Alpha"); f.t.fadeIn:SetFromAlpha(0); f.t.fadeIn:SetToAlpha(1); f.t.fadeIn:SetDuration(0.15); f.t.fadeIn:SetOrder(1)
    f.t.hold    = f.t:CreateAnimation("Alpha"); f.t.hold:SetFromAlpha(1); f.t.hold:SetToAlpha(1); f.t.hold:SetDuration(0.01); f.t.hold:SetOrder(2)
    f.t.fadeOut = f.t:CreateAnimation("Alpha"); f.t.fadeOut:SetFromAlpha(1); f.t.fadeOut:SetToAlpha(0); f.t.fadeOut:SetDuration(0.20); f.t.fadeOut:SetOrder(3)

    f.t:SetScript("OnFinished", function()
      f:Hide(); playing = false
      if #Queue > 0 then
        local nxt = table.remove(Queue, 1)
        SRPWL._TalkingPlay(nxt.petKey, nxt.petName, nxt.action, nxt.text, nxt.icon, nxt.duration)
      end
    end)

    function f:PlaySequence(total)
      local hold = math.max(0.01, (tonumber(total) or DB.talking_duration or 4.5))
      f.t.hold:SetDuration(hold)
      f:SetAlpha(0); f:Show()
      if f.t:IsPlaying() then f.t:Stop() end
      f.t:Play()
    end

    function f:StopSequence()
      if f.t and f.t:IsPlaying() then f.t:Stop() end
      f:Hide()
      ticket = ticket + 1
    end

  else
    function f:PlaySequence(total)
      local my = ticket + 1
      ticket = my
      f:SetAlpha(1); f:Show()
      local dur = math.max(0.01, (tonumber(total) or DB.talking_duration or 4.5))
      C_Timer.After(dur, function()
        if my ~= ticket then return end
        f:Hide(); playing = false
        if #Queue > 0 then
          local nxt = table.remove(Queue, 1)
          SRPWL._TalkingPlay(nxt.petKey, nxt.petName, nxt.action, nxt.text, nxt.icon, nxt.duration)
        end
      end)
    end
    function f:StopSequence()
      ticket = ticket + 1
      f:Hide()
    end
  end

  f:SetScript("OnMouseDown", function(self, btn)
    if btn == "RightButton" and not DB.talking_lock then
      self:StopSequence(); playing = false; wipe(Queue)
    end
  end)

  local function ApplyScale()
    local s = (DB.global_scale or 1.0) * (DB.talking_scale or 1.0)
    f:SetScale(s)
  end
  f.ApplyScale = ApplyScale
  ApplyScale()

  f.UpdateRing = UpdateRing

  -- Masquage auto en instance si l’option est cochée
  local function ShouldHideForInstance()
    if not DB.talking_instsuppress then return false end
    local inInst = false
    if IsInInstance then
      local ok, _, instType = pcall(IsInInstance)
      inInst = ok and (instType ~= "none")
    end
    return inInst
  end

  f._instFrame = CreateFrame("Frame")
  f._instFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
  f._instFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
  f._instFrame:RegisterEvent("ZONE_CHANGED")
  f._instFrame:RegisterEvent("ZONE_CHANGED_INDOORS")
  f._instFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
  f._instFrame:SetScript("OnEvent", function()
    if ShouldHideForInstance() then
      f:Hide()
    end
  end)
end

-- ========= Instance helper (lecture au moment de parler) =========
local function InInstanceNow()
  if not DB.talking_instsuppress then return false end
  if IsInInstance then
    local ok, _, instType = pcall(IsInInstance)
    return ok and (instType ~= "none")
  end
  return false
end

-- ========= Play =========
function SRPWL._TalkingPlay(petKey, petName, actionLabel, text, icon, duration)
  if not DB.use_talking_style then return end
  if InInstanceNow() then return end

  BuildFrame()
  if f.ApplyScale then f:ApplyScale() end

  local tex = GetPetIconTexture(petKey, icon, petName)
  f.portrait:SetTexture(tex)

  -- Type affiché (canon dynamique)
  local canon = CanonicalPetKey(petKey, petName)
  local typePretty = GetLocalizedPetType(canon)
  local unitName = UnitName and UnitName("pet") or nil
  local displayName = unitName or petName or (petKey or "Pet")
  local header = (displayName .. " - " .. typePretty)
  f.name:SetText(header)

  local tint = canon and RING_TINT[canon]
  if tint then f.ring:SetVertexColor(tint[1], tint[2], tint[3]) else f.ring:SetVertexColor(1,1,1) end

  if DB.talking_debug then
    print("|cffa9a9ff[SRP]|r petKey=", tostring(petKey),
          " canon=", tostring(canon),
          " name=", tostring(displayName),
          " header=", tostring(header),
          " icon=", tostring(tex),
          " style=", tostring(DB.portrait_style))
  end

  f.action:SetText(actionLabel or "")
  f.text:SetText(text or "")

  local total = tonumber(duration) or tonumber(DB.talking_duration) or 4.5
  f:PlaySequence(total)
  playing = true
end

function SRPWL.ShowTalking(petKey, petPretty, actionLabel, text, icon, duration)
  if not DB.use_talking_style then return false end
  if InInstanceNow() then return false end
  local entry = {
    petKey   = petKey,
    petName  = petPretty or petKey or UnitName("pet") or "Pet",
    action   = actionLabel or "",
    text     = text or "",
    icon     = icon,
    duration = duration,
  }
  if playing then
    table.insert(Queue, entry)
  else
    SRPWL._TalkingPlay(entry.petKey, entry.petName, entry.action, entry.text, entry.icon, entry.duration)
  end
  return true
end

-- ========= Live refresh from Options UI =========
local function RefreshForKey(key)
  if key == "global_scale" or key == "talking_scale" then
    if f and f.ApplyScale then f:ApplyScale() end
  elseif key == "talking_point" then
    if f then f:ClearAllPoints(); f:SetPoint(unpack(DB.talking_point)) end
  elseif key == "portrait_style" then
    -- la prochaine réplique appliquera le nouveau style
  elseif key == "talking_portrait" or key == "talking_ring_scale" or key == "talking_ring_ofsx" or key == "talking_ring_ofsy" then
    if f and f.UpdateRing then f:UpdatePortraitSize() end
  elseif key == "talking_instsuppress" then
    if f then
      -- si on vient d'activer la suppression et qu'on est en instance, masquer
      if IsInInstance and select(2, IsInInstance()) ~= "none" then f:Hide() end
    end
  end
end

local oldHook = SRPWL.OnConfigChanged
SRPWL.OnConfigChanged = function(key, value)
  if type(oldHook) == "function" then pcall(oldHook, key, value, DB) end
  RefreshForKey(key)
end

-- ========= Preview helper =========
function _G.SRPWL_ShowTalkingHead(petDisplayName, title, text, iconPath, duration)
  local petType = nil
  SRPWL.ShowTalking(petType, petDisplayName or "Pet", title or "Preview", text or "This is a preview line.", iconPath, duration or DB.talking_duration or 4.5)
end

-- ========= Slash =========
SLASH_SRPWLTALK1 = "/srptalkui"
SlashCmdList["SRPWLTALK"] = function(msg)
  msg = (msg or ""):lower()
  if msg == "on" then
    DB.use_talking_style = true; print("|cffa9a9ff[SRP]|r Talking UI: ON")
  elseif msg == "off" then
    DB.use_talking_style = false; print("|cffa9a9ff[SRP]|r Talking UI: OFF")
  elseif msg == "lock" then
    DB.talking_lock = true; print("|cffa9a9ff[SRP]|r Talking UI locked.")
  elseif msg == "unlock" then
    DB.talking_lock = false; print("|cffa9a9ff[SRP]|r Talking UI unlocked (LeftDrag to move, RightClick to close).")
  elseif msg:match("^style%s") then
    local mode = msg:match("style%s+(%S+)")
    DB.portrait_style = (mode == "realistic") and "realistic" or "cartoon"
    print("|cffa9a9ff[SRP]|r Portrait style:", DB.portrait_style)
  else
    print("|cffa9a9ff[SRP]|r /srptalkui on|off | lock|unlock | style cartoon|realistic")
  end
end
