"""
Demonstration of pressed/depressed states for buttons and links with visual feedback.

This example shows:
1. How to use the InteractionHandler for automatic press/release cycles
2. How to manually manage states for custom event loops
3. How the dirty flag system tracks when re-rendering is needed
4. Visual differences between normal, hovered, and pressed states

The demo creates a page with buttons and links, then simulates clicking them
with proper visual feedback timing.
"""

from pyWebLayout.concrete import Page
from pyWebLayout.concrete.interaction_handler import InteractionHandler, InteractionStateManager
from pyWebLayout.abstract.functional import Button, Link, LinkType
from pyWebLayout.abstract import Paragraph, Word
from pyWebLayout.abstract.inline import LinkedWord
from pyWebLayout.style import Font
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.layout.document_layouter import DocumentLayouter
import numpy as np
import time


def create_interactive_demo_page():
    """
    Create a page with various interactive elements demonstrating state changes.
    """
    # Create page
    page = Page(size=(600, 500), style=PageStyle(border_width=10))
    layouter = DocumentLayouter(page)

    # Create fonts
    title_font = Font(font_size=24, colour=(0, 0, 100))
    body_font = Font(font_size=16, colour=(0, 0, 0))
    button_font = Font(font_size=14, colour=(255, 255, 255))

    # Title
    title = Paragraph(title_font)
    title.add_word(Word("Interactive", title_font))
    title.add_word(Word("Elements", title_font))
    title.add_word(Word("Demo", title_font))
    layouter.layout_paragraph(title)
    page._current_y_offset += 15

    # Description
    desc = Paragraph(body_font)
    desc.add_word(Word("Click", body_font))
    desc.add_word(Word("the", body_font))
    desc.add_word(Word("buttons", body_font))
    desc.add_word(Word("and", body_font))
    desc.add_word(Word("links", body_font))
    desc.add_word(Word("below", body_font))
    desc.add_word(Word("to", body_font))
    desc.add_word(Word("see", body_font))
    desc.add_word(Word("pressed", body_font))
    desc.add_word(Word("state", body_font))
    desc.add_word(Word("feedback!", body_font))
    layouter.layout_paragraph(desc)
    page._current_y_offset += 20

    # Callback functions
    def on_save():
        print("💾 Save button clicked!")
        return "saved"

    def on_cancel():
        print("❌ Cancel button clicked!")
        return "cancelled"

    def on_link_click(location, point):
        print(f"🔗 Link clicked: {location} at {point}")
        return location

    # Create buttons
    save_button = Button(
        label="Save Document",
        callback=lambda point, **kwargs: on_save(),
        html_id="save-btn"
    )

    cancel_button = Button(
        label="Cancel",
        callback=lambda point, **kwargs: on_cancel(),
        html_id="cancel-btn"
    )

    # Layout buttons
    success1, save_id = layouter.layout_button(save_button, font=button_font)
    page._current_y_offset += 12
    success2, cancel_id = layouter.layout_button(cancel_button, font=button_font)
    page._current_y_offset += 25

    # Create paragraph with links
    link_para = Paragraph(body_font)
    link_para.add_word(Word("Visit", body_font))
    link_para.add_word(Word("our", body_font))

    # Add a link
    internal_link = Link(
        location="https://example.com",
        link_type=LinkType.EXTERNAL,
        callback=on_link_click,
        title="Example website"
    )
    link_para.add_word(LinkedWord(
        "website",
        body_font,
        location="https://example.com",
        link_type=LinkType.EXTERNAL,
        callback=on_link_click,
        title="Example website"
    ))
    link_para.add_word(Word("or", body_font))

    # Add another link
    docs_link = Link(
        location="/docs",
        link_type=LinkType.INTERNAL,
        callback=on_link_click,
        title="Documentation"
    )
    link_para.add_word(LinkedWord(
        "documentation",
        body_font,
        location="/docs",
        link_type=LinkType.INTERNAL,
        callback=on_link_click,
        title="Documentation"
    ))
    link_para.add_word(Word("page.", body_font))

    layouter.layout_paragraph(link_para)

    return page, save_id, cancel_id


