#!/usr/bin/env python3
"""
Live HLS Broadcast Server v5
Based on v4 with:
- #EXT-X-PROGRAM-DATE-TIME timestamps
- Cache-buster query strings on segments
Port 9005
"""

import http.server
import socketserver
import ssl
import time
import threading
from datetime import datetime, timezone

PORT = 9005
SSL_CERT = '/etc/letsencrypt/live/cdn-srt.smartbit.co/fullchain.pem'
SSL_KEY = '/etc/letsencrypt/live/cdn-srt.smartbit.co/privkey.pem'
SEGMENT_DURATION = 6  # seconds
TOTAL_SEGMENTS = 60
TOTAL_DURATION = 360  # seconds (60 * 6)
WINDOW_SIZE = 12  # segments in live window

# SCTE-35 marker positions (with buffer fix from v4)
SCTE35_CUE_OUT_SEGMENT = 18
SCTE35_CUE_IN_SEGMENT = 58
SCTE35_DURATION = 240.0

class LivePlaylistGenerator:
    def __init__(self):
        self.start_time = time.time()
        self.start_datetime = datetime.now(timezone.utc)
        self.lock = threading.Lock()

    def get_current_segment(self):
        """Calculate which segment should be current based on elapsed time"""
        elapsed = time.time() - self.start_time
        position_in_loop = elapsed % TOTAL_DURATION
        segment_num = int(position_in_loop / SEGMENT_DURATION)
        if segment_num >= TOTAL_SEGMENTS:
            segment_num = TOTAL_SEGMENTS - 1
        return segment_num

    def get_media_sequence(self):
        """Get current media sequence number (monotonically increasing)"""
        elapsed = time.time() - self.start_time
        return int(elapsed / SEGMENT_DURATION)

    def get_segment_datetime(self, media_seq):
        """Get the datetime for a specific media sequence number"""
        # Each segment is SEGMENT_DURATION seconds from start
        segment_offset = media_seq * SEGMENT_DURATION
        from datetime import timedelta
        return self.start_datetime + timedelta(seconds=segment_offset)

    def generate_playlist(self, variant):
        """Generate live HLS playlist for a variant with timestamps"""
        current_seg = self.get_current_segment()
        media_seq = self.get_media_sequence()

        # Calculate window
        window_start = max(0, current_seg - WINDOW_SIZE + 1)
        window_start_seq = media_seq - (current_seg - window_start)

        lines = [
            '#EXTM3U',
            '#EXT-X-VERSION:6',
            '#EXT-X-TARGETDURATION:6',
            f'#EXT-X-MEDIA-SEQUENCE:{window_start_seq}',
            '#EXT-X-INDEPENDENT-SEGMENTS',
        ]

        # Add segments in window
        for i, seg_num in enumerate(range(window_start, current_seg + 1)):
            actual_seg = seg_num % TOTAL_SEGMENTS
            segment_seq = window_start_seq + i
            duration = SEGMENT_DURATION

            # Add SCTE-35 CUE-OUT marker
            if actual_seg == SCTE35_CUE_OUT_SEGMENT:
                lines.append(f'#EXT-X-CUE-OUT:DURATION={SCTE35_DURATION}')

            # Add SCTE-35 CUE-IN marker
            if actual_seg == SCTE35_CUE_IN_SEGMENT:
                lines.append('#EXT-X-CUE-IN')

            # Add PROGRAM-DATE-TIME timestamp
            seg_datetime = self.get_segment_datetime(segment_seq)
            lines.append(f'#EXT-X-PROGRAM-DATE-TIME:{seg_datetime.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]}Z')

            lines.append(f'#EXTINF:{duration:.6f},')
            # Cache-buster: add media sequence as query param
            lines.append(f'segment_{variant}_{actual_seg:03d}.ts?seq={segment_seq}')

        return '\n'.join(lines) + '\n'

    def generate_master_playlist(self):
        """Generate master playlist"""
        return '''#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=3700000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
playlist_1080p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2700000,RESOLUTION=1280x720,CODECS="avc1.640028,mp4a.40.2"
playlist_720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=854x480,CODECS="avc1.640028,mp4a.40.2"
playlist_480p.m3u8
'''

playlist_gen = LivePlaylistGenerator()

class LiveBroadcastHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        # Strip query params for file serving
        path_without_query = self.path.split('?')[0]
        
        if path_without_query == '/master.m3u8':
            content = playlist_gen.generate_master_playlist()
            self.send_response(200)
            self.send_header('Content-Type', 'application/vnd.apple.mpegurl')
            self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            self.wfile.write(content.encode())

        elif path_without_query == '/playlist_1080p.m3u8':
            content = playlist_gen.generate_playlist('1080p')
            self.send_response(200)
            self.send_header('Content-Type', 'application/vnd.apple.mpegurl')
            self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            self.wfile.write(content.encode())

        elif path_without_query == '/playlist_720p.m3u8':
            content = playlist_gen.generate_playlist('720p')
            self.send_response(200)
            self.send_header('Content-Type', 'application/vnd.apple.mpegurl')
            self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            self.wfile.write(content.encode())

        elif path_without_query == '/playlist_480p.m3u8':
            content = playlist_gen.generate_playlist('480p')
            self.send_response(200)
            self.send_header('Content-Type', 'application/vnd.apple.mpegurl')
            self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            self.wfile.write(content.encode())

        elif path_without_query.endswith('.ts'):
            # Serve segment files (ignore query string)
            self.path = path_without_query
            super().do_GET()

        else:
            super().do_GET()

    def log_message(self, format, *args):
        pass

if __name__ == '__main__':
    with socketserver.TCPServer(("", PORT), LiveBroadcastHandler) as httpd:
        context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        context.load_cert_chain(SSL_CERT, SSL_KEY)
        httpd.socket = context.wrap_socket(httpd.socket, server_side=True)

        print(f"=" * 60)
        print(f"Live HLS Broadcast Server V5 Started (HTTPS)")
        print(f"Port: {PORT}")
        print(f"Features: PROGRAM-DATE-TIME + Cache-Buster")
        print(f"=" * 60)
        httpd.serve_forever()
