#!/usr/bin/env python3
"""
Simple HTTP server for image gallery
Usage: python server.py [port] [directory]
Default: python server.py 8000 ./images
"""

import http.server
import socketserver
import json
import os
import sys
import subprocess
import shutil
from pathlib import Path
from urllib.parse import unquote, urlparse
import mimetypes

PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 1338
IMAGES_DIR = sys.argv[2] if len(sys.argv) > 2 else './'

class GalleryHandler(http.server.SimpleHTTPRequestHandler):
    
    def do_GET(self):
        parsed_path = urlparse(self.path)
        path = unquote(parsed_path.path)
        
        # Serve the main HTML page
        if path == '/' or path == '/index.html':
            self.serve_html()
        
        # API: Get list of directories
        elif path == '/api/directories':
            self.serve_directories()
        
        # API: Get list of images in a directory
        elif path.startswith('/api/images/'):
            directory = path.replace('/api/images/', '')
            self.serve_images_list(directory)
        
        # API: Get captions for a directory
        elif path.startswith('/api/captions/'):
            directory = path.replace('/api/captions/', '')
            self.serve_captions(directory)
        
        # API: Serve thumbnail (images or generated video thumbnail)
        elif path.startswith('/api/thumbnail/'):
            parts = path.replace('/api/thumbnail/', '').split('/', 1)
            if len(parts) == 2:
                directory, filename = parts
                self.serve_thumbnail(directory, filename)
        
        # API: Serve full image or video
        elif path.startswith('/api/image/'):
            parts = path.replace('/api/image/', '').split('/', 1)
            if len(parts) == 2:
                directory, filename = parts
                self.serve_image(directory, filename)

        # API: Download file (for videos or images)
        elif path.startswith('/api/download/'):
            parts = path.replace('/api/download/', '').split('/', 1)
            if len(parts) == 2:
                directory, filename = parts
                self.serve_download(directory, filename)
        
        else:
            self.send_error(404, "File not found")
    
    def serve_html(self):
        """Serve the embedded HTML page"""
        html_content = """<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Gallery Viewer</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: Arial, sans-serif;
            background: black;
            color: white;
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        .top-section {
            background: black;
            border-bottom: 2px solid #222;
            padding: 15px;
            display: flex;
            align-items: center;
            gap: 15px;
            max-height: 35vh;
            overflow: hidden;
        }
        
        .controls {
            display: flex;
            align-items: center;
            flex-shrink: 0;
        }
        
        select {
            padding: 8px 12px;
            font-size: 14px;
            border: 1px solid #ccc;
            border-radius: 4px;
            background: black;
            color: white;
            cursor: pointer;
            min-width: 200px;
        }
        
        .thumbnails {
            display: flex;
            gap: 10px;
            overflow-x: auto;
            overflow-y: hidden;
            padding: 5px 0;
            flex: 1;
        }
        
        .thumbnail {
            flex-shrink: 0;
            width: 120px;
            height: 120px;
            object-fit: cover;
            cursor: pointer;
            border: 3px solid transparent;
            border-radius: 4px;
            transition: border-color 0.2s;
        }
        
        .thumbnail:hover {
            border-color: #4CAF50;
        }
        
        .thumbnail.selected {
            border-color: #2196F3;
        }
        
        .main-section {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            background: black;
            padding: 0;
            position: relative;
            overflow: hidden;
        }
        
        .image-container {
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background: black;
            position: relative;
        }
        
        .main-image {
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            background: black;
        }
        
        .description {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            padding: 15px 20px;
            background: rgba(0, 0, 0, 0.8);
            color: white;
            text-align: center;
            font-size: 16px;
        }
        
        .nav-button {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            background: rgba(255,255,255,0.9);
            border: none;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            font-size: 24px;
            cursor: pointer;
            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
            transition: background 0.2s;
            z-index: 10;
        }
        
        .nav-button:hover {
            background: white;
        }
        
        .nav-button:disabled {
            opacity: 0.3;
            cursor: not-allowed;
        }
        
        .nav-button.prev {
            left: 20px;
        }
        
        .nav-button.next {
            right: 20px;
        }
        
        .placeholder {
            text-align: center;
            color: #999;
            font-size: 18px;
        }
        
        .loading {
            text-align: center;
            color: #666;
            font-size: 16px;
        }
    </style>
</head>
<body>
    <div class="top-section">
        <div class="controls">
            <select id="directorySelect">
                <option value="">Loading directories...</option>
            </select>
        </div>
        <div class="thumbnails" id="thumbnails"></div>
    </div>
    
    <div class="main-section">
        <button class="nav-button prev" id="prevBtn" onclick="navigateImage(-1)">‹</button>
        <div class="image-container" id="imageContainer">
            <div class="placeholder">Select a directory and image to begin</div>
        </div>
        <button class="nav-button next" id="nextBtn" onclick="navigateImage(1)">›</button>
    </div>

    <script>
        let currentDirectory = '';
        let currentImages = [];
        let currentIndex = -1;
        let currentCaptions = {};

        // Initialize
        async function init() {
            const select = document.getElementById('directorySelect');
            
            try {
                const response = await fetch('/api/directories');
                const directories = await response.json();
                
                select.innerHTML = '<option value="">Select a directory...</option>';
                
                directories.forEach(dir => {
                    const option = document.createElement('option');
                    option.value = dir;
                    option.textContent = dir;
                    select.appendChild(option);
                });
                
                select.addEventListener('change', loadDirectory);
                
                // Auto-select first directory
                if (directories.length > 0) {
                    select.value = directories[0];
                    await loadDirectory();
                }
            } catch (error) {
                console.error('Error loading directories:', error);
                select.innerHTML = '<option value="">Error loading directories</option>';
            }
        }

        async function loadDirectory() {
            const select = document.getElementById('directorySelect');
            currentDirectory = select.value;
            
            if (!currentDirectory) {
                document.getElementById('thumbnails').innerHTML = '';
                showPlaceholder();
                return;
            }
            
            try {
                // Load images
                const imagesResponse = await fetch(`/api/images/${encodeURIComponent(currentDirectory)}`);
                currentImages = await imagesResponse.json();
                
                // Load captions
                const captionsResponse = await fetch(`/api/captions/${encodeURIComponent(currentDirectory)}`);
                currentCaptions = await captionsResponse.json();
                
                currentIndex = -1;
                
                displayThumbnails();
                showPlaceholder();
            } catch (error) {
                console.error('Error loading directory:', error);
                document.getElementById('thumbnails').innerHTML = '<div class="loading">Error loading images</div>';
            }
        }

        function displayThumbnails() {
            const container = document.getElementById('thumbnails');
            container.innerHTML = '';
            
            if (currentImages.length === 0) {
                container.innerHTML = '<div class="loading">No images found in this directory</div>';
                return;
            }
            
                currentImages.forEach((img, index) => {
                    const thumb = document.createElement('img');
                    // request thumbnail for images and videos (video thumbnails are generated server-side)
                    thumb.src = `/api/thumbnail/${encodeURIComponent(currentDirectory)}/${encodeURIComponent(img)}`;
                    thumb.className = 'thumbnail';
                    thumb.onclick = () => selectImage(index);

                    const caption = currentCaptions[img]?.caption || img;
                    thumb.title = caption;

                    container.appendChild(thumb);
                });
        }

        function selectImage(index) {
            if (index < 0 || index >= currentImages.length) return;
            
            currentIndex = index;
            
            // Update thumbnail selection
            const thumbs = document.querySelectorAll('.thumbnail');
            thumbs.forEach((t, i) => {
                t.classList.toggle('selected', i === index);
            });
            
            displayMainImage();
        }

        function displayMainImage() {
            const container = document.getElementById('imageContainer');
            const img = currentImages[currentIndex];
            const caption = currentCaptions[img]?.caption || img;
            const description = currentCaptions[img]?.description || '';

            const imgSrc = `/api/image/${encodeURIComponent(currentDirectory)}/${encodeURIComponent(img)}`;
            const thumbSrc = `/api/thumbnail/${encodeURIComponent(currentDirectory)}/${encodeURIComponent(img)}`;

            // If it's a video, render a video player with poster and download link
            if (img.toLowerCase().endsWith('.mp4')) {
                container.innerHTML = `
                    <video controls autoplay class="main-image" poster="${thumbSrc}">
                        <source src="${imgSrc}" type="video/mp4">
                        Your browser does not support the video tag.
                    </video>
                    ${description ? `<div class="description">${escapeHtml(description)}</div>` : ''}
                    <div style="position:absolute; top:10px; right:10px; z-index:20;">
                        <a href="/api/download/${encodeURIComponent(currentDirectory)}/${encodeURIComponent(img)}" style="color:white; background:rgba(0,0,0,0.6); padding:6px 10px; border-radius:4px; text-decoration:none;">Download</a>
                    </div>
                `;
            } else {
                container.innerHTML = `
                    <img src="${imgSrc}" alt="${escapeHtml(caption)}" title="${escapeHtml(caption)}" class="main-image">
                    ${description ? `<div class="description">${escapeHtml(description)}</div>` : ''}
                `;
            }
            
            updateNavButtons();
        }

        function navigateImage(direction) {
            const newIndex = currentIndex + direction;
            if (newIndex >= 0 && newIndex < currentImages.length) {
                selectImage(newIndex);
            }
        }

        function updateNavButtons() {
            document.getElementById('prevBtn').disabled = currentIndex <= 0;
            document.getElementById('nextBtn').disabled = currentIndex >= currentImages.length - 1;
        }

        function showPlaceholder() {
            const container = document.getElementById('imageContainer');
            container.innerHTML = '<div class="placeholder">Velg ett av småbildene over</div>';
            document.getElementById('prevBtn').disabled = true;
            document.getElementById('nextBtn').disabled = true;
        }

        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        // Initialize on page load
        init();
    </script>
</body>
</html>"""
        
        self.send_response(200)
        self.send_header('Content-type', 'text/html; charset=utf-8')
        self.end_headers()
        self.wfile.write(html_content.encode('utf-8'))
    
    def serve_directories(self):
        """Return list of subdirectories in IMAGES_DIR"""
        try:
            images_path = Path(IMAGES_DIR)
            if not images_path.exists():
                directories = []
            else:
                directories = [d.name for d in images_path.iterdir() if d.is_dir()]
            
            self.send_json_response(directories)
        except Exception as e:
            self.send_error(500, "Error reading directories: {}".format(str(e)))
    
    def serve_images_list(self, directory):
        """Return list of JPG files in the specified directory"""
        try:
            images_base = Path(IMAGES_DIR).resolve()
            dir_path = (images_base / directory).resolve()
            
            # Security check - ensure path is within IMAGES_DIR
            if not str(dir_path).startswith(str(images_base)):
                self.send_error(403, "Access denied")
                return
            
            if not dir_path.exists():
                self.send_json_response([])
                return
            
            # Include JPEG images and MP4 movies
            images = [f.name for f in dir_path.iterdir()
                     if f.is_file() and f.suffix.lower() in ['.jpg', '.jpeg', '.mp4']]
            images.sort()
            
            self.send_json_response(images)
        except Exception as e:
            self.send_error(500, "Error reading images: {}".format(str(e)))
    
    def serve_captions(self, directory):
        """Return captions from image_captions.json if it exists"""
        try:
            images_base = Path(IMAGES_DIR).resolve()
            dir_path = (images_base / directory).resolve()
            
            # Security check - ensure path is within IMAGES_DIR
            if not str(dir_path).startswith(str(images_base)):
                self.send_error(403, "Access denied")
                return
            
            caption_file = dir_path / 'image_captions.json'
            
            if caption_file.exists():
                with open(str(caption_file), 'r', encoding='utf-8') as f:
                    captions = json.load(f)
                self.send_json_response(captions)
            else:
                self.send_json_response({})
        except json.JSONDecodeError:
            self.send_json_response({})
        except Exception as e:
            self.send_error(500, "Error reading captions: {}".format(str(e)))
    
    def serve_image(self, directory, filename):
        """Serve an image file"""
        try:
            # Construct the full path
            images_base = Path(IMAGES_DIR).resolve()
            image_path = (images_base / directory / filename).resolve()
            
            # Security check - ensure path is within IMAGES_DIR
            if not str(image_path).startswith(str(images_base)):
                self.send_error(403, "Access denied")
                return
            
            if not image_path.exists() or not image_path.is_file():
                self.send_error(404, "Image not found: {}".format(image_path))
                return
            
            # Determine content type
            content_type, _ = mimetypes.guess_type(str(image_path))
            if not content_type:
                content_type = 'application/octet-stream'

            # Send the file (streamed in chunks)
            self.send_response(200)
            self.send_header('Content-type', content_type)
            self.send_header('Content-Length', image_path.stat().st_size)
            self.end_headers()

            with open(str(image_path), 'rb') as f:
                shutil.copyfileobj(f, self.wfile)
        
        except Exception as e:
            import traceback
            print("Error serving image: {}".format(traceback.format_exc()))
            self.send_error(500, "Error serving image: {}".format(str(e)))
    
    def send_json_response(self, data):
        """Send JSON response"""
        json_data = json.dumps(data).encode()
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.send_header('Content-Length', len(json_data))
        self.end_headers()
        self.wfile.write(json_data)

    def serve_thumbnail(self, directory, filename):
        """Serve a thumbnail for an image or generate one for a video (mp4)"""
        try:
            images_base = Path(IMAGES_DIR).resolve()
            dir_path = (images_base / directory).resolve()

            # Security check
            if not str(dir_path).startswith(str(images_base)):
                self.send_error(403, "Access denied")
                return

            file_path = (dir_path / filename).resolve()
            if not file_path.exists() or not file_path.is_file():
                self.send_error(404, "File not found")
                return

            # If it's an image, just serve it (browser will scale)
            if file_path.suffix.lower() in ['.jpg', '.jpeg', '.png']:
                # reuse serve_image
                return self.serve_image(directory, filename)

            # If it's a video, try to generate a thumbnail using ffmpeg
            if file_path.suffix.lower() == '.mp4':
                thumbs_base = images_base / '.thumbnails' / directory
                thumbs_base.mkdir(parents=True, exist_ok=True)
                thumb_name = filename + '.jpg'
                thumb_path = (thumbs_base / thumb_name).resolve()

                # regenerate if missing or older than source
                try:
                    if not thumb_path.exists() or thumb_path.stat().st_mtime < file_path.stat().st_mtime:
                        # run ffmpeg to grab frame at 1 second
                        cmd = [
                            'ffmpeg', '-y', '-ss', '00:00:01', '-i', str(file_path),
                            '-vframes', '1', '-q:v', '2', str(thumb_path)
                        ]
                        subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
                except Exception:
                    # if ffmpeg fails, continue and attempt to serve if thumb exists
                    pass

                if thumb_path.exists():
                    # serve thumbnail image
                    content_type = 'image/jpeg'
                    self.send_response(200)
                    self.send_header('Content-type', content_type)
                    self.send_header('Content-Length', thumb_path.stat().st_size)
                    self.end_headers()
                    with open(str(thumb_path), 'rb') as f:
                        shutil.copyfileobj(f, self.wfile)
                    return

                # fallback: serve a simple SVG placeholder so UI shows something
                placeholder_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="320" height="180" viewBox="0 0 320 180">
  <rect width="100%" height="100%" fill="#111" />
  <g fill="#888" font-family="Arial, Helvetica, sans-serif" font-size="16">
    <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle">No thumbnail</text>
  </g>