def demo_automatic_interaction():
    """
    Demonstrate automatic interaction handling with InteractionHandler.

    This shows the simplest usage pattern where InteractionHandler manages
    the complete press/release cycle automatically.
    """
    print("=" * 70)
    print("Demo 1: Automatic Interaction with Visual Feedback")
    print("=" * 70)
    print()

    # Create the page
    page, save_id, cancel_id = create_interactive_demo_page()

    # Create interaction handler
    handler = InteractionHandler(page, press_duration_ms=150)

    print("Initial render:")
    initial_render = page.render()
    initial_render.save("demo_07_initial.png")
    print(f"  ✓ Saved: demo_07_initial.png")
    print(f"  ✓ Page dirty flag: {page.is_dirty}")
    print()

    # Get the save button
    save_button = page.callbacks.get_by_id("save-btn")
    click_point = np.array([50, 150])

    print("Simulating button click with automatic feedback...")
    print(f"  → Setting pressed state at t=0ms")

    # Execute with automatic feedback
    pressed_frame, released_frame, result = handler.execute_with_feedback(
        save_button,
        click_point
    )

    print(f"  → Showing pressed state for 150ms")
    print(f"  → Executing callback")
    print(f"  → Result: {result}")
    print(f"  → Setting released state")

    # Save the frames
    pressed_frame.save("demo_07_pressed.png")
    print(f"  ✓ Saved: demo_07_pressed.png")

    released_frame.save("demo_07_released.png")
    print(f"  ✓ Saved: demo_07_released.png")
    print()


def demo_manual_state_management():
    """
    Demonstrate manual state management for custom event loops.

    This shows how an application with its own event loop can manage
    states and check the dirty flag before re-rendering.
    """
    print("=" * 70)
    print("Demo 2: Manual State Management with Dirty Flag Checking")
    print("=" * 70)
    print()

    # Create the page
    page, save_id, cancel_id = create_interactive_demo_page()

    # Initial render
    print("Initial render:")
    current_frame = page.render()
    print(f"  ✓ Page dirty: {page.is_dirty} (cleaned after render)")
    print()

    # Get the cancel button
    cancel_button = page.callbacks.get_by_id("cancel-btn")

    # Simulate mouse down
    print("Mouse down event:")
    # Set page reference if not already set
    if not hasattr(cancel_button, '_page') or cancel_button._page is None:
        cancel_button.set_page(page)
    cancel_button.set_pressed(True)
    print(f"  ✓ Set pressed state")
    print(f"  ✓ Page dirty: {page.is_dirty} (needs re-render)")

    # Check if we need to re-render
    if page.is_dirty:
        print("  → Re-rendering (dirty flag is set)")
        current_frame = page.render()
        current_frame.save("demo_07_manual_pressed.png")
        print(f"  ✓ Saved: demo_07_manual_pressed.png")
        print(f"  ✓ Page dirty: {page.is_dirty} (cleaned after render)")
    print()

    # Wait a bit
    print("Waiting 150ms for visual feedback...")
    time.sleep(0.15)
    print()

    # Execute callback
    print("Executing callback:")
    result = cancel_button.interact(np.array([50, 200]))
    print(f"  ✓ Result: {result}")
    print()

    # Simulate mouse up
    print("Mouse up event:")
    cancel_button.set_pressed(False)
    print(f"  ✓ Set released state")
    print(f"  ✓ Page dirty: {page.is_dirty} (needs re-render)")

    # Check if we need to re-render
    if page.is_dirty:
        print("  → Re-rendering (dirty flag is set)")
        current_frame = page.render()
        current_frame.save("demo_07_manual_released.png")
        print(f"  ✓ Saved: demo_07_manual_released.png")
        print(f"  ✓ Page dirty: {page.is_dirty} (cleaned after render)")
    print()


def demo_state_manager():
    """
    Demonstrate the InteractionStateManager for hover/press tracking.

    This shows how to use the high-level state manager that automatically
    handles hover and press states based on cursor position.
    """
    print("=" * 70)
    print("Demo 3: InteractionStateManager for Hover and Press Tracking")
    print("=" * 70)
    print()

    # Create the page
    page, save_id, cancel_id = create_interactive_demo_page()

    # Create state manager
    state_mgr = InteractionStateManager(page)

    # Initial render
    print("Initial render:")
    current_frame = page.render()
    print(f"  ✓ Rendered initial state")
    print()

    # Simulate cursor moving over a button
    button_center = (150, 150)
    print(f"Cursor moves to button position {button_center}:")
    hover_frame = state_mgr.update_hover(button_center)
    if hover_frame:
        print(f"  ✓ Hover state changed, page re-rendered")
        hover_frame.save("demo_07_hover.png")
        print(f"  ✓ Saved: demo_07_hover.png")
    print()

    # Simulate mouse down
    print(f"Mouse down at {button_center}:")
    pressed_frame = state_mgr.handle_mouse_down(button_center)
    if pressed_frame:
        print(f"  ✓ Pressed state set, page re-rendered")
        pressed_frame.save("demo_07_state_mgr_pressed.png")
        print(f"  ✓ Saved: demo_07_state_mgr_pressed.png")
    print()

    # Wait for visual feedback
    time.sleep(0.15)

    # Simulate mouse up
    print(f"Mouse up at {button_center}:")
    released_frame, result = state_mgr.handle_mouse_up(button_center)
    if released_frame:
        print(f"  ✓ Released state set, page re-rendered")
        print(f"  ✓ Callback result: {result}")
        released_frame.save("demo_07_state_mgr_released.png")
        print(f"  ✓ Saved: demo_07_state_mgr_released.png")
    print()

    # Simulate cursor moving away
    away_point = (50, 50)
    print(f"Cursor moves away to {away_point}:")
    away_frame = state_mgr.update_hover(away_point)
    if away_frame:
        print(f"  ✓ Hover state cleared, page re-rendered")
        away_frame.save("demo_07_no_hover.png")
        print(f"  ✓ Saved: demo_07_no_hover.png")
    print()


