# pip install markdown
# pip install tkhtmlview
# pip install Pillow

import tkinter as tk
from tkinter import filedialog, simpledialog
import markdown
from tkhtmlview import HTMLText
import argparse
import os
import re
from PIL import Image
import time
import shutil

# mdview.iniファイルの読み書きを行うシンプルな関数
def read_ini_file(filename):
    """INIファイルから設定を読み込む"""
    settings = {}
    if not os.path.exists(filename):
        return settings

    with open(filename, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            if '=' in line:
                key, value = line.split('=', 1)
                settings[key.strip()] = value.strip()
    return settings

def write_ini_file(filename, settings):
    """設定をINIファイルに書き込む"""
    with open(filename, 'w', encoding='utf-8') as f:
        for key, value in settings.items():
            f.write(f"{key}={value}\n")

class MarkdownViewerApp:
    def __init__(self, master, md_filename=None, image_width=None, font_size=None):
        self.master = master
        
        self.md_filename = md_filename
        self.ini_filename = "mdview.ini"
        
        self.temp_image_dir = os.path.join(os.getcwd(), ".mdview_temp")
        os.makedirs(self.temp_image_dir, exist_ok=True)
        
        self.load_settings()
        
        self.image_width = image_width if image_width is not None else self.image_width
        self.font_size = font_size if font_size is not None else self.font_size
        
        master.title("Markdown Viewer")
        if hasattr(self, 'geometry'):
            master.geometry(self.geometry)
        else:
            master.geometry("800x600")

        self.html_widget = HTMLText(master)
        self.html_widget.pack(fill="both", expand=True, padx=10, pady=10)

        self.create_menu()
        
        if self.md_filename:
            self.load_markdown_file(self.md_filename)
        else:
            html_text = f'<div style="font-size: {self.font_size}px;"><p>コマンドライン引数でファイル名を指定するか、メニューからファイルを開いてください。</p></div>'
            self.html_widget.set_html(html_text)

        self.master.protocol("WM_DELETE_WINDOW", self.on_closing)

    def load_settings(self):
        settings = read_ini_file(self.ini_filename)
        self.geometry = settings.get("geometry")
        self.last_directory = settings.get("last_directory", os.getcwd())
        self.image_width = int(settings.get("image_width", "800"))
        self.font_size = int(settings.get("font_size", "12"))

    def on_closing(self):
        settings = {
            "geometry": self.master.winfo_geometry(),
            "last_directory": os.path.dirname(self.md_filename) if self.md_filename and os.path.dirname(self.md_filename) else self.last_directory,
            "image_width": str(self.image_width),
            "font_size": str(self.font_size)
        }
        write_ini_file(self.ini_filename, settings)
        
        if os.path.exists(self.temp_image_dir):
            shutil.rmtree(self.temp_image_dir, ignore_errors=True)
        
        self.master.destroy()
        self.master.quit()

    def create_menu(self):
        menubar = tk.Menu(self.master)
        self.master.config(menu=menubar)

        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="ファイル", menu=file_menu)
        file_menu.add_command(label="開く...", command=self.open_file)
        file_menu.add_separator()
        file_menu.add_command(label="更新", command=self.reload_file)
        file_menu.add_separator()
        file_menu.add_command(label="終了", command=self.on_closing)

        settings_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="設定", menu=settings_menu)
        settings_menu.add_command(label="画像幅を設定...", command=self.open_image_width_dialog)
        settings_menu.add_command(label="フォントサイズを設定...", command=self.open_font_size_dialog)

    def reload_file(self):
        if self.md_filename:
            self.load_markdown_file(self.md_filename)
        else:
            print("ファイルが開かれていません。")

    def open_image_width_dialog(self):
        new_width = simpledialog.askinteger(
            "設定", "画像の表示幅を入力してください (ピクセル)",
            initialvalue=self.image_width,
            parent=self.master
        )
        if new_width is not None and new_width > 0:
            self.image_width = new_width
            if self.md_filename:
                self.load_markdown_file(self.md_filename)

    def open_font_size_dialog(self):
        new_size = simpledialog.askinteger(
            "設定", "基準フォントサイズを入力してください (ピクセル)",
            initialvalue=self.font_size,
            parent=self.master,
            minvalue=8,
            maxvalue=32
        )
        if new_size is not None:
            self.font_size = new_size
            if self.md_filename:
                self.load_markdown_file(self.md_filename)
            else:
                html_text = f'<div style="font-size: {self.font_size}px;"><p>コマンドライン引数でファイル名を指定するか、メニューからファイルを開いてください。</p></div>'
                self.html_widget.set_html(html_text)

    def load_markdown_file(self, file_path):
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                markdown_text = f.read()

            markdown_text = self.process_images(markdown_text, os.path.dirname(file_path))
            
            html_text = markdown.markdown(markdown_text, extensions=['fenced_code', 'tables'])
            
            # 見出しタグにフォントサイズを直接適用
            html_text = re.sub(r'<h(\d)>(.*?)</h\1>', self.replace_heading_size, html_text)
            
            # HTMLテキスト全体を<div>で囲み、フォントサイズを適用する
            html_with_style = f'<div style="font-size: {self.font_size}px;">{html_text}</div>'
            self.html_widget.set_html(html_with_style)

            self.md_filename = file_path
            self.last_directory = os.path.dirname(file_path)

        except FileNotFoundError:
            html_text = f'<div style="font-size: {self.font_size}px;"><p>エラー: ファイル "{file_path}" が見つかりません。</p></div>'
            self.html_widget.set_html(html_text)
        except Exception as e:
            html_text = f'<div style="font-size: {self.font_size}px;"><p>エラーが発生しました: {e}</p></div>'
            self.html_widget.set_html(html_text)
            
    def replace_heading_size(self, match):
        """見出しタグのサイズを基準フォントサイズに基づいて計算し、スタイルを適用する"""
        level = int(match.group(1))
        content = match.group(2)
        
        # h1, h2, h3... に応じてフォントサイズを計算
        # 例: h1=基準*2, h2=基準*1.5, h3=基準*1.2
        size_factor = {1: 2.0, 2: 1.5, 3: 1.2, 4: 1.1, 5: 1.0, 6: 0.9}
        new_size = int(self.font_size * size_factor.get(level, 1.0))
        
        return f'<h{level} style="font-size: {new_size}px;">{content}</h{level}>'

    def process_images(self, markdown_text, base_dir):
        def replace_image_path(match):
            alt_text = match.group(1)
            original_path = match.group(2)
            
            img_path_abs = os.path.join(base_dir, original_path)
            
            if not os.path.exists(img_path_abs):
                return match.group(0)
            
            try:
                img = Image.open(img_path_abs)
                
                if img.width > self.image_width:
                    ratio = self.image_width / img.width
                    new_height = int(img.height * ratio)
                    resized_img = img.resize((self.image_width, new_height), Image.Resampling.LANCZOS)
                    
                    timestamp = int(time.time() * 1000)
                    temp_filename = f"resized_{timestamp}_{os.path.basename(original_path)}"
                    temp_path = os.path.join(self.temp_image_dir, temp_filename)
                    resized_img.save(temp_path)
                    
                    return f'![{alt_text}]({temp_path})'
                
                return match.group(0)
            
            except Exception as e:
                return match.group(0)

        markdown_text = re.sub(r'!\[(.*?)\]\((.*?)\)', replace_image_path, markdown_text)
        
        return markdown_text

    def open_file(self):
        file_path = filedialog.askopenfilename(
            defaultextension=".md",
            filetypes=[("Markdown files", "*.md"), ("All files", "*.*")],
            initialdir=self.last_directory
        )
        if file_path:
            self.load_markdown_file(file_path)

def parse_arguments():
    parser = argparse.ArgumentParser(description="Markdown Viewer")
    parser.add_argument('file', nargs='?', help='表示するMarkdownファイル名')
    parser.add_argument('-w', '--width', type=int, help='画像の表示幅 (ピクセル)')
    parser.add_argument('-f', '--font', type=int, help='基準フォントサイズ (ピクセル)')
    return parser.parse_args()

if __name__ == "__main__":
    args = parse_arguments()
    root = tk.Tk()
    app = MarkdownViewerApp(root, md_filename=args.file, image_width=args.width, font_size=args.font)
    root.mainloop()