#!/usr/bin/env python3
"""
Simple ereader application interface for pyWebLayout.

This module provides a user-friendly wrapper around the ereader infrastructure,
making it easy to build ebook reader applications with all essential features.

Example:
    from pyWebLayout.layout.ereader_application import EbookReader
    
    # Create reader
    reader = EbookReader(page_size=(800, 1000))
    
    # Load an EPUB
    reader.load_epub("mybook.epub")
    
    # Navigate
    reader.next_page()
    reader.previous_page()
    
    # Get current page
    page_image = reader.get_current_page()
    
    # Modify styling
    reader.increase_font_size()
    reader.set_line_spacing(8)
    
    # Chapter navigation
    chapters = reader.get_chapters()
    reader.jump_to_chapter("Chapter 1")
    
    # Position management
    reader.save_position("bookmark1")
    reader.load_position("bookmark1")
"""

from __future__ import annotations
from typing import List, Tuple, Optional, Dict, Any, Union
from pathlib import Path
import os

from PIL import Image

from pyWebLayout.io.readers.epub_reader import read_epub
from pyWebLayout.abstract.block import Block, HeadingLevel
from pyWebLayout.layout.ereader_manager import EreaderLayoutManager
from pyWebLayout.layout.ereader_layout import RenderingPosition
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.concrete.page import Page


