"""
Demonstration of functional elements (buttons, forms, links) with callback binding.

This example shows how to:
1. Create functional elements programmatically
2. Layout them on a page
3. Bind callbacks after layout using the CallbackRegistry
4. Simulate user interactions

This pattern is useful for:
- Manual GUI construction
- Applications where callbacks need access to runtime state
- Interactive document interfaces
"""

from pyWebLayout.concrete import Page
from pyWebLayout.abstract.functional import Button, Form, FormField, FormFieldType
from pyWebLayout.abstract import Paragraph, Word
from pyWebLayout.style import Font
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.layout.document_layouter import DocumentLayouter
import numpy as np


class SimpleApp:
    """
    A simple application that demonstrates functional element usage.

    This app has:
    - A settings form
    - Save and Cancel buttons
    - Application state that callbacks can access
    """

    def __init__(self):
        self.settings = {
            "username": "",
            "theme": "light",
            "notifications": True
        }
        self.saved = False

    def on_save_click(self, point, **kwargs):
        """Callback for save button"""
        print(f"Save button clicked at {point}")
        self.saved = True
        print("Settings saved!")
        return "saved"

    def on_cancel_click(self, point, **kwargs):
        """Callback for cancel button"""
        print(f"Cancel button clicked at {point}")
        print("Changes cancelled!")
        return "cancelled"

    def on_reset_click(self, point, **kwargs):
        """Callback for reset button"""
        print(f"Reset button clicked at {point}")
        self.settings = {
            "username": "",
            "theme": "light",
            "notifications": True
        }
        print("Settings reset to defaults!")
        return "reset"


def create_settings_page():
    """
    Create a settings page with functional elements.

    Returns:
        Tuple of (page, app, element_ids) where element_ids maps
        semantic names to registered callback ids
    """
    # Create the application instance
    app = SimpleApp()

    # Create page
    page = Page(size=(600, 800), style=PageStyle(border_width=10))
    layouter = DocumentLayouter(page)

    # Create content
    font = Font(font_size=16, colour=(0, 0, 0))

    # Title paragraph
    title_font = Font(font_size=24, colour=(0, 0, 100))
    title = Paragraph(title_font)
    title.add_word(Word("Settings", title_font))

    # Description paragraph
    desc = Paragraph(font)
    desc.add_word(Word("Configure", font))
    desc.add_word(Word("your", font))
    desc.add_word(Word("application", font))
    desc.add_word(Word("preferences", font))
    desc.add_word(Word("below.", font))

    # Layout title and description
    layouter.layout_paragraph(title)
    page._current_y_offset += 10  # Add some spacing
    layouter.layout_paragraph(desc)
    page._current_y_offset += 20  # Add more spacing before form

    # Create form
    settings_form = Form(
        form_id="settings-form",
        action="/save-settings",
        html_id="settings-form"
    )

    # Add form fields
    username_field = FormField(
        name="username",
        field_type=FormFieldType.TEXT,
        label="Username",
        value="john_doe"
    )

    theme_field = FormField(
        name="theme",
        field_type=FormFieldType.SELECT,
        label="Theme",
        value="light",
        options=[("light", "Light"), ("dark", "Dark")]
    )

    notifications_field = FormField(
        name="notifications",
        field_type=FormFieldType.CHECKBOX,
        label="Enable Notifications",
        value=True
    )

    settings_form.add_field(username_field)
    settings_form.add_field(theme_field)
    settings_form.add_field(notifications_field)

    # Layout the form
    success, field_ids = layouter.layout_form(settings_form)

    if not success:
        print("Warning: Form didn't fit on page!")

    page._current_y_offset += 20  # Spacing before buttons

    # Create buttons (NO callbacks yet - will be bound later)
    save_button = Button(
        label="Save Settings",
        callback=None,  # No callback yet!
        html_id="save-btn"
    )

    cancel_button = Button(
        label="Cancel",
        callback=None,  # No callback yet!
        html_id="cancel-btn"
    )

    reset_button = Button(
        label="Reset to Defaults",
        callback=None,  # No callback yet!
        html_id="reset-btn"
    )

    # Layout buttons
    button_font = Font(font_size=14, colour=(255, 255, 255))
    success1, save_id = layouter.layout_button(save_button, font=button_font)
    page._current_y_offset += 10  # Spacing between buttons
    success2, cancel_id = layouter.layout_button(cancel_button, font=button_font)
    page._current_y_offset += 10
    success3, reset_id = layouter.layout_button(reset_button, font=button_font)

    # ==============================================================
    # IMPORTANT: Callbacks are bound AFTER layout is complete
    # This allows callbacks to access the application instance
    # ==============================================================

    # Bind callbacks using the page's callback registry
    page.callbacks.set_callback("save-btn", app.on_save_click)
    page.callbacks.set_callback("cancel-btn", app.on_cancel_click)
    page.callbacks.set_callback("reset-btn", app.on_reset_click)

    # Track element ids for later reference
    element_ids = {
        "save_button": save_id,
        "cancel_button": cancel_id,
        "reset_button": reset_id,
        "form_fields": field_ids
    }

    return page, app, element_ids


