import tkinter as tk
from tkinter import scrolledtext, messagebox as mbox
import os
import re
import logging
import threading
import asyncio
import aiohttp
from PIL import Image
from fpdf import FPDF
from PyPDF2 import PdfReader, PdfWriter

# Configure logging for error and process tracking
logging.basicConfig(filename='app.log', level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

class URLProcessorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Enhanced E-Paper Grabber")
        self.root.geometry("800x600")
        self.setup_gui()
        self.temp_dir = "temp_images"
        os.makedirs(self.temp_dir, exist_ok=True)
        self.output_dir = os.getcwd()
        self.cleaned_links = []
        self.city_codes = ["ALP", "BLR", "CHE", "DEL", "IDK", "KNR", "KGD",
                           "CHN", "QLN", "KTM", "CLT", "MPM", "MBY", "PKD",
                           "PTA", "TVM", "TCR", "WYND", "BAH", "KWT", "OMAN", "QTR"]

    def setup_gui(self):
        # Setup GUI frames
        input_frame = tk.Frame(self.root)
        input_frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
        output_frame = tk.Frame(self.root)
        output_frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
        button_frame = tk.Frame(self.root)
        button_frame.pack(pady=10, side=tk.BOTTOM, fill=tk.X)

        # Input and output text areas
        self.url_input_area = scrolledtext.ScrolledText(input_frame, wrap=tk.WORD, height=10, width=70, bg='seashell', fg='blue')
        self.url_input_area.pack(fill=tk.BOTH, expand=True)
        self.url_output_area = scrolledtext.ScrolledText(output_frame, wrap=tk.WORD, height=10, width=70, bg='seashell', fg='blue')
        self.url_output_area.pack(fill=tk.BOTH, expand=True)
        self.url_output_area.configure(state=tk.DISABLED)

        # Status label
        self.status_label = tk.Label(self.root, text="")
        self.status_label.pack(pady=10)

        # Buttons
        self.create_buttons(button_frame)

        # Right-click menu
        self.create_right_click_menu()

    def create_buttons(self, frame):
        buttons = [
            ("Download", self.start_process, 'green'),
            ("Clear", self.clear_all, 'red'),
            ("Import from Clipboard", self.import_from_clipboard, 'blue'),
            ("Close", self.root.destroy, 'black')
        ]
        for (text, command, color) in buttons:
            tk.Button(frame, text=text, command=command, bg=color, fg='white').pack(pady=5, side=tk.LEFT, padx=10)

    def create_right_click_menu(self):
        self.right_click_menu = tk.Menu(self.root, tearoff=0)
        self.right_click_menu.add_command(label="Paste URL", command=self.paste_url)
        self.url_input_area.bind("<Button-3>", self.show_right_click_menu)

    def start_process(self):
        self.update_status("Starting process...")
        threading.Thread(target=self.process_and_create_pdf).start()

    def process_and_create_pdf(self):
        try:
            self.process_url()
            asyncio.run(self.download_images())
            self.create_pdf()
        except Exception as e:
            self.log_error(f"Error in process: {str(e)}")
        finally:
            self.update_status("PDF generated and saved.")

    def process_url(self):
        input_text = self.url_input_area.get("1.0", tk.END).strip()
        self.cleaned_links = sorted(
            filter(None, (self.clean_url(line.strip()) for line in input_text.splitlines())),
            key=self.extract_page_and_image
        )
        self.update_url_output_area('\n'.join(self.cleaned_links))
        self.update_status(f"Processed Links: {len(self.cleaned_links)}")

    def clean_url(self, url):
        if not url.startswith(("http://", "https://")):
            url = "https://" + url
        if "https://pages-epaper.manoramaonline.com/mmr/" in url:
            return re.sub(r"_tn|_mr", "", url)
        return None

    def extract_page_and_image(self, url):
        page_number, image_number = (int(m.group(1)) if m else 0 for m in
                                     (re.search(r"(\d+)_", url), re.search(r"_(\d+)(?:\.jpg|\.png)", url)))
        return page_number, image_number

    async def download_images(self):
        if not self.cleaned_links:
            self.update_status("Please process URLs first.")
            return
        self.clear_temp_dir()
        async with aiohttp.ClientSession() as session:
            tasks = (self.download_image(session, link, i+1) for i, link in enumerate(self.cleaned_links))
            await asyncio.gather(*tasks)
        self.update_status("All images downloaded.")

    async def download_image(self, session, url, index):
        self.update_status(f"Downloading image {index}/{len(self.cleaned_links)}: {url}")
        try:
            async with session.get(url) as response:
                response.raise_for_status()
                ext = os.path.splitext(url)[1] or '.jpg'
                img_path = os.path.join(self.temp_dir, f"image_{index}{ext}")
                with open(img_path, 'wb') as f:
                    f.write(await response.read())
        except Exception as e:
            self.log_error(f"Error downloading image {index}: {str(e)}")
            mbox.showerror("Download Error", f"Failed to download image {index}")

    def create_pdf(self):
        if not os.listdir(self.temp_dir):
            self.update_status("No images to include in the PDF.")
            return
        pdf_path = os.path.join(self.output_dir, self.generate_filename(self.cleaned_links[0]))
        self.create_pdf_async(pdf_path)

    def create_pdf_async(self, pdf_path):
        try:
            self.create_pdf_with_images(pdf_path)
            self.activate_fast_web_view(pdf_path)
            self.clear_temp_dir()
            self.update_status(f"PDF created and saved at: {pdf_path}")
        except Exception as e:
            self.log_error(f"Error creating PDF: {str(e)}")

    def create_pdf_with_images(self, pdf_path):
        pdf = FPDF()
        for img_file in sorted(os.listdir(self.temp_dir), key=lambda x: int(re.search(r'image_(\d+)', x).group(1))):
            try:
                img_path = os.path.join(self.temp_dir, img_file)
                pdf.add_page()
                self.add_image_to_pdf(pdf, img_path)
            except Exception as e:
                self.log_error(f"Error adding image to PDF: {str(e)}")
        pdf.output(pdf_path, 'F')

    def add_image_to_pdf(self, pdf, img_path):
        pdf_width, pdf_height, margin = 210, 297, 10  # A4 dimensions
        max_width, max_height = pdf_width - 2 * margin, pdf_height - 2 * margin

        with Image.open(img_path) as img:
            img_width, img_height = img.size
            img_ratio = img_width / img_height
            if img_ratio > max_width / max_height:
                scaled_width, scaled_height = max_width, max_width / img_ratio
            else:
                scaled_width, scaled_height = max_height * img_ratio, max_height

            x, y = (pdf_width - scaled_width) / 2, (pdf_height - scaled_height) / 2
            pdf.image(img_path, x, y, scaled_width, scaled_height)

            border_margin = 5
            pdf.set_line_width(0.5)
            pdf.rect(x - border_margin, y - border_margin, scaled_width + 2 * border_margin, scaled_height + 2 * border_margin)

    def activate_fast_web_view(self, pdf_path):
        with open(pdf_path, "rb") as f:
            reader, writer = PdfReader(f), PdfWriter()
            for page in reader.pages:
                writer.add_page(page)
            writer.add_metadata({'/FastWebView': 'True'})
            with open(pdf_path, "wb") as f_out:
                writer.write(f_out)

    def generate_filename(self, url):
        city_code = next((code for code in self.city_codes if code.lower() in url.lower()), "")
        base_name = "epaper"  # Set base name directly to "epaper" as intended.

        date_match = re.search(r'(\d{4})[/.-](\d{2})[/.-](\d{2})', url)
        if date_match:
            date = f"{date_match.group(3)}.{date_match.group(2)}.{date_match.group(1)}"
        else:
            date = ""

        filename = f"MM_{city_code}_{base_name}_{date}.pdf".replace('--', '-')
        return filename[:255]  # Ensure filename length constraint

    def update_status(self, message):
        self.status_label.configure(text=message)
        self.root.update_idletasks()

    def log_error(self, message):
        logging.error(message)
        self.update_status(message)

    def clear_all(self):
        self.url_input_area.delete("1.0", tk.END)
        self.update_url_output_area("")
        self.status_label.configure(text="")
        self.clear_temp_dir()

    def update_url_output_area(self, text):
        self.url_output_area.configure(state=tk.NORMAL)
        self.url_output_area.delete("1.0", tk.END)
        self.url_output_area.insert("1.0", text)
        self.url_output_area.configure(state=tk.DISABLED)

    def import_from_clipboard(self):
        try:
            self.url_input_area.insert(tk.END, self.root.clipboard_get())
        except tk.TclError:
            self.update_status("No text found in clipboard.")

    def show_right_click_menu(self, event):
        self.right_click_menu.post(event.x_root, event.y_root)

    def paste_url(self):
        try:
            self.url_input_area.insert(tk.END, self.root.clipboard_get())
        except tk.TclError:
            self.update_status("No text found in clipboard.")

    def clear_temp_dir(self):
        try:
            for file in os.listdir(self.temp_dir):
                os.remove(os.path.join(self.temp_dir, file))
        except Exception as e:
            self.log_error(f"Error clearing temp files: {str(e)}")

# Run the application
if __name__ == "__main__":
    root = tk.Tk()
    app = URLProcessorApp(root)
    root.mainloop()