# -*- coding: utf-8 -*-
import argparse
import base64
import re
import sys
import os
import time
from pathlib import Path
from typing import Dict, List, Optional

# --- ライブラリのインポートチェック ---
try:
    import win32com.client  # pywin32 (PPTX -> PDF画像化用)
except ImportError:
    print("Import error: win32com. Please install it with: pip install pywin32")
    input("\nPress ENTER to terminate>>\n")
    sys.exit(1)

try:
    import fitz  # PyMuPDF (PDF -> PNG用)
except ImportError:
    print("Import error: fitz. Please install it with: pip install pymupdf")
    input("\nPress ENTER to terminate>>\n")
    sys.exit(1)

try:
    from pptx import Presentation  # テキスト抽出用
    from lxml import etree         # 数式解析用
except ImportError:
    print("Import error: python-pptx or lxml. Please install: pip install python-pptx lxml")
    input("\nPress ENTER to terminate>>\n")
    sys.exit(1)

from openai import OpenAI
try:
    import google.generativeai as genai
except ImportError:
    print("Geminiを利用する場合は 'pip install google-generativeai' が必要です。")
    input("\nPress ENTER to terminate>>\n")
    sys.exit(1)

pause = 0


# =========================================================
# 1. Advanced Math Extraction Logic (Provided Code Integrated)
# =========================================================

# 名前空間の定義
NAMESPACES = {
    'p': 'http://schemas.openxmlformats.org/presentationml/2006/main',
    'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
    'm': 'http://schemas.openxmlformats.org/officeDocument/2006/math',
    'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
    'a14': 'http://schemas.microsoft.com/office/drawing/2010/main'
}

# Unicode 数学記号をASCII/LaTeXに変換する辞書
MATH_UNICODE_MAP = {
    '𝑷': 'P', '𝑽': 'V', '𝑹': 'R', '𝑻': 'T',
    '𝒂': 'a', '𝒃': 'b', '𝒄': 'c', '𝒅': '\\mathrm{d}', '𝒆': 'e', '𝒇': 'f', '𝒈': 'g',
    '𝒉': 'h', '𝒊': 'i', '𝒋': 'j', '𝒌': 'k', '𝒍': 'l', '𝒎': 'm', '𝒏': 'n',
    '𝒙': 'x', '𝒚': 'y', '𝒛': 'z',
    '𝟐': '2', '𝟏': '1', '𝟎': '0',
    '−': '-', '＋': '+', '÷': '/', '×': '*', '⋅': '\\cdot',
    '…': '...', '∞': '\\infty',
    '∑': '\\sum', '∏': '\\prod',
    '∫': '\\int', '∬': '\\iint', '∭': '\\iiint', '∮': '\\oint',
    'α': '\\alpha', 'β': '\\beta', 'γ': '\\gamma', 'δ': '\\delta', 'ε': '\\epsilon',
    'ζ': '\\zeta', 'η': '\\eta', 'θ': '\\theta', 'ι': '\\iota', 'κ': '\\kappa',
    'λ': '\\lambda', 'μ': '\\mu', 'ν': '\\nu', 'ξ': '\\xi', 'π': '\\pi', 'ρ': '\\rho',
    'σ': '\\sigma', 'τ': '\\tau', 'υ': '\\upsilon', 'φ': '\\phi', 'χ': '\\chi',
    'ψ': '\\psi', 'ω': '\\omega',
    'Γ': '\\Gamma', 'Δ': '\\Delta', 'Θ': '\\Theta', 'Λ': '\\Lambda',
    'Ξ': '\\Xi', 'Π': '\\Pi', 'Σ': '\\Sigma', 'Φ': '\\Phi', 'Ψ': '\\Psi', 'Ω': '\\Omega',
    '′': "'", '°': '\\degree', '℃': '\\degree C'
}

