#
# backend/tokenizer_playground/__init__.py
#
from __future__ import annotations
from typing import Any, Callable, List, Literal, Sequence

from gradio.components.base import Component, FormComponent
from gradio.events import Events
import gradio as gr

class TokenizerTextBox(FormComponent):
    """
    Creates a textarea for user to enter string input or display string output,
    with built-in, client-side tokenization visualization powered by Transformers.js.
    The component's value is a JSON object containing the text and tokenization results.
    """
    EVENTS = [Events.change, Events.input, Events.submit, Events.blur, Events.select]

    def __init__(
        self,
        value: str | dict | Callable | None = None,
        *,
        # Custom parameters for this component
        model: str = "Xenova/gpt-3",
        display_mode: Literal['text', 'token_ids', 'hidden'] = 'text',
        hide_input: bool = False,           
        lines: int = 2,
        max_lines: int | None = None,
        placeholder: str | None = None,
        autofocus: bool = False,
        autoscroll: bool = True,
        text_align: Literal["left", "right"] | None = None,
        rtl: bool = False,
        show_copy_button: bool = False,
        max_length: int | None = None,
        
        # Standard FormComponent props that are passed to super()
        label: str | None = None,
        info: str | None = None,
        every: float | None = None,
        show_label: bool = True,
        container: bool = True,
        scale: int | None = None,
        min_width: int = 160,
        interactive: bool | None = None,
        visible: bool = True,
        elem_id: str | None = None,
        elem_classes: list[str] | str | None = None,      
      
        **kwargs,
    ):
        """
        Initializes the TokenizerTextBox component.

        Parameters:
            value: The initial value. Can be a string to initialize the text, or a dictionary for full state. If a function is provided, it will be called when the app loads to set the initial value.
            model: The name of a Hugging Face tokenizer to use (must be compatible with Transformers.js). Defaults to "Xenova/gpt-2".
            display_mode: Controls the content of the token visualization panel. Can be 'text' (default), 'token_ids', or 'hidden'.
            hide_input: If True, the component's own textbox is hidden, turning it into a read-only visualizer. Defaults to False.
            lines: The minimum number of line rows for the textarea.
            max_lines: The maximum number of line rows for the textarea.
            placeholder: A placeholder hint to display in the textarea when it is empty.
            label: The label for this component, displayed above the component.
            info: Additional component description, displayed below the label.
            every: If `value` is a callable, this sets a timer to run the function repeatedly.
            show_label: If False, the label is not displayed.
            container: If False, the component will not be wrapped in a container.
            scale: The relative size of the component compared to others in a `gr.Row` or `gr.Column`.
            min_width: The minimum-width of the component in pixels.
            interactive: If False, the user will not be able to edit the text.
            visible: If False, the component will be hidden.
            elem_id: An optional string that is assigned as the id of this component in the HTML DOM.
            elem_classes: An optional list of strings that are assigned as the classes of this component in the HTML DOM.
            autofocus: If True, will focus on the textbox when the page loads.
            autoscroll: If True, will automatically scroll to the bottom of the textbox when the value changes.
            text_align: How to align the text in the textbox, can be: "left" or "right".
            rtl: If True, sets the direction of the text to right-to-left.
            show_copy_button: If True, a copy button will be shown.
            max_length: The maximum number of characters allowed in the textbox.
        """
        # Store all the custom/frontend-specific props on the instance.
        # Gradio will automatically pass these to the Svelte component.
        
                 
        self.model = model
        self.display_mode = display_mode       
        self.hide_input = hide_input
        self.lines = lines
        self.max_lines = max_lines
        self.placeholder = placeholder
        self.autofocus = autofocus
        self.autoscroll = autoscroll
        self.text_align = text_align
        self.rtl = rtl
        self.show_copy_button = show_copy_button
        self.max_length = max_length
        
        # Call the parent constructor with ONLY the arguments it expects,
        # plus the catch-all kwargs.
        super().__init__(
            label=label,
            info=info,
            every=every,
            show_label=show_label,
            container=container,
            scale=scale,
            min_width=min_width,
            interactive=interactive,
            visible=visible,
            elem_id=elem_id,
            elem_classes=elem_classes,
            value=value,            
            **kwargs, # Pass any extra arguments up the chain.
        )

    def preprocess(self, payload: dict | None) -> dict | None:
        """
        Processes the main value of the component, which is a dictionary.
        Enriches it with character and token counts before passing it to the Python function.
        
        Parameters:
            payload: The dictionary value from the frontend, e.g., {"text": "...", "tokens": [...], ...}.
        Returns:
            A dictionary enriched with 'char_count' and 'token_count'.
        """
        if payload is None:
            return {"text": "", "tokens": [], "token_ids": [], "char_count": 0, "token_count": 0}

        text = payload.get("text", "")
        tokens = payload.get("tokens", [])
        
        payload["char_count"] = len(text)
        payload["token_count"] = len(tokens)
        
        return payload

    def postprocess(self, value: str | dict | None) -> dict | None:
        """
        Handles setting the component's value from Python.
        If a simple string is provided, it wraps it in the dictionary
        structure that the frontend expects.

        Parameters:
            value: The value to set for the component, can be a string or a dictionary.
        Returns:
            A dictionary formatted for the frontend.
        """
        if value is None:
            return None
        
        if isinstance(value, dict):
            return value
        
        if isinstance(value, str):
            return {"text": value, "tokens": [], "token_ids": []}
        
        return None
  
    def api_info(self) -> dict[str, Any]:
        """
        Defines the API info for the component. The output is a JSON object.
        """
        return {
            "type": "object",
            "properties": {
                "text": {"type": "string"},
                "tokens": {"type": "array", "items": {"type": "string"}},
                "token_ids": {"type": "array", "items": {"type": "integer"}},
                "char_count": {"type": "integer"},
                "token_count": {"type": "integer"},
            },
            "description": "An object containing the tokenization results and counts.",
        }

    def example_payload(self) -> Any:
        """
        An example of the JSON object that this component's `preprocess` method returns.
        """
        return {
            "text": "Gradio is great!",
            "tokens": ["Gradio", " is", " great", "!"],
            "token_ids": [35345, 318, 1049, 0],
            "char_count": 16,
            "token_count": 4,
        }

    def example_value(self) -> Any:
        """
        An example of the value that can be passed to the component.
        """
        return {"text": "This is the initial text.", "tokens": [], "token_ids": []}