import cv2
import numpy as np
import os
import json
import time
import threading
import queue
import requests
import hashlib
import base64
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, Optional, Tuple, List
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from PIL import Image, ImageTk, ImageDraw, ImageFont
import customtkinter as ctk
from dataclasses import dataclass
from enum import Enum
import io
import wave
import subprocess
import tempfile

# Konfiguracja CustomTkinter - motyw zielony/ciemny jak na stronie
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")

# ==================== KONFIGURACJA ====================

# Użyj folderu użytkownika zamiast bieżącego katalogu
USER_HOME = Path.home()
APP_DIR = USER_HOME / "ASCII_CAMERA_v25"
CONFIG_DIR = APP_DIR / "ascii_config"
CONFIG_FILE = CONFIG_DIR / "settings.json"
CLOUD_CONFIG_FILE = CONFIG_DIR / "cloud_settings.json"
RECORDINGS_DIR = APP_DIR / "ascii_recordings"
THEMES_DIR = APP_DIR / "themes"

# Upewnij się, że foldery istnieją
APP_DIR.mkdir(exist_ok=True)
CONFIG_DIR.mkdir(exist_ok=True)
RECORDINGS_DIR.mkdir(exist_ok=True)
THEMES_DIR.mkdir(exist_ok=True)

# Language support
LANGUAGES = {
    "pl": {
        "title": "ASCII CAMERA v2.5",
        "start": "Start",
        "stop": "Stop",
        "settings": "Ustawienia",
        "export": "Eksport",
        "effects": "Efekty",
        "ai_enhance": "AI Ulepszanie",
        "cloud_sync": "Synchronizacja Chmury"
    },
    "en": {
        "title": "ASCII CAMERA v2.5",
        "start": "Start",
        "stop": "Stop", 
        "settings": "Settings",
        "export": "Export",
        "effects": "Effects",
        "ai_enhance": "AI Enhancement",
        "cloud_sync": "Cloud Sync"
    }
}

# Enhanced ASCII sets with AI optimization
ASCII_SETS = {
    "standard": "@%#*+=-:. ",
    "detailed": "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ",
    "simple": "#=-. ",
    "block": "▓▒░ ",
    "minimal": "██▉▊▋▌▅▴▃▂▁ ",
    "circuit": "█▓▒░░ ",
    "inverse": " .:-=+*#%@█",
    "heavy": "█▓▒░",
    "gradients": "░▒▓█",
    "ai_optimized": "█▓▒░▒▓█@#%*+=-:. ",  # AI will optimize this dynamically
    "retro": "█▀▄■□▪▫●○◐◑◒◓◔◕◖◗",
    "matrix": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
}

# Enhanced profiles with new features
PROFILES = {
    "normal": {
        "width": 120,
        "char_set": "standard",
        "use_color": False,
        "outline_only": False,
        "invert_colors": False,
        "brightness": 1.0,
        "contrast": 1.0,
        "sharpen": False,
        "target_fps": 30,
        "ai_enhanced": False,
        "effects": [],
    },
    "detailed": {
        "width": 150,
        "char_set": "detailed",
        "use_color": True,
        "outline_only": False,
        "invert_colors": False,
        "brightness": 1.1,
        "contrast": 1.2,
        "sharpen": True,
        "target_fps": 20,
        "ai_enhanced": True,
        "effects": ["sharpen", "edge_enhance"],
    },
    "performance": {
        "width": 80,
        "char_set": "simple",
        "use_color": False,
        "outline_only": False,
        "invert_colors": False,
        "brightness": 1.0,
        "contrast": 1.0,
        "sharpen": False,
        "target_fps": 60,
        "ai_enhanced": False,
        "effects": [],
    },
    "artistic": {
        "width": 100,
        "char_set": "block",
        "use_color": True,
        "outline_only": True,
        "invert_colors": False,
        "brightness": 0.9,
        "contrast": 1.3,
        "sharpen": False,
        "target_fps": 30,
        "ai_enhanced": True,
        "effects": ["blur", "color_boost"],
    },
    "ai_studio": {
        "width": 140,
        "char_set": "ai_optimized",
        "use_color": True,
        "outline_only": False,
        "invert_colors": False,
        "brightness": 1.0,
        "contrast": 1.1,
        "sharpen": True,
        "target_fps": 25,
        "ai_enhanced": True,
        "effects": ["smart_enhance", "adaptive_lighting"],
    },
    "retro_crt": {
        "width": 100,
        "char_set": "retro",
        "use_color": True,
        "outline_only": False,
        "invert_colors": False,
        "brightness": 0.8,
        "contrast": 1.4,
        "sharpen": True,
        "target_fps": 30,
        "ai_enhanced": False,
        "effects": ["crt_curve", "scanlines", "glow"],
    },
}

# Real-time effects
REAL_TIME_EFFECTS = {
    "blur": {"kernel": (5, 5), "strength": 1.0},
    "sharpen": {"kernel": np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]]), "strength": 0.5},
    "emboss": {"kernel": np.array([[-2, -1, 0], [-1, 1, 1], [0, 1, 2]]), "strength": 1.0},
    "edge_enhance": {"kernel": np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]), "strength": 0.8},
    "color_boost": {"factor": 1.3},
    "crt_curve": {"curve_strength": 0.1},
    "scanlines": {"intensity": 0.3},
    "glow": {"radius": 2, "intensity": 0.5},
    "smart_enhance": {"auto_mode": True},
    "adaptive_lighting": {"adaptive": True},
}