# n-ary演算子のOMML文字→LaTeXの対応
NARY_TO_LATEX = {
    '∑': '\\sum', '∏': '\\prod',
    '∫': '\\int', '∬': '\\iint', '∭': '\\iiint', '∮': '\\oint',
    '⋀': '\\bigwedge', '⋁': '\\bigvee', '⋂': '\\bigcap', '⋃': '\\bigcup',
}
OPERATOR_CHARS = set(['∑', '∏', '∫', '∬', '∭', '∮'])


DEFAULT_PROMPT_TEMPLATE = """
あなたはプロフェッショナルな講師です。
スライドから抽出されたテキスト情報（Markdown形式）と添付のスライド画像を統合し、
解説を作成してください。

# スライド番号: {slide_no}
# 抽出テキスト情報:
{slide_text}
# 出力言語: {lang}

# 指示:
1. 画像内の図表やレイアウトを考慮しつつ、抽出テキスト内の数式を含めて正確に解説してください。
2. 以下のフォーマットで出力してください。
3. 出力Markdown中の数式は全て、
$$改行
LaTeX改行
$$
のブロック数式で出力してください

改行
## 1. 解説
## 2. 図・グラフの分析
## 3. このスライドから読み取れる詳細な情報、データの議論・予測
改行
"""

prompt_template = DEFAULT_PROMPT_TEMPLATE
_VAR_RE = re.compile(r"\$([A-Za-z_][A-Za-z0-9_]*)")


def terminate():
    if pause:
        input("\nPress ENTER to terminate>>\n")
    exit()


def parse_args():
    parser = argparse.ArgumentParser(description="PPTX -> Text/Math -> AI Explanation")
    parser.add_argument("infile", type=str, help="Path to input PPTX")
    parser.add_argument("-o", "--output", default = None, help="出力するMarkdownファイル名")
    parser.add_argument("--txt", type=str, default=None, help="Optional: Path to existing text file")
    parser.add_argument("--ini", "-i", type=str, default=Path(__file__).stem + ".ini",
                        help="prompt_templateを読み込むINIファイル。指定がなければスクリプト名.iniを探索。")

    openai_model = os.getenv("OPENAI_MODEL", "gpt-4o")
    openai_model5 = os.getenv("OPENAI_MODEL5", "gpt-5.2")
    google_model = os.getenv("GEMINI_MODEL") or os.getenv("GOOGLE_MODEL", "gemini-2.5-flash")

    parser.add_argument("--api", "-a", type=str, default="gemini",
                        choices=["gemini", "google", "openai5", "openai"])
    parser.add_argument("--model", default=None)
    parser.add_argument("--google_model", default=google_model)
    parser.add_argument("--openai_model", default=openai_model)
    parser.add_argument("--openai_model5", default=openai_model5)
    parser.add_argument("--visible", action="store_true")
    parser.add_argument("--language", type=str, default="Japanese")
    parser.add_argument("--pause", type=int, default=0)

    args = parser.parse_args()

    args.openai_key = os.getenv("OPENAI_API_KEY")
    args.gemini_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")

    if args.model:
        if args.api == 'openai5': args.openai_model5 = args.model
        elif args.api == 'openai': args.openai_model = args.model
        elif args.api in ('gemini', 'google'): args.google_model = args.model

    return parser, args


def search_file(infile=None):
    script_path = os.path.abspath(sys.argv[0])
    script_dir = os.path.dirname(script_path)
    script_name = os.path.splitext(os.path.basename(script_path))[0]
    default_ini = f"{script_name}.ini"

    if infile is None:
        for path in [os.getcwd(), script_dir]:
            candidate = os.path.join(path, default_ini)
            if os.path.isfile(candidate):
                return candidate
        return None

    if not os.path.isfile(infile):
        candidate = os.path.join(script_dir, infile)
        if os.path.isfile(candidate):
            return candidate
        return None

    return infile

