# GstMini
# Copyright (C) 2025 Rafael Caricio <rafael@caricio.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.

"""LiveSource - Simulates live camera generating frames."""

import threading
import time
from ..core.element import GstElement, GstState
from ..core.buffer import GstBuffer, BufferFlags
from ..core.pad import GstFlowReturn


class LiveSource(GstElement):
    """
    Source element that generates frames at a fixed rate (like a camera).

    Demonstrates live sources that cannot be paused - they keep generating data.
    """

    def __init__(self, name: str, fps: int = 30):
        """
        Create a live source.

        Args:
            name: Element name
            fps: Frames per second to generate
        """
        super().__init__(name)
        self.fps = fps
        self.frame_interval = 1.0 / fps  # Time between frames
        self.frame_count = 0
        self.dropped_frames = 0

        self._thread = None
        self._running = False
        self._start_time = None

        # Create source pad
        self.src_pad = self.create_src_pad("src")

    def on_playing(self):
        """Start generating frames when entering PLAYING state."""
        self.log(f"Starting frame generation at {self.fps} fps")

        self._running = True
        self._start_time = time.monotonic()
        self._thread = threading.Thread(target=self._generate_frames, daemon=True)
        self._thread.start()

    def on_null(self):
        """Stop generating frames when entering NULL state."""
        self.log("Stopping frame generation")
        self.log(f"Generated {self.frame_count} frames, dropped {self.dropped_frames} frames")

        self._running = False
        if self._thread is not None:
            self._thread.join(timeout=1.0)

    def _generate_frames(self):
        """
        Frame generation loop (runs in separate thread).

        This simulates a live camera that captures frames at regular intervals.
        """
        next_frame_time = time.monotonic()

        while self._running:
            current_time = time.monotonic()

            # Check if it's time for the next frame
            if current_time >= next_frame_time:
                # Calculate PTS (presentation timestamp)
                # This is the time since we started, in seconds
                pts = current_time - self._start_time
                duration = self.frame_interval

                # Create buffer with frame data
                buffer = GstBuffer(
                    pts=pts,
                    duration=duration,
                    data={
                        'frame': self.frame_count,
                        'timestamp': pts,
                        'content': f'frame_{self.frame_count:06d}'
                    }
                )

                # Try to push buffer downstream
                # This call may block if downstream is slow or queue is full
                result = self.src_pad.push(buffer)

                if result == GstFlowReturn.OK:
                    if self.frame_count % self.fps == 0:  # Log every second
                        self.log(f"Generated frame {self.frame_count}, PTS={pts:.3f}s")
                    self.frame_count += 1
                else:
                    # Push failed (queue full, flushing, etc.)
                    self.dropped_frames += 1
                    if self.dropped_frames % 10 == 1:
                        self.log(f"Dropped frame {self.frame_count} (total dropped: {self.dropped_frames})")

                # Schedule next frame
                next_frame_time += self.frame_interval

            else:
                # Sleep until next frame time
                sleep_time = next_frame_time - current_time
                if sleep_time > 0:
                    time.sleep(min(sleep_time, 0.001))  # Sleep in small increments

        # Send EOS when stopping
        eos_buffer = GstBuffer(pts=0.0, duration=0.0, data={}, flags=BufferFlags.EOS)
        self.src_pad.push(eos_buffer)

    def get_stats(self) -> dict:
        """Get statistics about frame generation."""
        return {
            'frames_generated': self.frame_count,
            'frames_dropped': self.dropped_frames,
            'fps': self.fps,
        }
