import asyncio
import aiohttp
import os
import re
import logging
import tkinter as tk
from tkinter import scrolledtext, ttk, filedialog, messagebox as mbox
from PIL import Image
from fpdf import FPDF
from concurrent.futures import ThreadPoolExecutor

# Configure logging
logging.basicConfig(filename='app.log', level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s')

class URLProcessorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Enhanced E-Paper Grabber")
        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.executor = ThreadPoolExecutor(max_workers=5)

    def setup_gui(self):
        self.input_frame = tk.Frame(self.root)
        self.input_frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)

        self.output_frame = tk.Frame(self.root)
        self.output_frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)

        self.button_frame = tk.Frame(self.root)
        self.button_frame.pack(pady=10, side=tk.BOTTOM, fill=tk.X)

        self.url_input_area = scrolledtext.ScrolledText(self.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(self.output_frame, wrap=tk.WORD, height=10, width=70, bg='seashell', fg='blue')
        self.url_output_area.configure(state=tk.DISABLED)
        self.url_output_area.pack(fill=tk.BOTH, expand=True)

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

        self.progress = ttk.Progressbar(self.root, orient=tk.HORIZONTAL, length=300, mode='indeterminate')
        self.progress.pack(pady=10)

        self.process_button = tk.Button(self.button_frame, text="Download", command=self.start_process, bg='Green', fg='white')
        self.process_button.pack(pady=5, side=tk.RIGHT, padx=10)

        self.clear_button = tk.Button(self.button_frame, text="Clear", command=self.clear_all, bg='red', fg='white')
        self.clear_button.pack(pady=5, side=tk.RIGHT, padx=10)

        self.import_from_clipboard_button = tk.Button(self.button_frame, text="Import from Clipboard", command=self.import_from_clipboard, bg='blue', fg='white')
        self.import_from_clipboard_button.pack(pady=5, side=tk.LEFT, padx=10)

        self.choose_output_button = tk.Button(self.button_frame, text="Choose Output Directory", command=self.choose_output_directory, bg='orangered', fg='white')
        self.choose_output_button.pack(pady=5, side=tk.LEFT, padx=10)

        self.close_button = tk.Button(self.button_frame, text="Close", command=self.close_app, bg='darkorange', fg='white')
        self.close_button.pack(pady=5, side=tk.RIGHT, padx=10)

        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...")
        self.progress.start()
        asyncio.run(self.process_urls_and_create_pdf())

    async def process_urls_and_create_pdf(self):
        try:
            self.process_url()
            await self.download_images()
            self.create_pdf()
        finally:
            self.progress.stop()

    def process_url(self):
        input_text = self.url_input_area.get("1.0", tk.END).strip()
        lines = input_text.splitlines()
        cleaned_lines = set()

        required_substring = "https://pages-epaper.manoramaonline.com/mmr/"
        substrings_to_remove = ["_tn", "_mr"]

        for line in lines:
            line = line.strip()
            if line:
                if not line.startswith("http://") and not line.startswith("https://"):
                    line = "https://" + line
                if required_substring in line:
                    for substring in substrings_to_remove:
                        line = line.replace(substring, "")
                    cleaned_lines.add(line)

        cleaned_lines = list(cleaned_lines)
        cleaned_lines.sort(key=lambda url: self.extract_page_and_image(url))

        self.url_output_area.configure(state=tk.NORMAL)
        self.url_output_area.delete("1.0", tk.END)
        self.url_output_area.insert("1.0", '\n'.join(cleaned_lines))
        self.url_output_area.configure(state=tk.DISABLED)
        self.update_status(f"Processed Links: {len(cleaned_lines)}")
        self.cleaned_links = cleaned_lines

    def extract_page_and_image(self, url):
        page_match = re.search(r"(\d+)_", url)
        image_match = re.search(r"_(\d+)(?:\.jpg|\.png)", url)

        page_number = int(page_match.group(1)) if page_match else 0
        image_number = int(image_match.group(1)) if image_match else 0

        return (page_number, image_number)

    async def download_images(self):
        self.clear_temp_dir()
        async with aiohttp.ClientSession() as session:
            tasks = [self.download_image(session, link, index + 1) for index, 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:
                    while True:
                        chunk = await response.content.read(1024)
                        if not chunk:
                            break
                        f.write(chunk)
        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):
        self.update_status("Creating PDF...")

        if not os.listdir(self.temp_dir):
            self.update_status("No images to include in the PDF.")
            return

        pdf_name = self.generate_filename(self.cleaned_links[0]) if self.cleaned_links else "output.pdf"
        pdf_path = os.path.join(self.output_dir, pdf_name)

        self.root.after(0, self.executor.submit, self.create_pdf_async, pdf_path)

    def create_pdf_async(self, pdf_path):
        try:
            pdf = FPDF()
            image_files = sorted(os.listdir(self.temp_dir))
            for img_file in image_files:
                img_path = os.path.join(self.temp_dir, img_file)
                self.add_image_to_pdf(pdf, img_path)
            pdf.output(pdf_path, 'F')
            self.update_status(f"PDF created at: {pdf_path}")
            self.clear_temp_dir()
        except Exception as e:
            self.log_error(f"Error creating PDF: {str(e)}")

    def add_image_to_pdf(self, pdf, img_path):
        try:
            pdf.add_page()
            pdf_width, pdf_height = 210, 297  # A4 in mm
            margin = 10
            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
                page_ratio = max_width / max_height

                if img_ratio > page_ratio:
                    scaled_width = max_width
                    scaled_height = max_width / img_ratio
                else:
                    scaled_height = max_height
                    scaled_width = max_height * img_ratio

                x = (pdf_width - scaled_width) / 2
                y = (pdf_height - scaled_height) / 2

                pdf.image(img_path, x, y, scaled_width, scaled_height)

                # Adding a border
                pdf.set_line_width(0.1)
                pdf.rect(x, y, scaled_width, scaled_height)
        except Exception as e:
            self.log_error(f"Error adding image to PDF: {str(e)}")

    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.url_output_area.configure(state=tk.NORMAL)
        self.url_output_area.delete("1.0", tk.END)
        self.url_output_area.configure(state=tk.DISABLED)
        self.status_label.configure(text="")
        self.clear_temp_dir()

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

    def choose_output_directory(self):
        directory = filedialog.askdirectory()
        if directory:
            self.output_dir = directory
            self.update_status(f"Output directory set to: {self.output_dir}")

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

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

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

    def generate_filename(self, url):
        base_name = "epaper"
        main_name_match = re.search(r"([^/]+)(?=\.[^/]+$)", url)
        main_name = main_name_match.group(1) if main_name_match else ""
        main_name = re.sub(r'[^a-zA-Z0-9]', '_', main_name).lower()

        city_code = ""
        for code in ["ALP", "BLR", "CHE", "DEL", "IDK", "KNR", "KGD", "CHN", "QLN", "KTM", "CLT", "MPM", "MBY", "PKD", "PTA", "TVM", "TCR", "WYND", "BAH", "KWT", "OMAN", "QTR"]:
            if code.lower() in url.lower():
                city_code = code
                break

        new_name = f"{city_code}_{base_name.replace('-', '_')}" if city_code else base_name.replace('-', '_')

        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)}"
            new_name += f"_{date}"

        final_filename = f"MM_{new_name}.pdf"

        max_length = 255
        if len(final_filename) > max_length:
            final_filename = final_filename[:max_length]

        return final_filename

    def close_app(self):
        self.executor.shutdown(wait=False)
        self.root.destroy()

if __name__ == '__main__':
    root = tk.Tk()
    app = URLProcessorApp(root)
    root.mainloop()