"""
Custom cone light for flashlight effect.
Based on arcade.future.light.Light but with directional cone shape.
"""
from array import array
import math
from arcade import gl
from arcade.color import WHITE
from arcade.types import RGBOrA255


class ConeLight:
    """
    Create a directional cone-shaped light (like a flashlight).
    
    Args:
        center_x: X position of the light
        center_y: Y position of the light
        radius: Radius/distance of the light cone
        color: Color of the light
        mode: 'hard' or 'soft' light
        angle: Direction angle in degrees (0 = right, 90 = up)
        cone_angle: Total cone spread angle in degrees (default 60)
    """
    
    HARD = 1.0
    SOFT = 0.0
    
    def __init__(
        self,
        center_x: float,
        center_y: float,
        radius: float = 150.0,
        color: RGBOrA255 = WHITE,
        mode: str = "soft",
        angle: float = 0.0,
        cone_angle: float = 60.0,
    ):
        if not (isinstance(color, tuple) or isinstance(color, list)):
            raise ValueError(
                "Color must be a 3-4 element Tuple or List with red-green-blue "
                " and optionally an alpha."
            )
        
        if not isinstance(mode, str) or not (mode == "soft" or mode == "hard"):
            raise ValueError("Mode must be set to either 'soft' or 'hard'.")
        
        self._center_x = center_x
        self._center_y = center_y
        self._radius = radius
        self._attenuation = ConeLight.HARD if mode == "hard" else ConeLight.SOFT
        self._color = color[:3]
        self._angle = angle
        self._cone_angle = cone_angle
        self._cone_light_layer = None
        
        if len(self._color) != 3:
            raise ValueError(
                "Color must be a 3-4 element Tuple or List with red-green-blue "
                "and optionally an alpha."
            )
    
    @property
    def position(self) -> tuple[float, float]:
        """Get or set the light position"""
        return self._center_x, self._center_y
    
    @position.setter
    def position(self, value):
        if self._cone_light_layer:
            self._cone_light_layer._rebuild = True
        self._center_x, self._center_y = value
    
    @property
    def radius(self) -> float:
        """Get or set the light size"""
        return self._radius
    
    @radius.setter
    def radius(self, value):
        if self._cone_light_layer:
            self._cone_light_layer._rebuild = True
        self._radius = value
    
    @property
    def angle(self) -> float:
        """Get or set the light direction angle in degrees"""
        return self._angle
    
    @angle.setter
    def angle(self, value):
        if self._cone_light_layer:
            self._cone_light_layer._rebuild = True
        self._angle = value
    
    @property
    def cone_angle(self) -> float:
        """Get or set the cone spread angle in degrees"""
        return self._cone_angle
    
    @cone_angle.setter
    def cone_angle(self, value):
        if self._cone_light_layer:
            self._cone_light_layer._rebuild = True
        self._cone_angle = value


class ConeLightLayer:
    """
    Standalone cone light layer - no dependency on arcade.future.light!
    Renders cone lights directly as overlay with multiplicative blending.
    """
    
    def __init__(self, ctx, width: int, height: int):
        self._ctx = ctx
        self._width = width
        self._height = height
        self._lights: list[ConeLight] = []
        self._rebuild = False
        # Instance layout: 2f(position) + 1f(radius) + 1f(attenuation) + 3f(color) = 7 floats = 28 bytes
        self._instance_bytes = 7 * 4
        self._buffer = ctx.buffer(reserve=self._instance_bytes * 10)
        
        # Vertex data for quad
        vertex_data = array('f', [
            -1.0, +1.0, 0.0, 1.0,
            -1.0, -1.0, 0.0, 0.0,
            +1.0, +1.0, 1.0, 1.0,
            +1.0, -1.0, 1.0, 0.0,
        ])
        
        self._vao = ctx.geometry(
            [
                gl.BufferDescription(
                    ctx.buffer(data=vertex_data),
                    "2f 2f",
                    ["in_vert", "in_uv"],
                ),
                gl.BufferDescription(
                    self._buffer,
                    "2f 1f 1f 3f",
                    [
                        "in_instance_position",
                        "in_instance_radius",
                        "in_instance_attenuation",
                        "in_instance_color",
                    ],
                    instanced=True,
                ),
            ]
        )
        
        # Load custom cone shaders from files
        import os
        shader_dir = os.path.join(os.path.dirname(__file__), "shaders", "lights")
        
        with open(os.path.join(shader_dir, "cone_light_vs.glsl"), 'r') as f:
            vertex_shader_source = f.read()
        
        with open(os.path.join(shader_dir, "cone_light_fs.glsl"), 'r') as f:
            fragment_shader_source = f.read()
        
        self._cone_program = ctx.program(
            vertex_shader=vertex_shader_source,
            fragment_shader=fragment_shader_source,
        )
    
    def add(self, light: ConeLight):
        """Add a cone light"""
        self._lights.append(light)
        light._cone_light_layer = self
        self._rebuild = True
    
    def remove(self, light: ConeLight):
        """Remove a cone light"""
        self._lights.remove(light)
        light._cone_light_layer = None
        self._rebuild = True
    
    def set_ambient_brightness(self, brightness: float):
        """Set ambient brightness for shader (0-1)"""
        self._ambient_brightness = brightness
    
    def render(self, position: tuple[float, float] = (0, 0)):
        """Render cone lights to current framebuffer"""
        if len(self._lights) == 0:
            return

        # Ensure buffer can hold one instance
        while self._buffer.size < self._instance_bytes:
            self._buffer.orphan(double=True)
        self._rebuild = False
        
        # Render each light with its own rotation
        self._ctx.enable(self._ctx.BLEND)
        self._ctx.blend_func = self._ctx.BLEND_ADDITIVE
        
        for light in self._lights:
            # Write per-light instance data (one instance) so each cone uses its own position.
            instance = array(
                "f",
                [
                    light.position[0],
                    light.position[1],
                    light.radius,
                    light._attenuation,
                    light._color[0],
                    light._color[1],
                    light._color[2],
                ],
            )
            self._buffer.write(data=instance)
            self._cone_program["offset"] = position
            self._cone_program["rotation"] = math.radians(light.angle)
            self._cone_program["cone_angle"] = light.cone_angle  # Pass cone angle to shader
            self._cone_program["ambient_brightness"] = getattr(self, '_ambient_brightness', 0.5)
            self._vao.render(
                self._cone_program,
                mode=self._ctx.TRIANGLE_STRIP,
                instances=1
            )
        
        self._ctx.blend_func = self._ctx.BLEND_DEFAULT
    
    def resize(self, width: int, height: int):
        """Update dimensions when window resizes"""
        self._width = width
        self._height = height
    
    def draw_as_overlay(self, ambient_color: tuple):
        """
        Draw cone lights as overlay with darken effect.
        Areas not lit by cone will be darker (ambient color).
        """
        if len(self._lights) == 0:
            return
        
        # Render cone light with additive blending
        self.render(position=(0, 0))