def demo_performance_optimization():
    """
    Demonstrate how the dirty flag prevents unnecessary re-renders.
    """
    print("=" * 70)
    print("Demo 4: Performance Optimization with Dirty Flag")
    print("=" * 70)
    print()

    # Create the page
    page, save_id, cancel_id = create_interactive_demo_page()

    print("Scenario: Multiple state queries without changes")
    print()

    # Initial render
    page.render()
    print(f"1. After initial render - dirty: {page.is_dirty}")

    # Check if dirty before rendering again
    print(f"2. Check dirty flag: {page.is_dirty}")
    if not page.is_dirty:
        print("   → Skipping render (no changes)")
    print()

    # Now make a change
    button = page.callbacks.get_by_id("save-btn")
    print("3. Setting button to pressed state")
    # Ensure page reference is set
    if not hasattr(button, '_page') or button._page is None:
        button.set_page(page)
    button.set_pressed(True)
    print(f"   → dirty: {page.is_dirty}")
    print()

    # This time we need to render
    print(f"4. Check dirty flag: {page.is_dirty}")
    if page.is_dirty:
        print("   → Re-rendering (state changed)")
        page.render()
        print(f"   → dirty after render: {page.is_dirty}")
    print()

    print("Benefit: Only render when actual changes occur!")
    print()


def create_animated_gif():
    """
    Create an animated GIF showing the button press sequence.
    """
    from PIL import Image
    import os

    print("=" * 70)
    print("Creating Animated GIF")
    print("=" * 70)
    print()

    # Check if the PNG files exist
    png_files = [
        "demo_07_initial.png",
        "demo_07_pressed.png",
        "demo_07_released.png"
    ]

    if not all(os.path.exists(f) for f in png_files):
        print("  ⚠ PNG files not found, skipping GIF creation")
        return

    # Load the images
    initial = Image.open('demo_07_initial.png')
    pressed = Image.open('demo_07_pressed.png')
    released = Image.open('demo_07_released.png')

    # Create animated GIF showing the button interaction sequence
    # Sequence: initial (1000ms) -> pressed (200ms) -> released (500ms) -> loop
    frames = [initial, pressed, released]
    durations = [1000, 200, 500]  # milliseconds per frame

    output_path = "docs/images/example_07_button_animation.gif"

    # Create docs/images directory if it doesn't exist
    os.makedirs("docs/images", exist_ok=True)

    # Save as animated GIF
    initial.save(
        output_path,
        save_all=True,
        append_images=[pressed, released],
        duration=durations,
        loop=0  # 0 means loop forever
    )

    print(f"  ✓ Created: {output_path}")
    print(f"  ✓ Frames: {len(frames)}")
    print(f"  ✓ Sequence: initial (1000ms) → pressed (200ms) → released (500ms)")
    print()


if __name__ == "__main__":
    print("\n")
    print("╔" + "═" * 68 + "╗")
    print("║" + " " * 15 + "PRESSED STATE DEMONSTRATION" + " " * 26 + "║")
    print("╚" + "═" * 68 + "╝")
    print()

    # Run all demos
    demo_automatic_interaction()
    print("\n")

    demo_manual_state_management()
    print("\n")

    demo_state_manager()
    print("\n")

    demo_performance_optimization()
    print("\n")

    # Create animated GIF
    create_animated_gif()

    print("=" * 70)
    print("All demos complete! Check the generated PNG files and animated GIF.")
    print("=" * 70)