def demonstrate_callback_binding():
    """Demonstrate various callback binding patterns"""

    print("=" * 60)
    print("Functional Elements Demo: Manual GUI Construction")
    print("=" * 60)
    print()

    # Create the page
    page, app, element_ids = create_settings_page()

    print(f"Page created with {page.callbacks.count()} interactable elements")
    print()

    # Show what's registered
    print("Registered interactables:")
    for id_name in page.callbacks.get_all_ids():
        print(f"  - {id_name}")
    print()

    # Show breakdown by type
    print("Breakdown by type:")
    for type_name in page.callbacks.get_all_types():
        count = page.callbacks.count_by_type(type_name)
        print(f"  - {type_name}: {count}")
    print()

    # Simulate user clicking the save button
    print("Simulating user interaction:")
    print("-" * 60)
    print()

    # Get the save button
    save_button = page.callbacks.get_by_id("save-btn")
    print(f"Retrieved save button: {save_button}")

    # Simulate a click at position (50, 200)
    click_point = np.array([50, 200])
    print(f"Simulating click at {click_point}...")
    result = save_button.interact(click_point)
    print(f"Button interaction returned: {result}")
    print(f"App.saved state: {app.saved}")
    print()

    # Simulate clicking cancel
    cancel_button = page.callbacks.get_by_id("cancel-btn")
    print("Simulating cancel button click...")
    result = cancel_button.interact(click_point)
    print(f"Button interaction returned: {result}")
    print()

    # Simulate clicking reset
    reset_button = page.callbacks.get_by_id("reset-btn")
    print("Simulating reset button click...")
    result = reset_button.interact(click_point)
    print(f"Button interaction returned: {result}")
    print()

    # Demonstrate batch callback modification
    print("-" * 60)
    print("Demonstrating batch callback modification:")
    print()

    def log_all_clicks(point, **kwargs):
        """Generic click logger"""
        print(f"  [LOG] Button clicked at {point}")
        return "logged"

    # Set this callback for all buttons
    count = page.callbacks.set_callbacks_by_type("button", log_all_clicks)
    print(f"Set logging callback for {count} buttons")
    print()

    # Now clicking any button will just log
    print("Clicking save button again (now with logging callback):")
    result = save_button.interact(click_point)
    print(f"Returned: {result}")
    print()

    # Render the page
    print("-" * 60)
    print("Rendering page...")
    image = page.render()
    print(f"Page rendered: {image.size}")

    # Save to file
    output_path = "functional_elements_demo.png"
    image.save(output_path)
    print(f"Saved to: {output_path}")
    print()

    print("=" * 60)
    print("Demo complete!")
    print("=" * 60)


if __name__ == "__main__":
    demonstrate_callback_binding()