class EbookReader:
    """
    Simple ereader application with all essential features.
    
    Features:
    - Load EPUB files
    - Forward/backward page navigation
    - Position save/load (based on abstract document structure)
    - Chapter navigation
    - Font size and spacing control
    - Current page retrieval as PIL Image
    
    The reader maintains position using abstract document structure (chapter/block/word indices),
    ensuring positions remain valid across font size and styling changes.
    """
    
    def __init__(self, 
                 page_size: Tuple[int, int] = (800, 1000),
                 margin: int = 40,
                 background_color: Tuple[int, int, int] = (255, 255, 255),
                 line_spacing: int = 5,
                 inter_block_spacing: int = 15,
                 bookmarks_dir: str = "ereader_bookmarks",
                 buffer_size: int = 5):
        """
        Initialize the ebook reader.
        
        Args:
            page_size: Page dimensions (width, height) in pixels
            margin: Page margin in pixels
            background_color: Background color as RGB tuple
            line_spacing: Spacing between lines in pixels
            inter_block_spacing: Spacing between blocks in pixels
            bookmarks_dir: Directory to store bookmarks and positions
            buffer_size: Number of pages to cache for performance
        """
        self.page_size = page_size
        self.bookmarks_dir = bookmarks_dir
        self.buffer_size = buffer_size
        
        # Create page style
        self.page_style = PageStyle(
            background_color=background_color,
            border_width=margin,
            border_color=(200, 200, 200),
            padding=(10, 10, 10, 10),
            line_spacing=line_spacing,
            inter_block_spacing=inter_block_spacing
        )
        
        # State
        self.manager: Optional[EreaderLayoutManager] = None
        self.blocks: Optional[List[Block]] = None
        self.document_id: Optional[str] = None
        self.book_title: Optional[str] = None
        self.book_author: Optional[str] = None
        
        # Font scale state
        self.base_font_scale = 1.0
        self.font_scale_step = 0.1  # 10% change per step
    
    def load_epub(self, epub_path: str) -> bool:
        """
        Load an EPUB file into the reader.
        
        Args:
            epub_path: Path to the EPUB file
            
        Returns:
            True if loaded successfully, False otherwise
        """
        try:
            # Validate path
            if not os.path.exists(epub_path):
                raise FileNotFoundError(f"EPUB file not found: {epub_path}")
            
            # Load the EPUB
            book = read_epub(epub_path)
            
            # Extract metadata
            self.book_title = book.get_title() or "Unknown Title"
            self.book_author = book.get_metadata('AUTHOR') or "Unknown Author"
            
            # Create document ID from filename
            self.document_id = Path(epub_path).stem
            
            # Extract all blocks from chapters
            self.blocks = []
            for chapter in book.chapters:
                if hasattr(chapter, '_blocks'):
                    self.blocks.extend(chapter._blocks)
            
            if not self.blocks:
                raise ValueError("No content blocks found in EPUB")
            
            # Initialize the ereader manager
            self.manager = EreaderLayoutManager(
                blocks=self.blocks,
                page_size=self.page_size,
                document_id=self.document_id,
                buffer_size=self.buffer_size,
                page_style=self.page_style,
                bookmarks_dir=self.bookmarks_dir
            )
            
            return True
            
        except Exception as e:
            print(f"Error loading EPUB: {e}")
            return False
    
    def is_loaded(self) -> bool:
        """Check if a book is currently loaded."""
        return self.manager is not None
    
    def get_current_page(self) -> Optional[Image.Image]:
        """
        Get the current page as a PIL Image.
        
        Returns:
            PIL Image of the current page, or None if no book is loaded
        """
        if not self.manager:
            return None
        
        try:
            page = self.manager.get_current_page()
            return page.render()
        except Exception as e:
            print(f"Error rendering page: {e}")
            return None
    
    def next_page(self) -> Optional[Image.Image]:
        """
        Navigate to the next page.
        
        Returns:
            PIL Image of the next page, or None if at end of book
        """
        if not self.manager:
            return None
        
        try:
            page = self.manager.next_page()
            if page:
                return page.render()
            return None
        except Exception as e:
            print(f"Error navigating to next page: {e}")
            return None
    
    def previous_page(self) -> Optional[Image.Image]:
        """
        Navigate to the previous page.
        
        Returns:
            PIL Image of the previous page, or None if at beginning of book
        """
        if not self.manager:
            return None
        
        try:
            page = self.manager.previous_page()
            if page:
                return page.render()
            return None
        except Exception as e:
            print(f"Error navigating to previous page: {e}")
            return None
    
    def save_position(self, name: str = "current_position") -> bool:
        """
        Save the current reading position with a name.
        
        The position is saved based on abstract document structure (chapter, block, word indices),
        making it stable across font size and styling changes.
        
        Args:
            name: Name for this saved position
            
        Returns:
            True if saved successfully, False otherwise
        """
        if not self.manager:
            return False
        
        try:
            self.manager.add_bookmark(name)
            return True
        except Exception as e:
            print(f"Error saving position: {e}")
            return False
    
    def load_position(self, name: str = "current_position") -> Optional[Image.Image]:
        """
        Load a previously saved reading position.
        
        Args:
            name: Name of the saved position
            
        Returns:
            PIL Image of the page at the loaded position, or None if not found
        """
        if not self.manager:
            return None
        
        try:
            page = self.manager.jump_to_bookmark(name)
            if page:
                return page.render()
            return None
        except Exception as e:
            print(f"Error loading position: {e}")
            return None
    
    def list_saved_positions(self) -> List[str]:
        """
        Get a list of all saved position names.
        
        Returns:
            List of position names
        """
        if not self.manager:
            return []
        
        try:
            bookmarks = self.manager.list_bookmarks()
            return [name for name, _ in bookmarks]
        except Exception as e:
            print(f"Error listing positions: {e}")
            return []
    
    def delete_position(self, name: str) -> bool:
        """
        Delete a saved position.
        
        Args:
            name: Name of the position to delete
            
        Returns:
            True if deleted, False otherwise
        """
        if not self.manager:
            return False
        
        return self.manager.remove_bookmark(name)
    
    def get_chapters(self) -> List[Tuple[str, int]]:
        """
        Get a list of all chapters with their indices.
        
        Returns:
            List of (chapter_title, chapter_index) tuples
        """
        if not self.manager:
            return []
        
        try:
            toc = self.manager.get_table_of_contents()
            # Convert to simplified format (title, index)
            chapters = []
            for i, (title, level, position) in enumerate(toc):
                chapters.append((title, i))
            return chapters
        except Exception as e:
            print(f"Error getting chapters: {e}")
            return []
    
    def get_chapter_positions(self) -> List[Tuple[str, RenderingPosition]]:
        """
        Get chapter titles with their exact rendering positions.
        
        Returns:
            List of (title, position) tuples
        """
        if not self.manager:
            return []
        
        try:
            toc = self.manager.get_table_of_contents()
            return [(title, position) for title, level, position in toc]
        except Exception as e:
            print(f"Error getting chapter positions: {e}")
            return []
    
    def jump_to_chapter(self, chapter: Union[str, int]) -> Optional[Image.Image]:
        """
        Navigate to a specific chapter by title or index.
        
        Args:
            chapter: Chapter title (string) or chapter index (integer)
            
        Returns:
            PIL Image of the first page of the chapter, or None if not found
        """
        if not self.manager:
            return None
        
        try:
            if isinstance(chapter, int):
                page = self.manager.jump_to_chapter_index(chapter)
            else:
                page = self.manager.jump_to_chapter(chapter)
            
            if page:
                return page.render()
            return None
        except Exception as e:
            print(f"Error jumping to chapter: {e}")
            return None
    
    def set_font_size(self, scale: float) -> Optional[Image.Image]:
        """
        Set the font size scale and re-render current page.
        
        Args:
            scale: Font scale factor (1.0 = normal, 2.0 = double size, 0.5 = half size)
            
        Returns:
            PIL Image of the re-rendered page with new font size
        """
        if not self.manager:
            return None
        
        try:
            self.base_font_scale = max(0.5, min(3.0, scale))  # Clamp between 0.5x and 3.0x
            page = self.manager.set_font_scale(self.base_font_scale)
            return page.render()
        except Exception as e:
            print(f"Error setting font size: {e}")
            return None
    
    def increase_font_size(self) -> Optional[Image.Image]:
        """
        Increase font size by one step and re-render.
        
        Returns:
            PIL Image of the re-rendered page
        """
        new_scale = self.base_font_scale + self.font_scale_step
        return self.set_font_size(new_scale)
    
    def decrease_font_size(self) -> Optional[Image.Image]:
        """
        Decrease font size by one step and re-render.
        
        Returns:
            PIL Image of the re-rendered page
        """
        new_scale = self.base_font_scale - self.font_scale_step
        return self.set_font_size(new_scale)
    
    def get_font_size(self) -> float:
        """
        Get the current font size scale.
        
        Returns:
            Current font scale factor
        """
        return self.base_font_scale
    
    def set_line_spacing(self, spacing: int) -> Optional[Image.Image]:
        """
        Set line spacing and re-render current page.
        
        Args:
            spacing: Line spacing in pixels
            
        Returns:
            PIL Image of the re-rendered page
        """
        if not self.manager:
            return None
        
        try:
            # Update page style
            self.page_style.line_spacing = max(0, spacing)
            
            # Need to recreate the manager with new page style
            current_pos = self.manager.current_position
            current_font_scale = self.base_font_scale
            self.manager.shutdown()
            
            self.manager = EreaderLayoutManager(
                blocks=self.blocks,
                page_size=self.page_size,
                document_id=self.document_id,
                buffer_size=self.buffer_size,
                page_style=self.page_style,
                bookmarks_dir=self.bookmarks_dir
            )
            
            # Restore position
            self.manager.current_position = current_pos
            
            # Restore font scale using the method (not direct assignment)
            if current_font_scale != 1.0:
                self.manager.set_font_scale(current_font_scale)
            
            page = self.manager.get_current_page()
            return page.render()
        except Exception as e:
            print(f"Error setting line spacing: {e}")
            return None
    
    def set_inter_block_spacing(self, spacing: int) -> Optional[Image.Image]:
        """
        Set spacing between blocks (paragraphs, headings, etc.) and re-render.
        
        Args:
            spacing: Inter-block spacing in pixels
            
        Returns:
            PIL Image of the re-rendered page
        """
        if not self.manager:
            return None
        
        try:
            # Update page style
            self.page_style.inter_block_spacing = max(0, spacing)
            
            # Need to recreate the manager with new page style
            current_pos = self.manager.current_position
            current_font_scale = self.base_font_scale
            self.manager.shutdown()
            
            self.manager = EreaderLayoutManager(
                blocks=self.blocks,
                page_size=self.page_size,
                document_id=self.document_id,
                buffer_size=self.buffer_size,
                page_style=self.page_style,
                bookmarks_dir=self.bookmarks_dir
            )
            
            # Restore position
            self.manager.current_position = current_pos
            
            # Restore font scale using the method (not direct assignment)
            if current_font_scale != 1.0:
                self.manager.set_font_scale(current_font_scale)
            
            page = self.manager.get_current_page()
            return page.render()
        except Exception as e:
            print(f"Error setting inter-block spacing: {e}")
            return None
    
    def get_position_info(self) -> Dict[str, Any]:
        """
        Get detailed information about the current position.
        
        Returns:
            Dictionary with position details including:
            - position: RenderingPosition details (chapter_index, block_index, word_index)
            - chapter: Current chapter info (title, level)
            - progress: Reading progress (0.0 to 1.0)
            - font_scale: Current font scale
            - book_title: Book title
            - book_author: Book author
        """
        if not self.manager:
            return {}
        
        try:
            info = self.manager.get_position_info()
            info['book_title'] = self.book_title
            info['book_author'] = self.book_author
            return info
        except Exception as e:
            print(f"Error getting position info: {e}")
            return {}
    
    def get_reading_progress(self) -> float:
        """
        Get reading progress as a percentage.
        
        Returns:
            Progress from 0.0 (beginning) to 1.0 (end)
        """
        if not self.manager:
            return 0.0
        
        return self.manager.get_reading_progress()
    
    def get_current_chapter_info(self) -> Optional[Dict[str, Any]]:
        """
        Get information about the current chapter.
        
        Returns:
            Dictionary with chapter info (title, level) or None
        """
        if not self.manager:
            return None
        
        try:
            chapter = self.manager.get_current_chapter()
            if chapter:
                return {
                    'title': chapter.title,
                    'level': chapter.level,
                    'block_index': chapter.block_index
                }
            return None
        except Exception as e:
            print(f"Error getting current chapter: {e}")
            return None
    
    def render_to_file(self, output_path: str) -> bool:
        """
        Save the current page to an image file.
        
        Args:
            output_path: Path where to save the image (e.g., "page.png")
            
        Returns:
            True if saved successfully, False otherwise
        """
        page_image = self.get_current_page()
        if page_image:
            try:
                page_image.save(output_path)
                return True
            except Exception as e:
                print(f"Error saving image: {e}")
                return False
        return False
    
    def get_book_info(self) -> Dict[str, Any]:
        """
        Get information about the loaded book.
        
        Returns:
            Dictionary with book information
        """
        return {
            'title': self.book_title,
            'author': self.book_author,
            'document_id': self.document_id,
            'total_blocks': len(self.blocks) if self.blocks else 0,
            'total_chapters': len(self.get_chapters()),
            'page_size': self.page_size,
            'font_scale': self.base_font_scale
        }
    
    def close(self):
        """
        Close the reader and save current position.
        Should be called when done with the reader.
        """
        if self.manager:
            self.manager.shutdown()
            self.manager = None
    
    def __enter__(self):
        """Context manager support."""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """Context manager cleanup."""
        self.close()
    
    def __del__(self):
        """Cleanup on deletion."""
        self.close()


# Convenience function
def create_ebook_reader(page_size: Tuple[int, int] = (800, 1000), **kwargs) -> EbookReader:
    """
    Create an ebook reader with sensible defaults.
    
    Args:
        page_size: Page dimensions (width, height) in pixels
        **kwargs: Additional arguments passed to EbookReader
        
    Returns:
        Configured EbookReader instance
    """
    return EbookReader(page_size=page_size, **kwargs)
