#!/usr/bin/env python
"""
Windows版PowerPointを使って、PPTXファイルをMP4へ変換する。

必要条件:
    pip install pywin32
"""

from __future__ import annotations

import argparse
import sys
import time
from pathlib import Path

import pythoncom
import win32com.client


# PowerPoint.PpMediaTaskStatus
STATUS_NONE = 0
STATUS_IN_PROGRESS = 1
STATUS_QUEUED = 2
STATUS_DONE = 3
STATUS_FAILED = 4

STATUS_NAMES = {
    STATUS_NONE: "準備中",
    STATUS_IN_PROGRESS: "変換中",
    STATUS_QUEUED: "待機中",
    STATUS_DONE: "完了",
    STATUS_FAILED: "失敗",
}


def convert_pptx_to_mp4(
    input_path: Path,
    output_path: Path,
    *,
    resolution: int = 720,
    fps: int = 30,
    quality: int = 100,
    default_slide_duration: int = 5,
    use_timings_and_narrations: bool = True,
    overwrite: bool = False,
) -> None:
    """PowerPointのCreateVideoを使ってPPTXをMP4に変換する。"""

    input_path = input_path.resolve()
    output_path = output_path.resolve()

    if not input_path.is_file():
        raise FileNotFoundError(f"入力ファイルが見つかりません: {input_path}")

    if input_path.suffix.lower() not in {".pptx", ".pptm", ".ppt"}:
        raise ValueError(f"PowerPointファイルではありません: {input_path}")

    if output_path.suffix.lower() != ".mp4":
        raise ValueError("出力ファイルの拡張子は .mp4 にしてください。")

    output_path.parent.mkdir(parents=True, exist_ok=True)

    if output_path.exists():
        if not overwrite:
            raise FileExistsError(
                f"出力ファイルがすでに存在します: {output_path}\n"
                "--overwrite を指定すると上書きできます。"
            )
        output_path.unlink()

    pythoncom.CoInitialize()

    powerpoint = None
    presentation = None

    try:
        # 独立したPowerPointプロセスを起動
        powerpoint = win32com.client.DispatchEx("PowerPoint.Application")
        powerpoint.Visible = True

        print(f"入力: {input_path}")
        print(f"出力: {output_path}")
        print(
            f"設定: {resolution}p, {fps} fps, "
            f"quality={quality}, timings={use_timings_and_narrations}"
        )

        # Open(FileName, ReadOnly, Untitled, WithWindow)
        presentation = powerpoint.Presentations.Open(
            str(input_path),
            True,   # ReadOnly
            False,  # Untitled
            True,   # WithWindow
        )

        # CreateVideo(
        #     FileName,
        #     UseTimingsAndNarrations,
        #     DefaultSlideDuration,
        #     VertResolution,
        #     FramesPerSecond,
        #     Quality
        # )
        presentation.CreateVideo(
            str(output_path),
            use_timings_and_narrations,
            default_slide_duration,
            resolution,
            fps,
            quality,
        )

        print("ビデオの作成を開始しました。")

        previous_status = None

        while True:
            status = int(presentation.CreateVideoStatus)

            if status != previous_status:
                print(f"状態: {STATUS_NAMES.get(status, f'不明 ({status})')}")
                previous_status = status

            if status == STATUS_DONE:
                break

            if status == STATUS_FAILED:
                raise RuntimeError("PowerPointによるビデオ作成に失敗しました。")

            time.sleep(2)

        if not output_path.exists():
            raise RuntimeError(
                "PowerPointは完了を返しましたが、MP4ファイルが見つかりません。"
            )

        size_mb = output_path.stat().st_size / (1024 * 1024)

        print("変換が完了しました。")
        print(f"MP4: {output_path}")
        print(f"サイズ: {size_mb:.1f} MB")

    finally:
        if presentation is not None:
            try:
                presentation.Close()
            except Exception:
                pass

        if powerpoint is not None:
            try:
                powerpoint.Quit()
            except Exception:
                pass

        pythoncom.CoUninitialize()


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Windows版PowerPointを使ってPPTXをMP4に変換します。"
    )

    parser.add_argument(
        "input",
        type=Path,
        help="入力するPowerPointファイル",
    )

    parser.add_argument(
        "-o",
        "--output",
        type=Path,
        help="出力MP4ファイル。省略時は入力ファイルと同じ場所に作成",
    )

    parser.add_argument(
        "--resolution",
        type=int,
        default=720,
        choices=(480, 720, 1080, 2160),
        help="動画の縦解像度。デフォルト: 720",
    )

    parser.add_argument(
        "--fps",
        type=int,
        default=30,
        help="フレームレート。デフォルト: 30",
    )

    parser.add_argument(
        "--quality",
        type=int,
        default=100,
        choices=range(1, 101),
        metavar="1-100",
        help="動画品質。デフォルト: 100",
    )

    parser.add_argument(
        "--default-duration",
        type=int,
        default=5,
        help="タイミング未設定時の1スライドの秒数。デフォルト: 5",
    )

    parser.add_argument(
        "--ignore-timings",
        action="store_true",
        help="記録済みタイミングとナレーションを使わない",
    )

    parser.add_argument(
        "--overwrite",
        type=int,
        default=0,
        help="既存のMP4ファイルを上書きするか [0|1]",
    )

    parser.add_argument(
        "--pause",
        type=int,
        default=0,
        help="既存のMP4ファイルを上書きするか [0|1]",
    )

    return parser.parse_args()


def main() -> int:
    args = parse_args()

    output = args.output
    if output is None:
        output = args.input.with_suffix(".mp4")

    try:
        convert_pptx_to_mp4(
            args.input,
            output,
            resolution=args.resolution,
            fps=args.fps,
            quality=args.quality,
            default_slide_duration=args.default_duration,
            use_timings_and_narrations=not args.ignore_timings,
            overwrite=args.overwrite,
        )
        return 0, args

    except KeyboardInterrupt:
        print("\n中断しました。", file=sys.stderr)
        return 130, args

    except Exception as exc:
        print(f"エラー: {exc}", file=sys.stderr)
        return 1, args


if __name__ == "__main__":
    ret, args = main()
    if args.pause: input("\nPress ENTER to terminate>>\n")
    raise SystemExit(ret)