#!/usr/bin/env python3
"""
Basic EPUB Reader with Pagination using pyWebLayout

This reader loads EPUB files and displays them with page-by-page navigation
using the pyWebLayout system. It follows the proper architecture where:
- EPUBReader loads EPUB files into Document/Chapter objects
- Page renders those abstract objects into visual pages
- The UI handles pagination and navigation
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
from typing import List, Optional
from PIL import Image, ImageTk

from pyWebLayout.io.readers.epub_reader import EPUBReader
from pyWebLayout.concrete.page import Page
from pyWebLayout.style.fonts import Font
from pyWebLayout.abstract.document import Document, Chapter, Book
from pyWebLayout.io.readers.html_extraction import parse_html_string


class EPUBReaderApp:
    """Main EPUB reader application using Tkinter"""
    
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("pyWebLayout EPUB Reader")
        self.root.geometry("900x700")
        
        # Application state
        self.current_epub: Optional[EPUBReader] = None
        self.current_document: Optional[Document] = None
        self.rendered_pages: List[Page] = []
        self.current_page_index = 0
        
        # Page settings
        self.page_width = 700
        self.page_height = 550
        self.blocks_per_page = 3  # Fewer blocks per page for better readability
        
        self.setup_ui()
        
    def setup_ui(self):
        """Setup the user interface"""
        # Create main frame
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # Top control frame
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(fill=tk.X, pady=(0, 10))
        
        # File operations
        self.open_btn = ttk.Button(control_frame, text="Open EPUB", command=self.open_epub)
        self.open_btn.pack(side=tk.LEFT, padx=(0, 10))
        
        # Book info
        self.book_info_label = ttk.Label(control_frame, text="No book loaded")
        self.book_info_label.pack(side=tk.LEFT, expand=True)
        
        # Navigation frame
        nav_frame = ttk.Frame(main_frame)
        nav_frame.pack(fill=tk.X, pady=(0, 10))
        
        # Navigation buttons
        self.prev_btn = ttk.Button(nav_frame, text="◀ Previous", command=self.previous_page, state=tk.DISABLED)
        self.prev_btn.pack(side=tk.LEFT, padx=(0, 10))
        
        self.next_btn = ttk.Button(nav_frame, text="Next ▶", command=self.next_page, state=tk.DISABLED)
        self.next_btn.pack(side=tk.LEFT, padx=(0, 10))
        
        # Page info
        self.page_info_label = ttk.Label(nav_frame, text="Page 0 of 0")
        self.page_info_label.pack(side=tk.LEFT, padx=(20, 0))
        
        # Chapter selector
        ttk.Label(nav_frame, text="Chapter:").pack(side=tk.LEFT, padx=(20, 5))
        self.chapter_var = tk.StringVar()
        self.chapter_combo = ttk.Combobox(nav_frame, textvariable=self.chapter_var, state="readonly", width=30)
        self.chapter_combo.pack(side=tk.LEFT, padx=(0, 10))
        self.chapter_combo.bind('<<ComboboxSelected>>', self.on_chapter_selected)
        
        # Content frame with canvas
        content_frame = ttk.Frame(main_frame)
        content_frame.pack(fill=tk.BOTH, expand=True)
        
        # Create canvas for page display
        self.canvas = tk.Canvas(content_frame, bg='white', width=self.page_width, height=self.page_height)
        self.canvas.pack(expand=True)
        
        # Status bar
        self.status_var = tk.StringVar(value="Ready - Open an EPUB file to begin")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)
        status_bar.pack(fill=tk.X, pady=(10, 0))
        
        # Bind keyboard shortcuts
        self.root.bind('<Key-Left>', lambda e: self.previous_page())
        self.root.bind('<Key-Right>', lambda e: self.next_page())
        self.root.bind('<Key-space>', lambda e: self.next_page())
        self.root.focus_set()  # Allow keyboard input
        
    def open_epub(self):
        """Open and load an EPUB file"""
        file_path = filedialog.askopenfilename(
            title="Open EPUB File",
            filetypes=[("EPUB files", "*.epub"), ("All files", "*.*")]
        )
        
        if file_path:
            self.load_epub(file_path)
    
    def load_epub(self, file_path: str):
        """Load an EPUB file and prepare for display"""
        try:
            self.status_var.set("Loading EPUB file...")
            self.root.update()
            
            # Load the EPUB using the EPUBReader
            self.current_epub = EPUBReader(file_path)
            
            # Get the document structure from the EPUB
            self.current_document = self.current_epub.read()
            
            # Update book info
            if isinstance(self.current_document, Book):
                title = self.current_document.get_title() or "Unknown Title"
                author = self.current_document.get_author() or "Unknown Author"
                self.book_info_label.config(text=f"{title} by {author}")
            else:
                title = getattr(self.current_document, 'title', 'Unknown Title')
                self.book_info_label.config(text=title)
            
            # Populate chapter list
            self.populate_chapter_list()
            
            # Create pages from the document
            self.create_pages_from_document()
            
            # Show first page
            self.current_page_index = 0
            self.display_current_page()
            self.update_navigation()
            
            self.status_var.set(f"Loaded: {os.path.basename(file_path)} - {len(self.rendered_pages)} pages")
            
        except Exception as e:
            self.status_var.set(f"Error loading EPUB: {str(e)}")
            messagebox.showerror("Error", f"Failed to load EPUB file:\n{str(e)}")
            print(f"Detailed error: {e}")
            import traceback
            traceback.print_exc()
    
    def populate_chapter_list(self):
        """Populate the chapter selection dropdown"""
        if not self.current_document:
            return
            
        chapters = []
        
        # Check if it's a Book with chapters
        if isinstance(self.current_document, Book) and self.current_document.chapters:
            for i, chapter in enumerate(self.current_document.chapters):
                chapter_title = chapter.title or f"Chapter {i+1}"
                chapters.append(chapter_title)
        else:
            # Fallback: add a single "Document" entry
            chapters.append("Document")
        
        self.chapter_combo['values'] = chapters
        if chapters:
            self.chapter_combo.set(chapters[0])
    
    def create_pages_from_document(self):
        """Create pages using the new external pagination system with block handlers"""
        if not self.current_document:
            return
            
        self.rendered_pages.clear()
        
        try:
            # Get all blocks from the document
            all_blocks = []
            
            if isinstance(self.current_document, Book) and self.current_document.chapters:
                # Process chapters
                for chapter in self.current_document.chapters:
                    all_blocks.extend(chapter.blocks)
            else:
                # Process document blocks directly
                all_blocks = self.current_document.blocks
            
            # If no blocks found, try to create some from EPUB content
            if not all_blocks:
                all_blocks = self.create_blocks_from_epub_content()
            
            # Use the new external pagination system
            remaining_blocks = all_blocks
            
            while remaining_blocks:
                # Create a new page
                current_page = Page(size=(self.page_width, self.page_height))
                
                # Fill the page using the external pagination system
                next_index, remainder_blocks = current_page.fill_with_blocks(remaining_blocks)
                
                # Add the page if it has content
                if current_page._children:
                    self.rendered_pages.append(current_page)
                
                # Update remaining blocks for next iteration
                if remainder_blocks:
                    # We have remainder blocks (partial content)
                    remaining_blocks = remainder_blocks
                elif next_index < len(remaining_blocks):
                    # We stopped at a specific index
                    remaining_blocks = remaining_blocks[next_index:]
                else:
                    # All blocks processed
                    remaining_blocks = []
                
                # Safety check to prevent infinite loops
                if not current_page._children and remaining_blocks:
                    print(f"Warning: Could not fit any content on page, skipping {len(remaining_blocks)} blocks")
                    break
            
            # If no pages were created, create a default one
            if not self.rendered_pages:
                self.create_default_page()
                
        except Exception as e:
            print(f"Error creating pages: {e}")
            import traceback
            traceback.print_exc()
            self.create_default_page()
    
    
    def create_blocks_from_epub_content(self):
        """Create blocks from raw EPUB content when document parsing fails"""
        blocks = []
        
        try:
            # Get HTML content from EPUB spine items
            spine_items = self.current_epub.spine[:3]  # Limit to first 3 items
            
            for item_id in spine_items:
                try:
                    # Get the manifest item
                    if item_id in self.current_epub.manifest:
                        item = self.current_epub.manifest[item_id]
                        file_path = item['path']
                        
                        # Read the HTML content
                        if os.path.exists(file_path):
                            with open(file_path, 'r', encoding='utf-8') as f:
                                content = f.read()
                            
                            # Parse HTML content into blocks
                            html_blocks = parse_html_string(content)
                            blocks.extend(html_blocks[:5])  # Limit blocks per item
                except Exception as e:
                    print(f"Error processing spine item {item_id}: {e}")
                    continue
                    
        except Exception as e:
            print(f"Error getting EPUB content: {e}")
        
        return blocks
    
    def create_default_page(self):
        """Create a default page when content loading fails"""
        page = Page(size=(self.page_width, self.page_height))
        
        # Add some default content
        from pyWebLayout.concrete.text import Text
        default_font = Font()
        
        if self.current_document:
            title = getattr(self.current_document, 'title', None)
            if title:
                page.add_child(Text(f"Book: {title}", default_font))
            page.add_child(Text("Content is loading...", default_font))
        else:
            page.add_child(Text("EPUB content loaded", default_font))
            page.add_child(Text("Use arrow keys or buttons to navigate", default_font))
        
        self.rendered_pages = [page]
    
    def display_current_page(self):
        """Display the current page on the canvas"""
        if not self.rendered_pages or self.current_page_index >= len(self.rendered_pages):
            return
            
        try:
            # Clear the canvas
            self.canvas.delete("all")
            
            # Get the current page
            page = self.rendered_pages[self.current_page_index]
            
            # Render the page
            page_image = page.render()
            
            # Convert to PhotoImage
            self.photo = ImageTk.PhotoImage(page_image)
            
            # Calculate position to center the page
            canvas_width = self.canvas.winfo_width()
            canvas_height = self.canvas.winfo_height()
            
            if canvas_width > 1 and canvas_height > 1:  # Canvas is properly sized
                x_pos = max(0, (canvas_width - page_image.width) // 2)
                y_pos = max(0, (canvas_height - page_image.height) // 2)
            else:
                x_pos, y_pos = 0, 0
            
            # Display the page
            self.canvas.create_image(x_pos, y_pos, anchor=tk.NW, image=self.photo)
            
        except Exception as e:
            # Display error message
            self.canvas.delete("all")
            self.canvas.create_text(
                self.page_width // 2, self.page_height // 2,
                text=f"Error displaying page: {str(e)}",
                fill="red", font=("Arial", 12)
            )
            print(f"Display error: {e}")
    
    def previous_page(self):
        """Navigate to the previous page"""
        if self.current_page_index > 0:
            self.current_page_index -= 1
            self.display_current_page()
            self.update_navigation()
    
    def next_page(self):
        """Navigate to the next page"""
        if self.current_page_index < len(self.rendered_pages) - 1:
            self.current_page_index += 1
            self.display_current_page()
            self.update_navigation()
    
    def update_navigation(self):
        """Update navigation button states and page info"""
        if not self.rendered_pages:
            self.prev_btn.config(state=tk.DISABLED)
            self.next_btn.config(state=tk.DISABLED)
            self.page_info_label.config(text="Page 0 of 0")
            return
        
        # Update button states
        self.prev_btn.config(state=tk.NORMAL if self.current_page_index > 0 else tk.DISABLED)
        self.next_btn.config(state=tk.NORMAL if self.current_page_index < len(self.rendered_pages) - 1 else tk.DISABLED)
        
        # Update page info
        page_num = self.current_page_index + 1
        total_pages = len(self.rendered_pages)
        self.page_info_label.config(text=f"Page {page_num} of {total_pages}")
    
    def on_chapter_selected(self, event=None):
        """Handle chapter selection"""
        if not self.current_document or not self.rendered_pages:
            return
            
        selected_chapter = self.chapter_var.get()
        
        # For now, just go to the first page
        # In a more sophisticated implementation, we'd track chapter start pages
        self.current_page_index = 0
        self.display_current_page()
        self.update_navigation()
        
        self.status_var.set(f"Viewing: {selected_chapter}")
    
    def run(self):
        """Start the EPUB reader application"""
        # Make canvas responsive
        def on_configure(event):
            # Redisplay current page when canvas is resized
            if hasattr(self, 'photo'):
                self.root.after_idle(self.display_current_page)
        
        self.canvas.bind('<Configure>', on_configure)
        
        # Start the main loop
        self.root.mainloop()


def main():
    """Main function to run the EPUB reader"""
    print("Starting pyWebLayout EPUB Reader...")
    
    try:
        app = EPUBReaderApp()
        app.run()
    except Exception as e:
        print(f"Error starting EPUB reader: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    main()
