from __future__ import annotations

import sys
from dataclasses import dataclass, field
from fractions import Fraction

import numpy as np
from numpy.typing import NDArray

from auto_editor.analyze import audio_levels, motion_levels, pixeldiff_levels
from auto_editor.ffwrapper import FFmpeg, FileInfo
from auto_editor.objs.edit import audio_builder, motion_builder, pixeldiff_builder
from auto_editor.objs.util import _Vars, parse_dataclass
from auto_editor.output import Ensure
from auto_editor.utils.bar import Bar
from auto_editor.utils.func import setup_tempdir
from auto_editor.utils.log import Log
from auto_editor.utils.types import frame_rate
from auto_editor.vanparse import ArgumentParser


@dataclass
class Audio:
    stream: int


@dataclass
class Motion:
    stream: int
    blur: int
    width: int


@dataclass
class Pixeldiff:
    stream: int


@dataclass
class LevelArgs:
    input: list[str] = field(default_factory=list)
    edit: str = "audio"
    timebase: Fraction | None = None
    ffmpeg_location: str | None = None
    my_ffmpeg: bool = False
    help: bool = False


def levels_options(parser: ArgumentParser) -> ArgumentParser:
    parser.add_required("input", nargs="*")
    parser.add_argument(
        "--edit",
        metavar="METHOD:[ATTRS?]",
        help="Select the kind of detection to analyze with attributes",
    )
    parser.add_argument(
        "--timebase",
        "-tb",
        metavar="NUM",
        type=frame_rate,
        help="Set custom timebase",
    )
    parser.add_argument("--ffmpeg-location", help="Point to your custom ffmpeg file")
    parser.add_argument(
        "--my-ffmpeg",
        flag=True,
        help="Use the ffmpeg on your PATH instead of the one packaged",
    )
    return parser


def print_floats(arr: NDArray[np.float_]) -> None:
    for a in arr:
        sys.stdout.write(f"{a:.20f}\n")


def print_ints(arr: NDArray[np.uint64]) -> None:
    for a in arr:
        sys.stdout.write(f"{a}\n")


def main(sys_args: list[str] = sys.argv[1:]) -> None:
    parser = levels_options(ArgumentParser("levels"))
    args = parser.parse_args(LevelArgs, sys_args)

    ffmpeg = FFmpeg(args.ffmpeg_location, args.my_ffmpeg, False)

    bar = Bar("none")
    temp = setup_tempdir(None, Log())
    log = Log(temp=temp)

    sources = {}
    for i, path in enumerate(args.input):
        sources[str(i)] = FileInfo(path, ffmpeg, log, str(i))

    assert "0" in sources
    src = sources["0"]

    tb = src.get_fps() if args.timebase is None else args.timebase
    ensure = Ensure(ffmpeg, src.get_samplerate(), temp, log)

    strict = True
    METHODS = ("audio", "motion", "pixeldiff")

    if ":" in args.edit:
        method, attrs = args.edit.split(":")
    else:
        method, attrs = args.edit, ""

    if method not in METHODS:
        log.error(f"Method: {method} not supported")

    for src in sources.values():
        if method == "audio":
            aobj = parse_dataclass(attrs, (Audio, audio_builder[1:]), log)
            print_floats(
                audio_levels(ensure, src, aobj.stream, tb, bar, strict, temp, log)
            )

        if method == "motion":
            if src.videos:
                _vars: _Vars = {"width": src.videos[0].width}
            else:
                _vars = {"width": 1}
            mobj = parse_dataclass(attrs, (Motion, motion_builder[1:]), log, _vars)
            print_floats(motion_levels(ensure, src, mobj, tb, bar, strict, temp, log))

        if method == "pixeldiff":
            pobj = parse_dataclass(attrs, (Pixeldiff, pixeldiff_builder[1:]), log)
            print_ints(pixeldiff_levels(ensure, src, pobj, tb, bar, strict, temp, log))

    log.cleanup()


if __name__ == "__main__":
    main()
