from __future__ import annotations

from math import cos, pi, sin
from typing import Sequence

from rich.console import Console, ConsoleOptions, RenderResult
from rich.segment import Segment
from rich.style import Style

from textual.color import Color, Gradient


class VerticalGradient:
    """Draw a vertical gradient."""

    def __init__(self, color1: str, color2: str) -> None:
        self._color1 = Color.parse(color1)
        self._color2 = Color.parse(color2)

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        width = options.max_width
        height = options.height or options.max_height
        color1 = self._color1
        color2 = self._color2
        default_color = Color(0, 0, 0).rich_color
        from_color = Style.from_color
        blend = color1.blend
        rich_color1 = color1.rich_color
        for y in range(height):
            line_color = from_color(
                default_color,
                (
                    blend(color2, y / (height - 1)).rich_color
                    if height > 1
                    else rich_color1
                ),
            )
            yield Segment(f"{width * ' '}\n", line_color)


class LinearGradient:
    """Render a linear gradient with a rotation.

    Args:
        angle: Angle of rotation in degrees.
        stops: List of stop consisting of pairs of offset (between 0 and 1) and color.

    """

    def __init__(
        self, angle: float, stops: Sequence[tuple[float, Color | str]]
    ) -> None:
        self.angle = angle
        self._stops = [
            (stop, Color.parse(color) if isinstance(color, str) else color)
            for stop, color in stops
        ]
        self._color_gradient = Gradient(*self._stops)

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        width = options.max_width
        height = options.height or options.max_height

        angle_radians = -self.angle * pi / 180.0
        sin_angle = sin(angle_radians)
        cos_angle = cos(angle_radians)

        center_x = width / 2
        center_y = height

        new_line = Segment.line()

        _Segment = Segment
        get_color = self._color_gradient.get_rich_color
        from_color = Style.from_color

        for line_y in range(height):
            point_y = float(line_y) * 2 - center_y
            point_x = 0 - center_x

            x1 = (center_x + (point_x * cos_angle - point_y * sin_angle)) / width
            x2 = (
                center_x + (point_x * cos_angle - (point_y + 1.0) * sin_angle)
            ) / width
            point_x = width - center_x
            end_x1 = (center_x + (point_x * cos_angle - point_y * sin_angle)) / width
            delta_x = (end_x1 - x1) / width

            if abs(delta_x) < 0.0001:
                # Special case for verticals
                yield _Segment(
                    "▀" * width,
                    from_color(
                        get_color(x1),
                        get_color(x2),
                    ),
                )

            else:
                yield from [
                    _Segment(
                        "▀",
                        from_color(
                            get_color(x1 + x * delta_x),
                            get_color(x2 + x * delta_x),
                        ),
                    )
                    for x in range(width)
                ]

            yield new_line


if __name__ == "__main__":
    from rich import print

    COLORS = [
        "#881177",
        "#aa3355",
        "#cc6666",
        "#ee9944",
        "#eedd00",
        "#99dd55",
        "#44dd88",
        "#22ccbb",
        "#00bbcc",
        "#0099cc",
        "#3366bb",
        "#663399",
    ]

    stops = [(i / (len(COLORS) - 1), Color.parse(c)) for i, c in enumerate(COLORS)]

    print(LinearGradient(25, stops))

    from time import time

    from textual.app import App, ComposeResult
    from textual.widgets import Static

    class GradientApp(App):
        CSS = """
        Screen {
            background: transparent;
            align: center middle;
        }

        Static {
            padding: 2 4;
            background: $panel;
            width: 50;
        }

        """

        def compose(self) -> ComposeResult:
            yield Static("Gradients are fast now :-) ")

        def render(self):
            return LinearGradient(time() * 90, stops)

        def on_mount(self) -> None:
            self.set_interval(1 / 30, self.refresh)

    app = GradientApp()
    app.run()