def read_ini(inifile=None):
    path = search_file(inifile)
    if path is None:
        raise FileNotFoundError("INIファイルが見つかりませんでした")

    result = {}
    variables = {}
    current_key = None
    multiline_val = []
    multiline_delim = None

    with open(path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.rstrip()

            if not line or line.startswith('#') or line.startswith(';'):
                continue

            # 複数行値の終了判定（stripで判定）
            if multiline_delim:
                if line.strip() == multiline_delim:
                    val = '\n'.join(multiline_val)
                    result[current_key] = val
                    variables[current_key] = val
                    current_key = None
                    multiline_val = []
                    multiline_delim = None
                else:
                    multiline_val.append(line)
                continue

            # key=val の解析
            if '=' in line:
                key, val = map(str.strip, line.split('=', 1))
                val = val.strip()

                # 複数行値の開始判定（空文字でも対応）
                if (val == '"""' or val == "'''" or
                   (val.startswith('"""') and not val.endswith('"""')) or \
                   (val.startswith("'''") and not val.endswith("'''")) ):
                    multiline_delim = val[:3]
                    content = val[3:]
                    multiline_val = [content] if content else []
                    current_key = key
                    continue

                # 単一行の複数行値
                if (val.startswith('"""') and val.endswith('"""')) or \
                   (val.startswith("'''") and val.endswith("'''")):
                    val = val[3:-3]

                result[key] = val
                variables[key] = val

    # 変数展開（あとから一括処理）
    for key, val in result.items():
        def expand_var(match):
            var_name = match.group(1)
            return variables.get(var_name, match.group(0))
        result[key] = re.sub(r"\$(\w+)\b", expand_var, val)

    return result

def find_ini_path(ini_name: str) -> Optional[Path]:
    """cwd→スクリプトdirの順にiniを探索"""
    if not ini_name:
        return None
    p = Path(ini_name)
    if p.is_absolute() and p.is_file():
        return p
    cwd_path = Path.cwd() / p.name
    if cwd_path.is_file():
        return cwd_path
    script_path = Path(__file__).resolve().parent / p.name
    if script_path.is_file():
        return script_path
    return None

def _safe_text_replace_math_unicode(text: str) -> str:
    if not text:
        return ""
    for u, ltx in MATH_UNICODE_MAP.items():
        text = text.replace(u, ltx)
    return text

def _find_first(element, candidates):
    """候補タグ名（'m:sub'や'm:low'など）のうち最初に見つかった要素を返す"""
    for cand in candidates:
        found = element.find(cand, NAMESPACES)
        if found is not None:
            return found
    return None

def _detect_nary_op_char(element):
    op_tag = element.find('m:naryPr/m:chr', NAMESPACES)
    if op_tag is not None:
        val = op_tag.get(f"{{{NAMESPACES['m']}}}val", "")
        if val:
            return val
    ts = element.xpath('.//m:t[not(ancestor::m:e) and not(ancestor::m:sub) and not(ancestor::m:sup)]', namespaces=NAMESPACES)
    for t in ts:
        s = t.text or ""
        for ch in s:
            if ch in OPERATOR_CHARS:
                return ch
    return ""

def omml_to_latex(element):
    """
    OMMLのXML要素を再帰的に解析し、LaTeXに変換します。
    """
    tag = etree.QName(element).localname

    if tag in ('oMath', 'oMathPara'):
        return "".join(omml_to_latex(child) for child in element)

    elif tag == 'f':  # Fraction
        num = element.find('m:num', NAMESPACES)
        den = element.find('m:den', NAMESPACES)
        return f"\\frac{{{omml_to_latex(num)}}}{{{omml_to_latex(den)}}}"

    elif tag == 'rad':  # Root
        deg = element.find('m:deg', NAMESPACES)
        e = element.find('m:e', NAMESPACES)
        if deg is not None:
            return f"\\sqrt[{omml_to_latex(deg)}]{{{omml_to_latex(e)}}}"
        return f"\\sqrt{{{omml_to_latex(e)}}}"

    elif tag == 'sSup':  # Superscript
        e = element.find('m:e', NAMESPACES)
        sup = element.find('m:sup', NAMESPACES)
        return f"{omml_to_latex(e)}^{{{omml_to_latex(sup)}}}"

    elif tag == 'sSub':  # Subscript
        e = element.find('m:e', NAMESPACES)
        sub = element.find('m:sub', NAMESPACES)
        return f"{omml_to_latex(e)}_{{{omml_to_latex(sub)}}}"

    elif tag == 'sSubSup':  # Subscript + Superscript
        e = element.find('m:e', NAMESPACES)
        sub = element.find('m:sub', NAMESPACES)
        sup = element.find('m:sup', NAMESPACES)
        base = omml_to_latex(e)
        sub_l = omml_to_latex(sub) if sub is not None else ""
        sup_l = omml_to_latex(sup) if sup is not None else ""
        return f"{base}_{{{sub_l}}}^{{{sup_l}}}"

    elif tag == 'd':  # Delimiter
        beg_chr = element.find('m:dPr/m:begChr', NAMESPACES)
        end_chr = element.find('m:dPr/m:endChr', NAMESPACES)
        beg = beg_chr.get(f"{{{NAMESPACES['m']}}}val") if beg_chr is not None else "("
        end = end_chr.get(f"{{{NAMESPACES['m']}}}val") if end_chr is not None else ")"
        content = "".join(omml_to_latex(child) for child in element if etree.QName(child).localname != 'dPr')
        return f"{beg}{content}{end}"

    elif tag == 'r':  # Run
        return "".join(omml_to_latex(child) for child in element)

    elif tag == 't':  # Text
        return _safe_text_replace_math_unicode(element.text or "")

    elif tag == 'limLow':
        base = omml_to_latex(element.find('m:e', NAMESPACES))
        low = omml_to_latex(element.find('m:lim', NAMESPACES))
        return f"{base}_{{{low}}}"

    elif tag == 'limUpp':
        base = omml_to_latex(element.find('m:e', NAMESPACES))
        upp = omml_to_latex(element.find('m:lim', NAMESPACES))
        return f"{base}^{{{upp}}}"

    elif tag == 'int':
        lower = _find_first(element, ['m:sub', 'm:low'])
        upper = _find_first(element, ['m:sup', 'm:up'])
        e = element.find('m:e', NAMESPACES)
        lower_ltx = omml_to_latex(lower) if lower is not None else ""
        upper_ltx = omml_to_latex(upper) if upper is not None else ""
        e_ltx = omml_to_latex(e) if e is not None else ""
        if lower_ltx or upper_ltx:
            return f"\\int_{{{lower_ltx}}}^{{{upper_ltx}}}{e_ltx}"
        else:
            return f"\\int {e_ltx}"

    elif tag == 'nary':
        op_char = _detect_nary_op_char(element)
        lower_tag = _find_first(element, ['m:sub', 'm:low'])
        upper_tag = _find_first(element, ['m:sup', 'm:up'])
        content_tag = element.find('m:e', NAMESPACES)

        lower_ltx = omml_to_latex(lower_tag) if lower_tag is not None else ''
        upper_ltx = omml_to_latex(upper_tag) if upper_tag is not None else ''
        content_ltx = omml_to_latex(content_tag) if content_tag is not None else ''

        op_ltx = NARY_TO_LATEX.get(op_char, _safe_text_replace_math_unicode(op_char))
        if not op_ltx and (lower_ltx or upper_ltx):
            op_ltx = '\\int'

        if lower_ltx or upper_ltx:
            return f"{op_ltx}_{{{lower_ltx}}}^{{{upper_ltx}}}{content_ltx}"
        else:
            return f"{op_ltx} {content_ltx}".rstrip()

    return "".join(omml_to_latex(child) for child in element)

def split_latex_blocks(s: str):
    if not s:
        return []
    parts = [p.strip() for p in re.split(r'\\\\', s) if p.strip()]
    return parts if parts else [s.strip()]

def get_slide_title(slide):
    """スライドからタイトルを抽出します。"""
    for shape in slide.shapes:
        if getattr(shape, "has_text_frame", False) and shape.is_placeholder and shape.placeholder_format.type == 1:
            title_text = shape.text
            if title_text:
                return title_text.strip()
    for shape in slide.shapes:
        if getattr(shape, "has_text_frame", False):
            first_text = (shape.text or "").strip()
            if first_text:
                return first_text.split('\n')[0]
    return "無題のスライド"

def extract_content_to_markdown(pptx_path, output_md = None, image_dir = None, include_xml = False):
    """
    PPTXファイルからコンテンツを抽出し、Markdownファイルに保存します。
    - 数式: oMathPara を優先、oMathPara 配下の oMath は除外して重複回避
    - 複数行の式は 1 行 = 1 ブロック $$ ... $$ に分割（Pandoc対策）
    """

    try:
        presentation = Presentation(pptx_path)
    except Exception as e:
        print(f"エラー: ファイル '{pptx_path}' を開けませんでした。{e}")
        return

    if image_dir:
        os.makedirs(image_dir, exist_ok=True)

    print(f"Extracting text & math from PPTX: {pptx_path.name}...")
    slides_data = {}
    markdown_output = ""
    for i, slide in enumerate(presentation.slides):
        title = get_slide_title(slide)
        page_output = f"# スライド {i + 1}　{title}\n\n"

        # スライドの生XML
        slide_xml = slide.part.blob
        root = etree.fromstring(slide_xml)

        # テキスト
        text_elements = root.xpath('//a:t', namespaces=NAMESPACES)
        if text_elements:
            slide_text = " ".join([elem.text for elem in text_elements if elem.text])
            if slide_text.strip():
                page_output += f"## テキスト\n\n{slide_text.strip()}\n\n"

        # 数式（ブロック優先: oMathPara と、oMathPara外の単独oMath）
        math_elements = root.xpath(
            '//m:oMathPara | //m:oMath[not(ancestor::m:oMathPara)]',
            namespaces=NAMESPACES
        )
        if math_elements:
            page_output += "## 数式\n\n"
            seen_omml = set()
            for math_elem in math_elements:
                # 重複ガード（OMML文字列単位）
                omml_string = etree.tostring(math_elem, encoding='unicode')
                if omml_string in seen_omml:
                    continue
                seen_omml.add(omml_string)

                tag = etree.QName(math_elem).localname

                # oMathPara は直下の oMath を1行ごとに出力
                if tag == 'oMathPara':
                    inner_oms = math_elem.findall('m:oMath', NAMESPACES)
                    if inner_oms:
                        for om in inner_oms:
                            latex_code = omml_to_latex(om)
                            for one_line in split_latex_blocks(latex_code):
                                one_line = one_line.replace('\\mathrm{d}', '\\,\\mathrm{d}')
                                page_output += f"$$ {one_line} $$\n\n"
                    else:
                        # フォールバック
                        latex_code = omml_to_latex(math_elem)
                        for one_line in split_latex_blocks(latex_code):
                            one_line = one_line.replace('\\mathrm{d}', '\\,\\mathrm{d}')
                            page_output += f"$$ {one_line} $$\n\n"

                    if include_xml:
                        page_output += f"**元のOMML XML:**\n```xml\n{omml_string.strip()}\n```\n\n"

                # 単独 oMath はそのまま（ただし \\ があれば分割）
                else:
                    latex_code = omml_to_latex(math_elem)
                    for one_line in split_latex_blocks(latex_code):
                        one_line = one_line.replace('\\mathrm{d}', '\\,\\mathrm{d}')
                        page_output += f"$$ {one_line} $$\n\n"
                    if include_xml:
                        page_output += f"**元のOMML XML:**\n```xml\n{omml_string.strip()}\n```\n\n"

        # 画像
        image_elements = root.xpath('//a:blip[@r:embed]', namespaces=NAMESPACES)
        if image_dir and image_elements:
            page_output += "## 図\n\n"
            for j, image_elem in enumerate(image_elements):
                r_id = image_elem.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed')
                try:
                    image_part = slide.part.rels[r_id].target_part
                    image_data = image_part.blob
                    image_ext = image_part.content_type.split('/')[-1]
                    image_filename = f"slide{i+1}_image{j+1}.{image_ext}"
                    image_path = os.path.join(image_dir, image_filename)
                    with open(image_path, 'wb') as f:
                        f.write(image_data)
                    page_output += f"![スライド{i+1}の図{j+1}]({os.path.join(os.path.basename(image_dir), image_filename)})\n\n"
                except KeyError:
                    page_output += f"図のリンクが見つかりませんでした (rId: {r_id})\n\n"
            page_output += "\n"

        page_output += "\n---\n\n"
        slides_data[i] = page_output
        markdown_output += page_output
#        print("page_output:", page_output)

    if output_md:
        with open(output_md, "w", encoding="utf-8") as f:
            f.write(markdown_output)
        print(f"抽出が完了しました。結果は '{output_md}' に保存されました。")
    else:
#        for key, val in slides_data.items():
#            print(f"{key}: {val}")
        return slides_data

# =========================================================
# 2. Text Parsing (Existing Markdown File Support)
# =========================================================
SLIDE_HEADER_RE = re.compile(r"^\s*#\s*Slide\s+(\d+)", re.IGNORECASE)

def load_slide_texts(txt_path: Path) -> Dict[int, str]:
    slides: Dict[int, str] = {}
    if not txt_path or not txt_path.exists():
        return {}

    print(f"Loading text from file: {txt_path.name}")
    current_no: Optional[int] = None
    buf: List[str] = []
    
    with txt_path.open("r", encoding="utf-8") as f:
        for line in f:
            m = SLIDE_HEADER_RE.match(line)
            if m:
                if current_no is not None: slides[current_no] = "".join(buf).strip()
                current_no = int(m.group(1)); buf = []
            else:
                buf.append(line)
    if current_no is not None: slides[current_no] = "".join(buf).strip()
    return slides


# =========================================================
# 3. PDF/Image Conversion
# =========================================================
def pptx_to_pdf(pptx_path: Path, pdf_path: Path, visible: bool = False) -> None:
    abs_pptx = str(pptx_path.resolve())
    abs_pdf = str(pdf_path.resolve())
    if pdf_path.exists(): return

    print(f"Converting PPTX to PDF...")
    try:
        app = win32com.client.Dispatch("PowerPoint.Application")
        if not visible: app.DisplayAlerts = 0
        presentation = app.Presentations.Open(abs_pptx, ReadOnly=True, Untitled=False, WithWindow=visible)
        try:
            presentation.SaveAs(abs_pdf, 32)
        finally:
            presentation.Close()
            if not visible: app.Quit()
    except Exception as e:
        print(f"PPTX->PDF Error: {e}"); raise e

def convert_pdf_to_images(pdf_path: Path, out_dir: Path) -> List[Path]:
    if not out_dir.exists(): out_dir.mkdir(parents=True, exist_ok=True)
    doc = fitz.open(str(pdf_path.resolve()))
    paths = []
    print(f"Converting PDF to Images...")
    for i in range(len(doc)):
        mat = fitz.Matrix(2.0, 2.0) # 高解像度化
        pix = doc.load_page(i).get_pixmap(matrix=mat)
        p = out_dir / f"slide_{i+1:03d}.png"
        pix.save(str(p))
        paths.append(p)
    doc.close()
    return paths

# =========================================================
# 3. AI 呼び出し部
# =========================================================
def call_gemini(model_name, slide_no, slide_text, image_path, lang):
    prompt = prompt_template\
        .replace("{slide_no}", f"{slide_no}")\
        .replace("{slide_text}", slide_text)\
        .replace("{lang}", lang)\
        .strip()

    print(f"\nprompt:", prompt)

    model = genai.GenerativeModel(model_name)
    img_data = {'mime_type': 'image/png', 'data': image_path.read_bytes()}

    try:
        res = model.generate_content([prompt, img_data])
        return res.text
    except Exception as e:
        return f"Gemini Error: {e}"


def call_openai(client, model, slide_no, slide_text, image_path, lang):
    b64 = base64.b64encode(image_path.read_bytes()).decode('utf-8')
    prompt = prompt_template\
        .replace("{slide_no}", f"{slide_no}")\
        .replace("{slide_text}", slide_text)\
        .replace("{lang}", lang)\
        .strip()

    print(f"\nprompt:", prompt)

    res = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": [
            {"type": "text", "text": prompt},
            {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}}
        ]}]
    )
    return res.choices[0].message.content


