#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Immersive Book Animation Overhaul v8.5
- BookOpenEffect: 6-phase epic sequence
- ShowBook: custom 0.08->1.08->1.0 scale tween (vs boring 0.9->1.0)
- HideBook: puff + collapse close animation
- pageOutAnim / pageInAnim: shimmer + glow on chapter switch
"""

BASE = r"C:\Users\Norme\Documents\verdent-projects\new-project\GuildDeathLog"

# ═══════════════════════════════════════════════════════════════════════════
# 1. ANIMFX.LUA — Replace BookOpenEffect
# ═══════════════════════════════════════════════════════════════════════════
anim_path = BASE + r"\Modules\AnimFX.lua"
with open(anim_path, 'r', encoding='utf-8') as f:
    anim = f.read()

OLD_BOE = '''function AnimFX.BookOpenEffect(bookFrame)
    -- Dust particles rising from the book
    local cx, cy = bookFrame:GetCenter()
    if cx and cy then
        local relX = cx - UIParent:GetWidth()  * 0.5
        local relY = cy - UIParent:GetHeight() * 0.5
        AnimFX.ParticleBurst(relX, relY - 55, 20, 0.85, 0.72, 0.38, 55, 1.6)
        AnimFX.ParticleBurst(relX, relY + 55, 14, 0.78, 0.62, 0.30, 40, 1.3)
    end
    -- Warm amber flash
    AnimFX.ScreenFlash(0.72, 0.55, 0.18, 0.65, 0.12)
    -- Shimmer sweep across the book
    C_Timer.After(0.30, function()
        AnimFX.ShimmerSweep(bookFrame, 0.95, 0.78, 0.35, 0.85)
    end)
    -- Rune orbit
    C_Timer.After(0.05, function()
        AnimFX.RuneCircle(bookFrame, 0.95, 0.78, 0.35, 65, 1.0)
    end)
end'''

NEW_BOE = '''function AnimFX.BookOpenEffect(bookFrame)
    -- ════════════════════════════════════════════════════════════════════
    -- DAS GRIMOIRE ERWACHT — 6-stufige Kino-Sequenz
    -- ════════════════════════════════════════════════════════════════════
    local W  = bookFrame:GetWidth()  or 800
    local H  = bookFrame:GetHeight() or 500
    local cx, cy = bookFrame:GetCenter()
    local relX = cx and (cx - UIParent:GetWidth()  * 0.5) or 0
    local relY = cy and (cy - UIParent:GetHeight() * 0.5) or 0

    -- ★ t=0.00  Phase 1: Warmer Gold-Flash (Erwachen)
    AnimFX.ScreenFlash(0.88, 0.70, 0.22, 0.80, 0.16)

    -- ★ t=0.00  Phase 1: Erster Rune-Ring expandiert nach außen
    AnimFX.GlowRing(bookFrame, 0.95, 0.78, 0.28, W * 0.42, 0.88)

    -- ★ t=0.00  Phase 1: Kleiner RuneCircle (innen, schnell)
    AnimFX.RuneCircle(bookFrame, 0.95, 0.78, 0.35, 58, 0.90)

    -- ★ t=0.06  Phase 2: Staub steigt aus den unteren Ecken auf
    AnimFX.ParticleBurst(relX - W * 0.30, relY - H * 0.28, 16, 0.78, 0.62, 0.24, 52, 2.4)
    AnimFX.ParticleBurst(relX + W * 0.30, relY - H * 0.28, 16, 0.78, 0.62, 0.24, 52, 2.4)
    AnimFX.ParticleBurst(relX,             relY - H * 0.18, 12, 0.70, 0.56, 0.20, 40, 2.0)
    AnimFX.ParticleBurst(relX,             relY + H * 0.10,  8, 0.85, 0.70, 0.30, 35, 1.6)

    -- ★ t=0.38  Phase 3: AUFPRALL — Buch liegt offen
    C_Timer.After(0.38, function()
        if not bookFrame:IsShown() then return end
        -- Subtiles Aufprall-Zittern
        AnimFX.ShakeFrame(bookFrame, 5, 0.24)
        -- Innerer GlowRing (kompakt, heiß)
        AnimFX.GlowRing(bookFrame, 0.95, 0.78, 0.28, W * 0.76, 0.95)
        -- Äußerer GlowRing (weit, nach Verzögerung)
        C_Timer.After(0.16, function()
            if bookFrame:IsShown() then
                AnimFX.GlowRing(bookFrame, 0.82, 0.52, 0.14, W * 1.12, 0.82)
            end
        end)
        -- RuneCircle (mittlerer Ring, außen)
        AnimFX.RuneCircle(bookFrame, 0.90, 0.70, 0.20, 92, 1.40)
        -- Partikel-Explosion: 3 Schichten (Klasse + Gold + Weiß)
        AnimFX.ParticleBurst(relX, relY,       28, 0.95, 0.78, 0.26, 118, 1.90)
        AnimFX.ParticleBurst(relX, relY,       16, 1.00, 0.94, 0.55,  65, 1.35)
        AnimFX.ParticleBurst(relX, relY,        9, 1.00, 1.00, 1.00,  44, 0.95)
    end)

    -- ★ t=0.55  Phase 4: Shimmer-Welle über das Buch
    C_Timer.After(0.55, function()
        if bookFrame:IsShown() then
            AnimFX.ShimmerSweep(bookFrame, 0.96, 0.82, 0.30, 0.92)
        end
    end)

    -- ★ t=0.80  Phase 5: Zweiter warmer Gold-Flash (sehr subtil)
    C_Timer.After(0.80, function()
        if bookFrame:IsShown() then
            AnimFX.ScreenFlash(0.75, 0.55, 0.15, 0.60, 0.10)
        end
    end)

    -- ★ t=1.10  Phase 6: Äußerster RuneCircle (langsam, majestätisch)
    C_Timer.After(1.10, function()
        if bookFrame:IsShown() then
            AnimFX.RuneCircle(bookFrame, 0.72, 0.48, 0.10, 118, 1.80)
            AnimFX.GlowRing(bookFrame, 0.65, 0.42, 0.08, W * 1.35, 0.55)
        end
    end)
end'''

if OLD_BOE in anim:
    anim = anim.replace(OLD_BOE, NEW_BOE)
    print("AnimFX: BookOpenEffect replaced successfully")
else:
    # Fallback: line-based replacement
    lines = anim.splitlines(keepends=True)
    start = None
    for i, l in enumerate(lines):
        if 'function AnimFX.BookOpenEffect' in l:
            start = i; break
    if start is not None:
        end = start + 1
        depth = 1
        while end < len(lines) and depth > 0:
            s = lines[end].strip()
            if s.startswith('function ') or s == 'do':
                depth += 1
            elif s == 'end' or s.startswith('end ') or s.startswith('end--'):
                depth -= 1
            end += 1
        lines = lines[:start] + [NEW_BOE + '\n'] + lines[end:]
        anim = ''.join(lines)
        print(f"AnimFX: BookOpenEffect replaced via line-based (lines {start+1}-{end})")
    else:
        print("AnimFX: ERROR - BookOpenEffect not found!")

with open(anim_path, 'w', encoding='utf-8') as f:
    f.write(anim)
print("AnimFX.lua saved")

# ═══════════════════════════════════════════════════════════════════════════
# 2. UI.LUA — Replace ShowBook + HideBook
# ═══════════════════════════════════════════════════════════════════════════
ui_path = BASE + r"\Modules\UI.lua"
with open(ui_path, 'r', encoding='utf-8') as f:
    ui = f.read()

# ── ShowBook ──────────────────────────────────────────────────────────────
OLD_SHOW = '''function UI:ShowBook()
    if self.mainFrame then
        local f = self.mainFrame
        if f:IsShown() then f:Raise(); return end
        if f.closeAnim and f.closeAnim:IsPlaying() then f.closeAnim:Stop() end
        f:SetAlpha(0)
        f:Show()
        if f.openAnim then
            f.openAnim:Stop()
            GDL_ScaleLerp(f, 0.9, 1.0, 0.35, "OUT")
            f.openAnim:Play()
        end
        -- ─── BOOK OPEN EFFECT + LEBENDIGE ATMOSPHÄRE ─────────────────────
        C_Timer.After(0.12, function()
            local FX = GDL:GetModule("AnimFX")
            if FX then
                FX.BookOpenEffect(f)
                -- Glut-Funken rund ums Buch
                if self._emberHandle then self._emberHandle.active = false end
                self._emberHandle = FX.EmberAmbience(f, 10, 1.0, 0.45, 0.06)
                -- Kerzen-Glühen links und rechts
                -- Atmendes Außenleuchten
                if self._breathGlowAG then self._breathGlowAG:Stop() end
                if self._breathGlow   then self._breathGlow:Hide()   end
                self._breathGlow, self._breathGlowAG = FX.BreathGlow(f, 0.92, 0.72, 0.22, 4.0, 0.35)
            end
        end)
    end
end'''

NEW_SHOW = '''function UI:ShowBook()
    if not self.mainFrame then return end
    local f = self.mainFrame
    if f:IsShown() then f:Raise(); return end

    -- Stop any running close animation or tween
    if f.closeAnim and f.closeAnim:IsPlaying() then f.closeAnim:Stop() end
    if f.openAnim  and f.openAnim:IsPlaying()  then f.openAnim:Stop()  end
    f:SetScript("OnUpdate", nil)

    local savedScale = (GuildDeathLogDB and GuildDeathLogDB.bookScale) or 1.0
    local savedPos   = GuildDeathLogDB and GuildDeathLogDB.bookPosition
    if savedPos and savedPos.point then
        f:ClearAllPoints()
        f:SetPoint(savedPos.point, UIParent, savedPos.relPoint, savedPos.x, savedPos.y)
    else
        f:ClearAllPoints()
        f:SetPoint("CENTER", UIParent, "CENTER", 0, 40)
    end

    -- ── DRAMATISCHES EINBLENDEN: Winzig → Überschießen → Fertig ──────────
    -- Phase 1  0.00 – 0.42s   scale 0.06 → 1.08  (kubisch ease-out + alpha 0→1)
    -- Phase 2  0.42 – 0.54s   scale 1.08 → 1.00  (einfedern)
    f:SetAlpha(0)
    f:SetScale(savedScale * 0.06)
    f:Show()

    local el = 0
    local P1, P2 = 0.42, 0.12
    local math_min3 = math.min
    f:SetScript("OnUpdate", function(self, dt)
        el = el + dt
        if el <= P1 then
            local p = el / P1
            local e = 1 - (1 - p) * (1 - p) * (1 - p)   -- cubic ease-out
            self:SetScale(savedScale * (0.06 + e * 1.02))
            self:SetAlpha(math_min3(1, e * 1.7))
        elseif el <= P1 + P2 then
            local p = (el - P1) / P2
            self:SetScale(savedScale * (1.08 - p * 0.08))
            self:SetAlpha(1)
        else
            self:SetScript("OnUpdate", nil)
            self:SetScale(savedScale)
            self:SetAlpha(1)
        end
    end)

    -- ── CINEMATIC EFFEKTE ─────────────────────────────────────────────────
    local FX = GDL:GetModule("AnimFX")
    if FX then
        -- t=0.05: BookOpenEffect startet (6-phasige Sequenz)
        C_Timer.After(0.05, function()
            if f:IsShown() then FX.BookOpenEffect(f) end
        end)
        -- t=0.55: Persistente Ambience (Ember + Atem-Leuchten)
        C_Timer.After(0.55, function()
            if not f:IsShown() then return end
            if self._emberHandle  then self._emberHandle.active = false  end
            if self._breathGlowAG then self._breathGlowAG:Stop()        end
            if self._breathGlow   then self._breathGlow:Hide()          end
            self._emberHandle                       = FX.EmberAmbience(f, 10, 1.0, 0.45, 0.06)
            self._breathGlow, self._breathGlowAG    = FX.BreathGlow(f, 0.92, 0.72, 0.22, 4.0, 0.35)
        end)
    end
end'''

# ── HideBook ──────────────────────────────────────────────────────────────
OLD_HIDE = '''function UI:HideBook()
    if self.mainFrame then
        local f = self.mainFrame
        if not f:IsShown() then return end
        -- Atmosphäre stoppen
        if self._emberHandle   then self._emberHandle.active = false; self._emberHandle   = nil end
        if self._breathGlowAG  then self._breathGlowAG:Stop();       self._breathGlowAG  = nil end
        if self._breathGlow    then self._breathGlow:Hide();          self._breathGlow    = nil end
        if f.closeAnim then
            if f.openAnim and f.openAnim:IsPlaying() then f.openAnim:Stop() end
            f:SetAlpha(1)
            f.closeAnim:Stop()
            GDL_ScaleLerp(f, 1.0, 0.92, 0.18, "IN")
            f.closeAnim:Play()
        else
            f:Hide()
        end
    end
end'''

NEW_HIDE = '''function UI:HideBook()
    if not self.mainFrame then return end
    local f = self.mainFrame
    if not f:IsShown() then return end

    -- Ambience sofort stoppen
    if self._emberHandle   then self._emberHandle.active = false; self._emberHandle   = nil end
    if self._breathGlowAG  then self._breathGlowAG:Stop();       self._breathGlowAG  = nil end
    if self._breathGlow    then self._breathGlow:Hide();          self._breathGlow    = nil end
    if f.openAnim  and f.openAnim:IsPlaying()  then f.openAnim:Stop()  end
    if f.closeAnim and f.closeAnim:IsPlaying() then f.closeAnim:Stop() end
    f:SetScript("OnUpdate", nil)

    -- Flash nach außen beim Schließen
    local FX = GDL:GetModule("AnimFX")
    if FX then
        FX.GlowRing(f, 0.88, 0.68, 0.20, (f:GetWidth() or 800) * 0.90, 0.78)
        AnimFX.ScreenFlash(0.65, 0.48, 0.12, 0.28, 0.14)
    end

    -- Schließ-Tween: kurzes Aufpusten → Zusammenfalten → Hide
    local savedScale = (GuildDeathLogDB and GuildDeathLogDB.bookScale) or 1.0
    local el = 0
    local PUFF  = 0.07   -- kurzes Aufpusten
    local CLOSE = 0.26   -- Zusammenfallen
    f:SetScript("OnUpdate", function(self, dt)
        el = el + dt
        if el <= PUFF then
            local p = el / PUFF
            self:SetScale(savedScale * (1.0 + p * 0.05))
            self:SetAlpha(1)
        elseif el <= PUFF + CLOSE then
            local p = (el - PUFF) / CLOSE
            local e = p * p * p           -- cubic ease-in
            self:SetScale(savedScale * (1.05 - e * 1.01))
            self:SetAlpha(1 - p * p)
        else
            self:SetScript("OnUpdate", nil)
            self:Hide()
            self:SetAlpha(1)
            self:SetScale(savedScale)
        end
    end)
end'''

changed = 0
if OLD_SHOW in ui:
    ui = ui.replace(OLD_SHOW, NEW_SHOW)
    changed += 1
    print("UI: ShowBook replaced")
else:
    print("UI: WARNING - ShowBook not found exactly, trying line-based")
    lines = ui.splitlines(keepends=True)
    start = None
    for i, l in enumerate(lines):
        if 'function UI:ShowBook()' in l:
            start = i; break
    if start is not None:
        end = start + 1
        brace = 1
        while end < len(lines) and brace > 0:
            s = lines[end].strip()
            if s.startswith('function ') or s == 'do' or s.endswith(' do'):
                brace += 1
            elif s == 'end' or s.startswith('end ') or s == 'end)':
                brace -= 1
            end += 1
        lines = lines[:start] + [NEW_SHOW + '\n\n'] + lines[end:]
        ui = ''.join(lines)
        changed += 1
        print(f"UI: ShowBook replaced via line-based (lines {start+1}-{end})")
    else:
        print("UI: ERROR - ShowBook function not found!")

if OLD_HIDE in ui:
    ui = ui.replace(OLD_HIDE, NEW_HIDE)
    changed += 1
    print("UI: HideBook replaced")
else:
    print("UI: WARNING - HideBook not found exactly, trying line-based")
    lines = ui.splitlines(keepends=True)
    start = None
    for i, l in enumerate(lines):
        if 'function UI:HideBook()' in l:
            start = i; break
    if start is not None:
        end = start + 1
        brace = 1
        while end < len(lines) and brace > 0:
            s = lines[end].strip()
            if s.startswith('function ') or s == 'do' or s.endswith(' do'):
                brace += 1
            elif s == 'end' or s.startswith('end ') or s == 'end)':
                brace -= 1
            end += 1
        lines = lines[:start] + [NEW_HIDE + '\n\n'] + lines[end:]
        ui = ''.join(lines)
        changed += 1
        print(f"UI: HideBook replaced via line-based (lines {start+1}-{end})")
    else:
        print("UI: ERROR - HideBook function not found!")

# ── pageOutAnim / pageInAnim: Shimmer + Glow beim Kapitelwechsel ──────────
# Add shimmer to pageInAnim OnFinished
OLD_PAGE_IN = 'f.pageInAnim:SetScript("OnFinished", function() rp:SetAlpha(1) end)'
NEW_PAGE_IN = '''f.pageInAnim:SetScript("OnFinished", function()
        rp:SetAlpha(1)
        local FX2 = GDL:GetModule("AnimFX")
        if FX2 then
            FX2.ShimmerSweep(rp, 0.95, 0.78, 0.25, 0.65)
            FX2.GlowRing(rp, 0.88, 0.68, 0.22, (rp:GetWidth() or 400) * 0.80, 0.70)
        end
    end)'''

if OLD_PAGE_IN in ui:
    ui = ui.replace(OLD_PAGE_IN, NEW_PAGE_IN)
    changed += 1
    print("UI: pageInAnim shimmer+glow added")
else:
    print("UI: pageInAnim OnFinished not found (may already be updated)")

# Extend pageOutAnim duration slightly for smoother feel (0.1 -> 0.18)
OLD_PAGE_OUT_DUR = 'pageOutA:SetDuration(0.1); pageOutA:SetSmoothing("IN")'
NEW_PAGE_OUT_DUR = 'pageOutA:SetDuration(0.18); pageOutA:SetSmoothing("IN")'
if OLD_PAGE_OUT_DUR in ui:
    ui = ui.replace(OLD_PAGE_OUT_DUR, NEW_PAGE_OUT_DUR)
    changed += 1
    print("UI: pageOutAnim duration 0.1->0.18")

# Extend pageInAnim duration slightly (0.2 -> 0.26)
OLD_PAGE_IN_DUR = 'pageInA:SetDuration(0.2); pageInA:SetSmoothing("OUT")'
NEW_PAGE_IN_DUR = 'pageInA:SetDuration(0.26); pageInA:SetSmoothing("OUT")'
if OLD_PAGE_IN_DUR in ui:
    ui = ui.replace(OLD_PAGE_IN_DUR, NEW_PAGE_IN_DUR)
    changed += 1
    print("UI: pageInAnim duration 0.2->0.26")

with open(ui_path, 'w', encoding='utf-8') as f:
    f.write(ui)
print(f"\nUI.lua saved ({changed} changes)")
print("\n=== ALL PATCHES APPLIED ===")