</svg>'''

                svg_bytes = placeholder_svg.encode('utf-8')
                self.send_response(200)
                self.send_header('Content-type', 'image/svg+xml')
                self.send_header('Content-Length', str(len(svg_bytes)))
                self.end_headers()
                self.wfile.write(svg_bytes)
                return

            # Other types: not supported
            self.send_error(415, 'Unsupported media type')

        except Exception as e:
            import traceback
            print("Error serving thumbnail: {}".format(traceback.format_exc()))
            self.send_error(500, "Error serving thumbnail: {}".format(str(e)))

    def serve_download(self, directory, filename):
        """Serve a file for download with Content-Disposition"""
        try:
            images_base = Path(IMAGES_DIR).resolve()
            file_path = (images_base / directory / filename).resolve()

            if not str(file_path).startswith(str(images_base)):
                self.send_error(403, "Access denied")
                return

            if not file_path.exists() or not file_path.is_file():
                self.send_error(404, "File not found")
                return

            content_type, _ = mimetypes.guess_type(str(file_path))
            if not content_type:
                content_type = 'application/octet-stream'

            self.send_response(200)
            self.send_header('Content-type', content_type)
            self.send_header('Content-Length', file_path.stat().st_size)
            self.send_header('Content-Disposition', 'attachment; filename="{}"'.format(file_path.name))
            self.end_headers()

            with open(str(file_path), 'rb') as f:
                shutil.copyfileobj(f, self.wfile)

        except Exception as e:
            import traceback
            print("Error serving download: {}".format(traceback.format_exc()))
            self.send_error(500, "Error serving download: {}".format(str(e)))
    
    def log_message(self, format, *args):
        """Override to customize logging"""
        print("{} - {}".format(self.address_string(), format % args))

if __name__ == "__main__":
    # Create images directory if it doesn't exist
    Path(IMAGES_DIR).mkdir(exist_ok=True)
    
    print("Starting Image Gallery Server")
    print("Port: {}".format(PORT))
    print("Images directory: {}".format(Path(IMAGES_DIR).resolve()))
    print("Open your browser to: http://localhost:{}".format(PORT))
    print("Press Ctrl+C to stop the server\n")
    
    httpd = socketserver.TCPServer(("", PORT), GalleryHandler)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\nServer stopped.")
    finally:
        httpd.server_close()