def main():
    global prompt_template, pause

    print()
    parser, args = parse_args()
    if args.pause: pause = 1
    if args.output:
        out_md = out_md = args.output
    else:
        out_md = infile_path.with_name(infile_path.stem + ".md")
#    out_md = infile_path.with_name(infile_path.stem + "_explanation.md")

    ini_path = find_ini_path(args.ini)
    if ini_path:
        try:
            ini_data = read_ini(ini_path)
            if "prompt_template" in ini_data:
                prompt_template = ini_data["prompt_template"]
                print(f"Loaded prompt_template from {ini_path}")
            else:
                print(f"prompt_template key not found in {ini_path}, using default.")
        except Exception as e:
            print(f"Warning: failed to read ini ({e}), using default prompt.")
    else:
        print(f"INI not found ({args.ini}), using default prompt.")

#    print("prompt_template:", prompt_template)
    print(f"infile: {args.infile}")
    
    infile_path = Path(args.infile)
    work_dir = infile_path.parent / (infile_path.stem + "_work")
    img_dir = work_dir / "slides_png"
    work_dir.mkdir(exist_ok=True)

    # 1. Text/Math Extraction
    slide_texts = {}
    if args.txt and Path(args.txt).exists():
        slide_texts = load_slide_texts(Path(args.txt))
    elif infile_path.suffix.lower() == ".pptx":
        # PPTXから高度な数式抽出を実行
        slide_texts = extract_content_to_markdown(infile_path)
    
    # 2. PDF/Image Conversion
    if infile_path.suffix.lower() == ".pptx":
        pdf_path = work_dir / (infile_path.stem + ".pdf")
        pptx_to_pdf(infile_path, pdf_path, visible=args.visible)
    else:
        # PDF入力の場合
        pdf_path = infile_path

    images = convert_pdf_to_images(pdf_path, img_dir)

    # 3. AI Setup
    if args.api == "openai5":
        model = args.openai_model5
    elif args.api == "openai":
        model = args.openai_model
    else:
        model = args.google_model
    print(f"Using API: {args.api} / Model: {model}")

    client_openai = None
    if args.api == "openai" or args.api == "openai5":
        if args.openai_key is None:
            print("Error: OPENAI_API_KEY missing.")
            sys.exit(1)
        client_openai = OpenAI()
    elif args.api == "gemini":
        if args.gemini_key is None:
            print("Error: GEMINI_KEY and GOOGLE_API_KEY missing.")
            sys.exit(1)
        genai.configure(api_key=args.gemini_key)

    # 4. Processing
    with open(out_md, "w", encoding="utf-8") as f:
        f.write(f"# Analysis Report: {infile_path.name}\n\n---\n")

    for i, img_p in enumerate(images, start=1):
        print()
        print(f"Processing Slide {i}/{len(images)}...", end=" ", flush=True)
        txt = slide_texts.get(i-1, "(テキスト情報なし)")

        try:
            if args.api in ("openai5", "openai"):
                content = call_openai(client_openai, model, i, txt, img_p, args.language)
            else:
                content = call_gemini(model, i, txt, img_p, args.language)

            chunk = f"\n# Slide {i}\n\n{content}\n\n---\n"
            print("Done.")
            time.sleep(1)

        except Exception as e:
            print(f"Error: {e}")
            chunk = f"# Slide {i}\n\nError: {e}\n\n---\n"

    # ✅ 毎回 append モードで開いて書き込む
        with open(out_md, "a", encoding="utf-8") as f:
            f.write(chunk)

    print(f"\nSaved to: {out_md}")


if __name__ == "__main__":
    main()
    terminate()