@dataclass
class CloudSettings:
    user_id: str
    sync_enabled: bool
    shared_profiles: List[Dict]
    api_endpoint: str = "https://ascii-camera-api.example.com"

class ExportFormat(Enum):
    PNG = "png"
    GIF = "gif" 
    MP4 = "mp4"
    WEBM = "webm"
    TXT = "txt"

# ==================== AI ENHANCEMENT MODULE ====================

class AIEnhancer:
    """AI-powered ASCII optimization"""
    
    def __init__(self):
        self.edge_detector = cv2.Canny
        self.contour_detector = cv2.findContours
        
    def optimize_charset(self, frame: np.ndarray, charset: str) -> str:
        """Dynamically optimize charset based on image content"""
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if len(frame.shape) == 3 else frame
        
        # Detect edges and contours
        edges = self.edge_detector(gray, 50, 150)
        contours, _ = self.contour_detector(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        # Analyze image complexity
        complexity = len(contours) / (gray.shape[0] * gray.shape[1])
        
        # Adjust charset based on complexity
        if complexity > 0.001:  # High detail
            return charset + "█▓▒░"
        elif complexity < 0.0001:  # Low detail
            return "@%#*+=-:. "
        else:
            return charset
    
    def adaptive_lighting(self, frame: np.ndarray) -> np.ndarray:
        """Apply adaptive lighting based on histogram"""
        if len(frame.shape) == 3:
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        else:
            gray = frame
            
        # Calculate histogram
        hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
        
        # Apply adaptive histogram equalization
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
        enhanced = clahe.apply(gray)
        
        if len(frame.shape) == 3:
            enhanced = cv2.cvtColor(enhanced, cv2.COLOR_GRAY2BGR)
            
        return enhanced
    
    def smart_enhance(self, frame: np.ndarray) -> np.ndarray:
        """Smart enhancement using multiple techniques"""
        # Apply bilateral filter for noise reduction
        denoised = cv2.bilateralFilter(frame, 9, 75, 75)
        
        # Apply unsharp masking
        gaussian = cv2.GaussianBlur(denoised, (0, 0), 2.0)
        enhanced = cv2.addWeighted(denoised, 1.5, gaussian, -0.5, 0)
        
        return enhanced

# ==================== EFFECTS PROCESSOR ====================

class EffectsProcessor:
    """Real-time effects processing"""
    
    def __init__(self):
        self.active_effects = []
        
    def apply_effect(self, frame: np.ndarray, effect_name: str, params: Dict) -> np.ndarray:
        """Apply a single effect to frame"""
        if effect_name == "blur":
            kernel_size = params.get("kernel", (5, 5))
            return cv2.GaussianBlur(frame, kernel_size, 0)
            
        elif effect_name == "sharpen":
            kernel = params.get("kernel")
            strength = params.get("strength", 0.5)
            sharpened = cv2.filter2D(frame, -1, kernel * strength)
            return cv2.addWeighted(frame, 1 - strength, sharpened, strength, 0)
            
        elif effect_name == "emboss":
            kernel = params.get("kernel")
            return cv2.filter2D(frame, -1, kernel)
            
        elif effect_name == "edge_enhance":
            kernel = params.get("kernel")
            strength = params.get("strength", 0.8)
            enhanced = cv2.filter2D(frame, -1, kernel * strength)
            return cv2.addWeighted(frame, 1 - strength, enhanced, strength, 0)
            
        elif effect_name == "color_boost":
            factor = params.get("factor", 1.3)
            return np.clip(frame * factor, 0, 255).astype(np.uint8)
            
        elif effect_name == "crt_curve":
            # Simple barrel distortion simulation
            h, w = frame.shape[:2]
            curve_strength = params.get("curve_strength", 0.1)
            
            # Create distortion map
            cx, cy = w // 2, h // 2
            max_radius = np.sqrt(cx**2 + cy**2)
            
            y, x = np.ogrid[:h, :w]
            dx, dy = x - cx, y - cy
            distance = np.sqrt(dx**2 + dy**2)
            
            # Apply barrel distortion
            factor = 1 + curve_strength * (distance / max_radius)**2
            map_x = (dx * factor + cx).astype(np.float32)
            map_y = (dy * factor + cy).astype(np.float32)
            
            return cv2.remap(frame, map_x, map_y, cv2.INTER_LINEAR)
            
        elif effect_name == "scanlines":
            intensity = params.get("intensity", 0.3)
            result = frame.copy()
            if len(result.shape) == 3:
                result[::2, :, :] = (result[::2, :, :] * (1 - intensity)).astype(np.uint8)
            else:
                result[::2, :] = (result[::2, :] * (1 - intensity)).astype(np.uint8)
            return result
            
        elif effect_name == "glow":
            radius = params.get("radius", 2)
            intensity = params.get("intensity", 0.5)
            blurred = cv2.GaussianBlur(frame, (radius*2+1, radius*2+1), 0)
            return cv2.addWeighted(frame, 1 - intensity, blurred, intensity, 0)
            
        return frame
    
    def apply_effects(self, frame: np.ndarray, effects: List[str]) -> np.ndarray:
        """Apply multiple effects in sequence"""
        result = frame.copy()
        for effect_name in effects:
            if effect_name in REAL_TIME_EFFECTS:
                params = REAL_TIME_EFFECTS[effect_name]
                result = self.apply_effect(result, effect_name, params)
        return result

# ==================== EXPORT MANAGER ====================

class ExportManager:
    """Advanced export functionality"""
    
    def __init__(self, recordings_dir: Path):
        self.recordings_dir = recordings_dir
        self.temp_dir = recordings_dir / "temp"
        self.temp_dir.mkdir(exist_ok=True)
        self.export_directory = recordings_dir  # Default export directory
        
    def export_png(self, ascii_text: str, filename: str = None, custom_path: str = None) -> str:
        """Export ASCII as PNG image"""
        if filename is None:
            filename = f"ascii_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
            
        # Use custom path if provided, otherwise use default
        if custom_path:
            filepath = Path(custom_path) / filename
        else:
            filepath = self.export_directory / filename
        
        # Create image from ASCII text
        lines = ascii_text.split('\n')
        max_width = max(len(line) for line in lines) if lines else 100
        height = len(lines)
        
        # Create image with green text on black background
        img = Image.new('RGB', (max_width * 8, height * 16), color='black')
        draw = ImageDraw.Draw(img)
        
        try:
            font = ImageFont.truetype("courier.ttf", 14)
        except:
            font = ImageFont.load_default()
            
        y_offset = 0
        for line in lines:
            draw.text((0, y_offset), line, fill='#00ff00', font=font)
            y_offset += 16
            
        img.save(filepath)
        return str(filepath)
    
    def export_gif(self, frames: List[str], filename: str = None, fps: int = 10, custom_path: str = None) -> str:
        """Export ASCII frames as animated GIF"""
        if filename is None:
            filename = f"ascii_animation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.gif"
            
        # Use custom path if provided, otherwise use default
        if custom_path:
            filepath = Path(custom_path) / filename
        else:
            filepath = self.export_directory / filename
        images = []
        
        for frame_text in frames:
            # Convert text to image
            lines = frame_text.split('\n')
            max_width = max(len(line) for line in lines) if lines else 100
            height = len(lines)
            
            img = Image.new('RGB', (max_width * 8, height * 16), color='black')
            draw = ImageDraw.Draw(img)
            
            try:
                font = ImageFont.truetype("courier.ttf", 14)
            except:
                font = ImageFont.load_default()
                
            y_offset = 0
            for line in lines:
                draw.text((0, y_offset), line, fill='#00ff00', font=font)
                y_offset += 16
                
            images.append(img)
        
        if images:
            images[0].save(filepath, save_all=True, append_images=images[1:], 
                          duration=1000//fps, loop=0)
        
        return str(filepath)
    
    def export_mp4(self, frames: List[str], filename: str = None, fps: int = 30, custom_path: str = None) -> str:
        """Export ASCII frames as MP4 video using ffmpeg"""
        if filename is None:
            filename = f"ascii_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
            
        # Use custom path if provided, otherwise use default
        if custom_path:
            filepath = Path(custom_path) / filename
        else:
            filepath = self.export_directory / filename
        temp_images = []
        
        try:
            # Convert frames to images
            for i, frame_text in enumerate(frames):
                lines = frame_text.split('\n')
                max_width = max(len(line) for line in lines) if lines else 100
                height = len(lines)
                
                img = Image.new('RGB', (max_width * 8, height * 16), color='black')
                draw = ImageDraw.Draw(img)
                
                try:
                    font = ImageFont.truetype("courier.ttf", 14)
                except:
                    font = ImageFont.load_default()
                    
                y_offset = 0
                for line in lines:
                    draw.text((0, y_offset), line, fill='#00ff00', font=font)
                    y_offset += 16
                
                temp_img_path = self.temp_dir / f"frame_{i:06d}.png"
                img.save(temp_img_path)
                temp_images.append(temp_img_path)
            
            # Use ffmpeg to create video
            if temp_images:
                cmd = [
                    'ffmpeg', '-y',  # Overwrite output file
                    '-framerate', str(fps),
                    '-i', str(self.temp_dir / 'frame_%06d.png'),
                    '-c:v', 'libx264',
                    '-pix_fmt', 'yuv420p',
                    '-crf', '23',
                    str(filepath)
                ]
                
                try:
                    subprocess.run(cmd, check=True, capture_output=True)
                except (subprocess.CalledProcessError, FileNotFoundError):
                    # Fallback: create simple image sequence info
                    print("FFmpeg not found. Install FFmpeg for MP4 export.")
                    return self.export_gif(frames, filename.replace('.mp4', '.gif'))
        
        finally:
            # Clean up temporary images
            for img_path in temp_images:
                if img_path.exists():
                    img_path.unlink()
        
        return str(filepath)
    
    def export_txt(self, ascii_text: str, filename: str = None, custom_path: str = None) -> str:
        """Export ASCII as plain text file"""
        if filename is None:
            filename = f"ascii_text_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
            
        # Use custom path if provided, otherwise use default
        if custom_path:
            filepath = Path(custom_path) / filename
        else:
            filepath = self.export_directory / filename
        
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(ascii_text)
        
        return str(filepath)

# ==================== CLOUD SYNC MANAGER ====================

class CloudSyncManager:
    """Cloud synchronization for settings and profiles"""
    
    def __init__(self, config_file: Path):
        self.config_file = config_file
        self.settings = self.load_settings()
        
    def load_settings(self) -> CloudSettings:
        """Load cloud settings from file"""
        if self.config_file.exists():
            try:
                with open(self.config_file, 'r') as f:
                    data = json.load(f)
                    return CloudSettings(**data)
            except:
                pass
        
        # Create default settings
        user_id = hashlib.md5(str(os.getlogin()).encode()).hexdigest()[:16]
        return CloudSettings(user_id=user_id, sync_enabled=False, shared_profiles=[])
    
    def save_settings(self) -> bool:
        """Save cloud settings to file"""
        try:
            data = {
                "user_id": self.settings.user_id,
                "sync_enabled": self.settings.sync_enabled,
                "shared_profiles": self.settings.shared_profiles,
                "api_endpoint": self.settings.api_endpoint
            }
            
            with open(self.config_file, 'w') as f:
                json.dump(data, f, indent=2)
            return True
        except:
            return False
    
    def upload_profile(self, profile_name: str, profile_data: Dict) -> bool:
        """Upload profile to cloud (mock implementation)"""
        if not self.settings.sync_enabled:
            return False
            
        # Mock cloud upload - in real implementation would use API
        try:
            profile_entry = {
                "name": profile_name,
                "data": profile_data,
                "user_id": self.settings.user_id,
                "timestamp": datetime.now().isoformat()
            }
            
            self.settings.shared_profiles.append(profile_entry)
            return self.save_settings()
        except:
            return False
    
    def download_profiles(self) -> List[Dict]:
        """Download shared profiles from cloud (mock implementation)"""
        if not self.settings.sync_enabled:
            return []
            
        # Mock cloud download - in real implementation would use API
        return self.settings.shared_profiles

# ==================== MAIN ASCII CONVERTER ====================

class ASCIIConverter:
    """Enhanced ASCII converter with AI and effects"""
    
    def __init__(self):
        self.ai_enhancer = AIEnhancer()
        self.effects_processor = EffectsProcessor()
        self.current_charset = ASCII_SETS["standard"]
        
    def apply_color_to_char(self, char, color):
        """Apply color to character using CTk-compatible text colors"""
        r, g, b = color
        
        # Convert RGB to hex for CTk
        hex_color = f"#{r:02x}{g:02x}{b:02x}"
        
        # Return character with color information for CTk to process
        if r > 127 or g > 127 or b > 127:
            return f"COLOR:{hex_color}:{char}"
        else:
            # For dark colors, use a darker version
            return f"COLOR:{hex_color}:{char}"
        
    def frame_to_ascii(self, frame: np.ndarray, width: int, charset: str, 
                      use_color: bool = False, ai_enhanced: bool = False,
                      effects: List[str] = None, horizontal_flip: bool = False, 
                      use_html_colors: bool = True, **kwargs) -> str:
        """Convert frame to ASCII with AI enhancement and effects"""
        if effects is None:
            effects = []
            
        # Apply real-time effects first
        processed_frame = self.effects_processor.apply_effects(frame, effects)
        
        # Apply horizontal flip if enabled (mirror effect removal)
        if horizontal_flip:
            processed_frame = cv2.flip(processed_frame, 1)  # 1 = horizontal flip
        
        # Apply AI enhancement if enabled
        if ai_enhanced:
            if "adaptive_lighting" in effects:
                processed_frame = self.ai_enhancer.adaptive_lighting(processed_frame)
            if "smart_enhance" in effects:
                processed_frame = self.ai_enhancer.smart_enhance(processed_frame)
            
            # Optimize charset based on content
            charset = self.ai_enhancer.optimize_charset(processed_frame, charset)
        
        # Convert to grayscale if needed
        if len(processed_frame.shape) == 3:
            gray = cv2.cvtColor(processed_frame, cv2.COLOR_BGR2GRAY)
        else:
            gray = processed_frame
            
        # Apply adjustments
        brightness = kwargs.get('brightness', 1.0)
        contrast = kwargs.get('contrast', 1.0)
        gray = cv2.convertScaleAbs(gray, alpha=contrast, beta=brightness * 255 - 255)
        
        # Resize to target width
        height = int(gray.shape[0] * (width / gray.shape[1]) * 0.55)  # Adjust for character aspect ratio
        resized = cv2.resize(gray, (width, height), interpolation=cv2.INTER_AREA)
        
        # Map pixels to ASCII characters
        chars = list(charset)
        ascii_lines = []
        color_map = []  # Store color information for each character
        
        for y_idx, row in enumerate(resized):
            line = ""
            line_colors = []
            for x_idx, pixel in enumerate(row):
                char_index = int((pixel / 255) * (len(chars) - 1))
                char = chars[min(char_index, len(chars) - 1)]
                
                if use_color and len(processed_frame.shape) == 3:
                    # Get original pixel color - fix bounds checking
                    y_pos = min(int(y_idx / resized.shape[0] * processed_frame.shape[0]), processed_frame.shape[0] - 1)
                    x_pos = min(int(x_idx / resized.shape[1] * processed_frame.shape[1]), processed_frame.shape[1] - 1)
                    color = processed_frame[y_pos, x_pos]
                    line_colors.append(color)
                else:
                    line_colors.append(None)
                    
                line += char
            ascii_lines.append(line)
            color_map.append(line_colors)
        
        return '\n'.join(ascii_lines), color_map

# ==================== ENHANCED GUI ====================

class ASCIICameraGUI:
    """Enhanced GUI with Studio Mode and new features"""
    
    def __init__(self):
        self.root = ctk.CTk()
        self.root.title("ASCII CAMERA v2.5 - Studio Edition")
        self.root.geometry("1400x900")
        
        # Initialize components
        self.converter = ASCIIConverter()
        self.export_manager = ExportManager(RECORDINGS_DIR)
        self.cloud_manager = CloudSyncManager(CLOUD_CONFIG_FILE)
        
        # State variables
        self.current_language = "pl"
        self.is_recording = False
        self.current_profile = "normal"
        self.camera = None
        self.recording_thread = None
        self.captured_frames = []
        self.horizontal_flip_enabled = False  # Mirror effect control
        self.use_html_colors = True  # Use HTML-like tags for colors in CTk
        
        # Theme
        self.current_theme = "dark"
        
        # Setup GUI
        self.setup_styles()
        self.create_widgets()
        self.load_settings()
        
    def setup_styles(self):
        """Setup custom styles for the GUI"""
        ctk.set_appearance_mode(self.current_theme)
        
    def create_widgets(self):
        """Create all GUI widgets"""
        # Main container
        self.main_container = ctk.CTkFrame(self.root)
        self.main_container.pack(fill="both", expand=True, padx=10, pady=10)
        
        # Create menu bar
        self.create_menu_bar()
        
        # Create main layout
        self.create_main_layout()
        
    def create_menu_bar(self):
        """Create menu bar with language switcher"""
        menu_frame = ctk.CTkFrame(self.main_container)
        menu_frame.pack(fill="x", padx=5, pady=5)
        
        # Language switcher
        self.lang_var = ctk.StringVar(value=self.current_language)
        lang_menu = ctk.CTkOptionMenu(menu_frame, variable=self.lang_var, 
                                     values=["pl", "en"], command=self.change_language)
        lang_menu.pack(side="right", padx=5)
        
        # Theme switcher
        theme_btn = ctk.CTkButton(menu_frame, text="🌓 Theme", command=self.toggle_theme)
        theme_btn.pack(side="right", padx=5)
        
        # Cloud sync button
        self.cloud_btn = ctk.CTkButton(menu_frame, text="☁️ Cloud", command=self.toggle_cloud_sync)
        self.cloud_btn.pack(side="right", padx=5)
        
    def create_main_layout(self):
        """Create main layout with studio mode"""
        # Left panel - Controls
        self.left_panel = ctk.CTkFrame(self.main_container)
        self.left_panel.pack(side="left", fill="y", padx=5)
        
        # Center panel - Preview
        self.center_panel = ctk.CTkFrame(self.main_container)
        self.center_panel.pack(side="left", fill="both", expand=True, padx=5)
        
        # Right panel - Effects & Export
        self.right_panel = ctk.CTkFrame(self.main_container)
        self.right_panel.pack(side="right", fill="y", padx=5)
        
        self.create_control_panel()
        self.create_preview_panel()
        self.create_effects_panel()
        
    def create_control_panel(self):
        """Create control panel"""
        # Profile selector
        ctk.CTkLabel(self.left_panel, text="Profile:", font=("Arial", 14, "bold")).pack(pady=5)
        self.profile_var = ctk.StringVar(value=self.current_profile)
        profile_menu = ctk.CTkOptionMenu(self.left_panel, variable=self.profile_var,
                                        values=list(PROFILES.keys()), command=self.change_profile)
        profile_menu.pack(pady=5, padx=10, fill="x")
        
        # AI Enhancement toggle
        self.ai_var = ctk.BooleanVar(value=False)
        ai_check = ctk.CTkCheckBox(self.left_panel, text="AI Enhancement", variable=self.ai_var)
        ai_check.pack(pady=5)
        
        # Basic controls
        controls_frame = ctk.CTkFrame(self.left_panel)
        controls_frame.pack(pady=10, padx=10, fill="x")
        
        # Width slider
        ctk.CTkLabel(controls_frame, text="Width:").pack()
        self.width_var = ctk.IntVar(value=120)
        self.width_slider = ctk.CTkSlider(controls_frame, from_=50, to=200, 
                                         variable=self.width_var, number_of_steps=150)
        self.width_slider.pack(fill="x", pady=5)
        
        # Brightness slider
        ctk.CTkLabel(controls_frame, text="Brightness:").pack()
        self.brightness_var = ctk.DoubleVar(value=1.0)
        brightness_slider = ctk.CTkSlider(controls_frame, from_=0.5, to=2.0, 
                                         variable=self.brightness_var)
        brightness_slider.pack(fill="x", pady=5)
        
        # Contrast slider
        ctk.CTkLabel(controls_frame, text="Contrast:").pack()
        self.contrast_var = ctk.DoubleVar(value=1.0)
        contrast_slider = ctk.CTkSlider(controls_frame, from_=0.5, to=2.0, 
                                       variable=self.contrast_var)
        contrast_slider.pack(fill="x", pady=5)
        
        # Control buttons
        button_frame = ctk.CTkFrame(self.left_panel)
        button_frame.pack(pady=20, padx=10, fill="x")
        
        self.start_btn = ctk.CTkButton(button_frame, text="▶ Start", command=self.start_capture,
                                      fg_color="green", hover_color="darkgreen")
        self.start_btn.pack(pady=5, fill="x")
        
        self.stop_btn = ctk.CTkButton(button_frame, text="⏹ Stop", command=self.stop_capture,
                                     fg_color="red", hover_color="darkred", state="disabled")
        self.stop_btn.pack(pady=5, fill="x")
        
        # Export buttons
        export_frame = ctk.CTkFrame(self.left_panel)
        export_frame.pack(pady=10, padx=10, fill="x")
        
        ctk.CTkLabel(export_frame, text="Export:", font=("Arial", 12, "bold")).pack(pady=5)
        
        # Export directory selector
        dir_frame = ctk.CTkFrame(export_frame)
        dir_frame.pack(pady=5, fill="x")
        
        self.export_dir_var = ctk.StringVar(value=str(self.export_manager.export_directory))
        dir_entry = ctk.CTkEntry(dir_frame, textvariable=self.export_dir_var, width=200)
        dir_entry.pack(side="left", padx=5, fill="x", expand=True)
        
        dir_btn = ctk.CTkButton(dir_frame, text="📁", width=30, command=self.select_export_directory)
        dir_btn.pack(side="right", padx=5)
        
        ctk.CTkButton(export_frame, text="📷 PNG", command=lambda: self.export_frame("png")).pack(pady=2, fill="x")
        ctk.CTkButton(export_frame, text="🎬 GIF", command=lambda: self.export_animation("gif")).pack(pady=2, fill="x")
        ctk.CTkButton(export_frame, text="📹 MP4", command=lambda: self.export_animation("mp4")).pack(pady=2, fill="x")
        ctk.CTkButton(export_frame, text="📄 TXT", command=lambda: self.export_frame("txt")).pack(pady=2, fill="x")
        
        # Mirror effect toggle
        mirror_frame = ctk.CTkFrame(self.left_panel)
        mirror_frame.pack(pady=10, padx=10, fill="x")
        
        self.mirror_var = ctk.BooleanVar(value=self.horizontal_flip_enabled)
        mirror_check = ctk.CTkCheckBox(mirror_frame, text="Usuń efekt lustra (prawo=prawo)", 
                                     variable=self.mirror_var, command=self.toggle_mirror)
        mirror_check.pack(pady=5)
        
    def convert_color_to_hex(self, color):
        """Convert RGB color to hex string"""
        r, g, b = color
        return f"#{r:02x}{g:02x}{b:02x}"
    
    def change_color_mode(self, mode):
        """Change color display mode"""
        self.use_html_colors = (mode == "colored")
        
    def toggle_mirror(self):
        """Toggle horizontal flip (mirror effect)"""
        self.horizontal_flip_enabled = self.mirror_var.get()
        
    def select_export_directory(self):
        """Select custom export directory"""
        directory = filedialog.askdirectory(title="Wybierz katalog eksportu")
        if directory:
            self.export_dir_var.set(directory)
            self.export_manager.export_directory = Path(directory)
        
    def create_preview_panel(self):
        # ASCII preview
        ctk.CTkLabel(self.center_panel, text="ASCII Preview", font=("Arial", 16, "bold")).pack(pady=5)
        
        # Create scrollable text widget for ASCII
        self.ascii_frame = ctk.CTkFrame(self.center_panel)
        self.ascii_frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        # Use CTkTextbox with custom font
        self.ascii_text = ctk.CTkTextbox(self.ascii_frame, font=("Courier New", 10))
        self.ascii_text.pack(fill="both", expand=True)
        
        # Set default text
        self.ascii_text.insert("0.0", "ASCII Camera v2.5\nPress Start to begin...")
        
        # Status bar
        self.status_label = ctk.CTkLabel(self.center_panel, text="Ready", font=("Arial", 10))
        self.status_label.pack(pady=5)
        
    def create_effects_panel(self):
        """Create effects panel"""
        ctk.CTkLabel(self.right_panel, text="Real-time Effects", font=("Arial", 14, "bold")).pack(pady=5)
        
        # Effects checkboxes
        self.effects_vars = {}
        effects_frame = ctk.CTkScrollableFrame(self.right_panel, height=300)
        effects_frame.pack(pady=10, padx=10, fill="both", expand=True)
        
        for effect_name in REAL_TIME_EFFECTS.keys():
            var = ctk.BooleanVar(value=False)
            checkbox = ctk.CTkCheckBox(effects_frame, text=effect_name.replace("_", " ").title(), 
                                       variable=var)
            checkbox.pack(anchor="w", pady=2)
            self.effects_vars[effect_name] = var
        
        # Charset selector
        charset_frame = ctk.CTkFrame(self.right_panel)
        charset_frame.pack(pady=10, padx=10, fill="x")
        
        ctk.CTkLabel(charset_frame, text="Charset:", font=("Arial", 12, "bold")).pack(pady=5)
        self.charset_var = ctk.StringVar(value="standard")
        charset_menu = ctk.CTkOptionMenu(charset_frame, variable=self.charset_var,
                                        values=list(ASCII_SETS.keys()))
        charset_menu.pack(pady=5, fill="x")
        
        # Color toggle
        self.color_var = ctk.BooleanVar(value=False)
        color_check = ctk.CTkCheckBox(charset_frame, text="Enable Colors", variable=self.color_var)
        color_check.pack(pady=5)
        
        # Color mode selector
        color_mode_frame = ctk.CTkFrame(charset_frame)
        color_mode_frame.pack(pady=5, fill="x")
        
        ctk.CTkLabel(color_mode_frame, text="Color Mode:", font=("Arial", 10)).pack()
        self.color_mode_var = ctk.StringVar(value="colored")
        color_mode_menu = ctk.CTkOptionMenu(color_mode_frame, variable=self.color_mode_var,
                                           values=["colored", "simple"], command=self.change_color_mode)
        color_mode_menu.pack(pady=2, fill="x")
        
        # Studio mode info
        studio_frame = ctk.CTkFrame(self.right_panel)
        studio_frame.pack(pady=10, padx=10, fill="x")
        
        ctk.CTkLabel(studio_frame, text="Studio Mode", font=("Arial", 12, "bold")).pack(pady=5)
        info_text = ctk.CTkTextbox(studio_frame, height=100)
        info_text.pack(pady=5, fill="x")
        info_text.insert("0.0", "Studio Mode provides:\n• Real-time effects\n• AI enhancement\n• Advanced export options\n• Cloud sync")
        info_text.configure(state="disabled")
        
    def change_language(self, lang: str):
        """Change interface language"""
        self.current_language = lang
        # Update UI text based on selected language
        # This is a simplified implementation
        
    def toggle_theme(self):
        """Toggle between dark and light theme"""
        self.current_theme = "light" if self.current_theme == "dark" else "dark"
        ctk.set_appearance_mode(self.current_theme)
        
    def toggle_cloud_sync(self):
        """Toggle cloud synchronization"""
        self.cloud_manager.settings.sync_enabled = not self.cloud_manager.settings.sync_enabled
        self.cloud_manager.save_settings()
        
        status = "enabled" if self.cloud_manager.settings.sync_enabled else "disabled"
        messagebox.showinfo("Cloud Sync", f"Cloud sync {status}")
        
    def change_profile(self, profile_name: str):
        """Change current profile"""
        self.current_profile = profile_name
        profile = PROFILES[profile_name]
        
        # Update UI controls
        self.width_var.set(profile["width"])
        self.brightness_var.set(profile["brightness"])
        self.contrast_var.set(profile["contrast"])
        self.charset_var.set(profile["char_set"])
        self.color_var.set(profile["use_color"])
        self.ai_var.set(profile.get("ai_enhanced", False))
        
        # Update effects
        for effect_name, var in self.effects_vars.items():
            var.set(effect_name in profile.get("effects", []))
            
    def start_capture(self):
        """Start camera capture"""
        if self.camera is None:
            self.camera = cv2.VideoCapture(0)
            
        if not self.camera.isOpened():
            messagebox.showerror("Error", "Cannot open camera")
            return
            
        self.is_recording = True
        self.captured_frames = []
        
        # Update UI
        self.start_btn.configure(state="disabled")
        self.stop_btn.configure(state="normal")
        self.status_label.configure(text="Recording...")
        
        # Start capture thread
        self.recording_thread = threading.Thread(target=self.capture_loop)
        self.recording_thread.daemon = True
        self.recording_thread.start()
        
    def stop_capture(self):
        """Stop camera capture"""
        self.is_recording = False
        
        # Update UI
        self.start_btn.configure(state="normal")
        self.stop_btn.configure(state="disabled")
        self.status_label.configure(text=f"Stopped - {len(self.captured_frames)} frames captured")
        
    def capture_loop(self):
        """Main capture loop"""
        while self.is_recording:
            ret, frame = self.camera.read()
            if not ret:
                break
                
            # Get current settings
            profile = PROFILES[self.current_profile]
            width = self.width_var.get()
            charset = ASCII_SETS[self.charset_var.get()]
            use_color = self.color_var.get()
            ai_enhanced = self.ai_var.get()
            
            # Get active effects
            active_effects = [name for name, var in self.effects_vars.items() if var.get()]
            
            # Convert to ASCII
            ascii_result, color_map = self.converter.frame_to_ascii(
                frame, width, charset, use_color, ai_enhanced, active_effects,
                horizontal_flip=self.horizontal_flip_enabled,
                use_html_colors=self.use_html_colors,
                brightness=self.brightness_var.get(),
                contrast=self.contrast_var.get()
            )
            
            # Update preview with colors
            self.ascii_text.delete("0.0", "end")
            if use_color and self.use_html_colors and color_map:
                # Simple approach: insert text and apply colors line by line
                lines = ascii_result.split('\n')
                for line_idx, line in enumerate(lines):
                    if line_idx < len(color_map):
                        # Insert the line
                        start_pos = f"{line_idx + 1}.0"
                        end_pos = f"{line_idx + 1}.end"
                        self.ascii_text.insert("end", line + ("\n" if line_idx < len(lines) - 1 else ""))
                        
                        # Apply colors to character ranges in this line
                        line_colors = color_map[line_idx]
                        for char_idx, color in enumerate(line_colors):
                            if color is not None:
                                r, g, b = color
                                hex_color = f"#{r:02x}{g:02x}{b:02x}"
                                char_start = f"{line_idx + 1}.{char_idx}"
                                char_end = f"{line_idx + 1}.{char_idx + 1}"
                                self.ascii_text.tag_add(f"color_{line_idx}_{char_idx}", char_start, char_end)
                                self.ascii_text.tag_config(f"color_{line_idx}_{char_idx}", foreground=hex_color)
            else:
                # Insert plain text
                self.ascii_text.insert("0.0", ascii_result)
            
            # Store frame for export
            self.captured_frames.append(ascii_result)
            
            # Limit stored frames
            if len(self.captured_frames) > 300:  # Keep last 10 seconds at 30fps
                self.captured_frames.pop(0)
                
            time.sleep(1 / profile["target_fps"])
            
    def export_frame(self, format_type: str):
        """Export current frame"""
        if not self.captured_frames:
            messagebox.showwarning("Warning", "No frames to export")
            return
            
        current_frame = self.captured_frames[-1]
        export_path = str(self.export_manager.export_directory)
        
        if format_type == "png":
            filepath = self.export_manager.export_png(current_frame, custom_path=export_path)
        elif format_type == "txt":
            filepath = self.export_manager.export_txt(current_frame, custom_path=export_path)
        else:
            return
            
        messagebox.showinfo("Export Complete", f"Frame exported to:\n{filepath}")
        
    def export_animation(self, format_type: str):
        """Export captured frames as animation"""
        if len(self.captured_frames) < 2:
            messagebox.showwarning("Warning", "Need at least 2 frames for animation")
            return
            
        profile = PROFILES[self.current_profile]
        export_path = str(self.export_manager.export_directory)
        
        if format_type == "gif":
            filepath = self.export_manager.export_gif(self.captured_frames, fps=profile["target_fps"], custom_path=export_path)
        elif format_type == "mp4":
            filepath = self.export_manager.export_mp4(self.captured_frames, fps=profile["target_fps"], custom_path=export_path)
        else:
            return
            
        messagebox.showinfo("Export Complete", f"Animation exported to:\n{filepath}")
        
    def load_settings(self):
        """Load saved settings"""
        if CONFIG_FILE.exists():
            try:
                with open(CONFIG_FILE, 'r') as f:
                    settings = json.load(f)
                    self.current_profile = settings.get("profile", "normal")
                    self.change_profile(self.current_profile)
                    
                    # Load color mode
                    if "color_mode" in settings:
                        self.color_mode_var.set(settings["color_mode"])
                        self.change_color_mode(settings["color_mode"])
                    
                    # Load export directory
                    if "export_directory" in settings:
                        self.export_manager.export_directory = Path(settings["export_directory"])
                        self.export_dir_var.set(str(self.export_manager.export_directory))
                    
                    # Load mirror setting
                    if "horizontal_flip" in settings:
                        self.horizontal_flip_enabled = settings["horizontal_flip"]
                        self.mirror_var.set(self.horizontal_flip_enabled)
            except:
                pass
                
    def save_settings(self):
        """Save current settings"""
        settings = {
            "profile": self.current_profile,
            "width": self.width_var.get(),
            "brightness": self.brightness_var.get(),
            "contrast": self.contrast_var.get(),
            "charset": self.charset_var.get(),
            "use_color": self.color_var.get(),
            "ai_enhanced": self.ai_var.get(),
            "effects": [name for name, var in self.effects_vars.items() if var.get()],
            "export_directory": str(self.export_manager.export_directory),
            "horizontal_flip": self.horizontal_flip_enabled,
            "color_mode": self.color_mode_var.get()
        }
        
        try:
            with open(CONFIG_FILE, 'w') as f:
                json.dump(settings, f, indent=2)
        except:
            pass
            
    def run(self):
        """Run the application"""
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.root.mainloop()
        
    def on_closing(self):
        """Handle application closing"""
        self.is_recording = False
        if self.camera:
            self.camera.release()
        self.save_settings()
        self.root.destroy()

# ==================== MAIN ENTRY POINT ====================

def main():
    """Main entry point"""
    try:
        app = ASCIICameraGUI()
        app.run()
    except KeyboardInterrupt:
        print("\nApplication interrupted by user")
    except Exception as e:
        print(f"Error: {e}")
        messagebox.showerror("Error", f"Application error: {e}")

if __name__ == "__main__":
    main()
