# Fluster - testing framework for decoders conformance
# Copyright (C) 2020, Fluendo, S.A.
#  Author: Pablo Marcos Oltra <pmarcos@fluendo.com>, Fluendo, S.A.
#  Author: Andoni Morales Alastruey <amorales@fluendo.com>, Fluendo, S.A.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation, either version 3
# of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <https://www.gnu.org/licenses/>.

import shlex
import subprocess
from functools import lru_cache
from typing import Optional, List

from fluster.codec import Codec, OutputFormat
from fluster.decoder import Decoder, register_decoder
from fluster.utils import (
    file_checksum,
    run_command,
    run_pipe_command_with_std_output,
    normalize_binary_cmd,
)

PIPELINE_TPL = "{} --no-fault filesrc location={} ! {} ! {} ! {} {}"


@lru_cache(maxsize=None)
def gst_element_exists(element: str) -> bool:
    """Check if an element exists in current GStreamer installation"""
    inspect_exe = normalize_binary_cmd("gst-inspect-1.0")

    try:
        subprocess.run(
            [inspect_exe, "--exists", element],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            check=True,
        )
        return True
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False
    return False


def output_format_to_gst(output_format: OutputFormat) -> str:
    """Return GStreamer pixel format"""
    mapping = {
        OutputFormat.GRAY: "GRAY8",
        OutputFormat.GRAY16LE: "GRAY16_LE",
        OutputFormat.YUV420P: "I420",
        OutputFormat.YUV422P: "Y42B",
        OutputFormat.YUV420P10LE: "I420_10LE",
        OutputFormat.YUV422P10LE: "I422_10LE",
        OutputFormat.YUV420P12LE: "I420_12LE",
        OutputFormat.YUV422P12LE: "I422_12LE",
        OutputFormat.YUV444P: "Y444",
        OutputFormat.YUV444P10LE: "Y444_10LE",
        OutputFormat.YUV444P12LE: "Y444_12LE",
    }
    if output_format not in mapping:
        raise Exception(
            f"No matching output format found in GStreamer for {output_format}"
        )
    return mapping[output_format]


class GStreamer(Decoder):
    """Base class for GStreamer decoders"""

    decoder_bin = ""
    cmd = ""
    caps = ""
    gst_api = ""
    api = ""
    provider = ""
    sink = ""

    def __init__(self) -> None:
        super().__init__()
        if not self.name:
            self.name = (
                f"{self.provider}-{self.codec.value}-{self.api}-Gst{self.gst_api}"
            )
        self.description = f"{self.provider} {self.codec.value} {self.api} decoder for GStreamer {self.gst_api}"
        self.cmd = normalize_binary_cmd(self.cmd)

        if not gst_element_exists(self.sink):
            self.sink = "filesink"

    def gen_pipeline(
        self,
        input_filepath: str,
        output_filepath: Optional[str],
        output_format: OutputFormat,
    ) -> str:
        """Generate the GStreamer pipeline used to decode the test vector"""
        # pylint: disable=unused-argument
        output = f"location={output_filepath}" if output_filepath else ""
        return PIPELINE_TPL.format(
            self.cmd, input_filepath, self.decoder_bin, self.caps, self.sink, output
        )

    def parse_videocodectestsink_md5sum(self, data: List[str], verbose: bool) -> str:
        """Parse the MD5 sum out of commandline output produced when using
        videocodectestsink."""
        md5sum = None
        for line in data:
            if verbose:
                print(line)
            pattern = (
                "conformance/checksum, checksum-type=(string)MD5, checksum=(string)"
            )
            sum_start = line.find(pattern)
            # pylint: disable=no-else-continue
            if sum_start <= 0:
                # Skip to the next iteration if sum_start is less than or equal to 0
                continue
            else:
                sum_start += len(pattern)
                sum_end = line[sum_start:].find(";")
                # pylint: disable=no-else-continue
                if sum_end <= 0:
                    # Skip to the next iteration if sum_end is less than or equal to 0
                    continue
                else:
                    sum_end += sum_start
                    md5sum = line[sum_start:sum_end]
                    if not verbose:
                        return md5sum

        if not md5sum:
            raise Exception("No MD5 found in the program trace.")

        return md5sum

    def decode(
        self,
        input_filepath: str,
        output_filepath: str,
        output_format: OutputFormat,
        timeout: int,
        verbose: bool,
        keep_files: bool,
    ) -> str:
        """Decode the test vector and do the checksum"""
        # When using videocodectestsink we can avoid writing files to disk
        # completely, or avoid a full raw file read in order to compute the MD5
        # SUM.
        if self.sink == "videocodectestsink":
            output_param = output_filepath if keep_files else None
            pipeline = self.gen_pipeline(input_filepath, output_param, output_format)
            command = shlex.split(pipeline)
            command.append("-m")
            data = run_pipe_command_with_std_output(
                command, timeout=timeout, verbose=verbose
            )
            return self.parse_videocodectestsink_md5sum(data, verbose)

        pipeline = self.gen_pipeline(input_filepath, output_filepath, output_format)
        run_command(shlex.split(pipeline), timeout=timeout, verbose=verbose)
        return file_checksum(output_filepath)

    @lru_cache(maxsize=128)
    def check(self, verbose: bool) -> bool:
        """Check if GStreamer decoder is valid (better than gst-inspect)"""
        # pylint: disable=broad-except
        try:
            if hasattr(self, "check_decoder_bin"):
                decoder_bin = getattr(self, "check_decoder_bin")
            else:
                decoder_bin = self.decoder_bin
            binary = normalize_binary_cmd(f"gst-launch-{self.gst_api}")
            pipeline = (
                f"{binary} --no-fault appsrc num-buffers=0 ! {decoder_bin} ! fakesink"
            )
            run_command(shlex.split(pipeline), verbose=verbose)
        except Exception:
            return False
        return True


class GStreamer10Video(GStreamer):
    """Base class for GStreamer 1.x video decoders"""

    cmd = "gst-launch-1.0"
    caps = "video/x-raw"
    gst_api = "1.0"
    sink = "videocodectestsink"
    provider = "GStreamer"

    def gen_pipeline(
        self,
        input_filepath: str,
        output_filepath: Optional[str],
        output_format: OutputFormat,
    ) -> str:
        caps = f"{self.caps} ! videoconvert dither=none ! video/x-raw,format={output_format_to_gst(output_format)}"
        output = f"location={output_filepath}" if output_filepath else ""
        return PIPELINE_TPL.format(
            self.cmd, input_filepath, self.decoder_bin, caps, self.sink, output
        )


class GStreamer10Audio(GStreamer):
    """Base class for GStreamer 1.x audio decoders"""

    cmd = "gst-launch-1.0"
    caps = "audio/x-raw"
    gst_api = "1.0"
    sink = "filesink"
    provider = "GStreamer"


class GStreamer010Video(GStreamer):
    """Base class for GStreamer 0.10 video decoders"""

    cmd = "gst-launch-0.10"
    caps = "video/x-raw-yuv"
    gst_api = "0.10"
    sink = "videocodectestsink"
    provider = "GStreamer"


@register_decoder
class GStreamerLibavH264(GStreamer10Video):
    """GStreamer H.264 Libav decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! avdec_h264 "
    api = "Libav"
    hw_acceleration = False


@register_decoder
class GStreamerLibavH265(GStreamer10Video):
    """GStreamer H.265 Libav decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! avdec_h265 "
    api = "Libav"
    hw_acceleration = False


@register_decoder
class GStreamerLibavVP8(GStreamer10Video):
    """GStreamer VP8 Libav decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! avdec_vp8 "
    api = "Libav"
    hw_acceleration = False


@register_decoder
class GStreamerLibavVP9(GStreamer10Video):
    """GStreamer VP9 Libav decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " avdec_vp9"
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "Libav"
    hw_acceleration = False


@register_decoder
class GStreamerVaapiH265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 VAAPI decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! vaapih265dec "
    api = "VAAPI"
    hw_acceleration = True


@register_decoder
class GStreamerVaH265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 VA decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! vah265dec "
    api = "VA"
    hw_acceleration = True


@register_decoder
class GStreamerMsdkH265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 Intel MSDK decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! msdkh265dec "
    api = "MSDK"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecH265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 NVDEC decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! nvh265dec "
    api = "NVDEC"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecSLH265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 NVDEC stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! nvh265sldec "
    api = "NVDECSL"
    hw_acceleration = True


@register_decoder
class GStreamerD3d11H265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 D3D11 decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! d3d11h265dec "
    api = "D3D11"
    hw_acceleration = True


@register_decoder
class GStreamerD3d12H265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 D3D12 decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! d3d12h265dec "
    api = "D3D12"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2CodecsH265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 V4L2 stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! v4l2slh265dec "
    api = "V4L2SL"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2H265Gst10Decoder(GStreamer10Video):
    """GStreamer H.265 V4L2 stateful decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! v4l2h265dec "
    api = "V4L2"
    hw_acceleration = True


@register_decoder
class GStreamerVaapiH264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 VAAPI decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! vaapih264dec "
    api = "VAAPI"
    hw_acceleration = True


@register_decoder
class GStreamerVaH264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 VA decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! vah264dec "
    api = "VA"
    hw_acceleration = True


@register_decoder
class GStreamerMsdkH264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 Intel MSDK decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! msdkh264dec "
    api = "MSDK"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecH264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 NVDEC decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! nvh264dec "
    api = "NVDEC"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecSLH264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 NVDEC stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! nvh264sldec "
    api = "NVDECSL"
    hw_acceleration = True


@register_decoder
class GStreamerD3d11H264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 D3D11 decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! d3d11h264dec "
    api = "D3D11"
    hw_acceleration = True


@register_decoder
class GStreamerD3d12H264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 D3D12 decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! d3d12h264dec "
    api = "D3D12"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2CodecsH264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 V4L2 stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! v4l2slh264dec "
    api = "V4L2SL"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2H264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 V4L2 stateful decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! v4l2h264dec "
    api = "V4L2"
    hw_acceleration = True


@register_decoder
class GStreamerVulkanH264Gst10Decoder(GStreamer10Video):
    """GStreamer H.264 Vulkan stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! vulkanh264dec ! vulkandownload "
    api = "Vulkan"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2CodecsVP8Gst10Decoder(GStreamer10Video):
    """GStreamer VP8 V4L2 stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! v4l2slvp8dec "
    api = "V4L2SL"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2VP8Gst10Decoder(GStreamer10Video):
    """GStreamer VP8 V4L2 stateful decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! v4l2vp8dec "
    api = "V4L2"
    hw_acceleration = True


@register_decoder
class GStreamerLibvpxVP8(GStreamer10Video):
    """GStreamer VP8 Libvpx decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! vp8dec "
    api = "libvpx"
    hw_acceleration = False


@register_decoder
class GStreamerVaapiVP8Gst10Decoder(GStreamer10Video):
    """GStreamer VP8 VAAPI decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! vaapivp8dec "
    api = "VAAPI"
    hw_acceleration = True


@register_decoder
class GStreamerVaVP8Gst10Decoder(GStreamer10Video):
    """GStreamer VP8 VA decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! vavp8dec "
    api = "VA"
    hw_acceleration = True


@register_decoder
class GStreamerD3d11VP8Gst10Decoder(GStreamer10Video):
    """GStreamer VP8 D3D11 decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! d3d11vp8dec "
    api = "D3D11"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecVP8Gst10Decoder(GStreamer10Video):
    """GStreamer VP8 NVDEC decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! nvvp8dec "
    api = "NVDEC"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecSLVP8Gst10Decoder(GStreamer10Video):
    """GStreamer VP8 NVDEC stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.VP8
    decoder_bin = " ivfparse ! nvvp8sldec "
    api = "NVDECSL"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2CodecsVP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 V4L2 stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " v4l2slvp9dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "V4L2SL"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2VP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 V4L2 stateful decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " v4l2vp9dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "V4L2"
    hw_acceleration = True


@register_decoder
class GStreamerVaAV1Gst10Decoder(GStreamer10Video):
    """GStreamer AV1 VA decoder implementation for GStreamer 1.0"""

    codec = Codec.AV1
    check_decoder_bin = " vaav1dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "VA"
    hw_acceleration = True


@register_decoder
class GStreamerV4l2CodecsAV1Gst10Decoder(GStreamer10Video):
    """GStreamer AV1 V4L2 stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.AV1
    check_decoder_bin = " v4l2slav1dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "V4L2SL"
    hw_acceleration = True


@register_decoder
class GStreamerD3d11AV1Gst10Decoder(GStreamer10Video):
    """GStreamer AV1 D3D11 decoder implementation for GStreamer 1.0"""

    codec = Codec.AV1
    check_decoder_bin = " d3d11av1dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "D3D11"
    hw_acceleration = True


@register_decoder
class GStreamerD3d12AV1Gst10Decoder(GStreamer10Video):
    """GStreamer AV1 D3D12 decoder implementation for GStreamer 1.0"""

    codec = Codec.AV1
    check_decoder_bin = " d3d12av1dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "D3D12"
    hw_acceleration = True


@register_decoder
class GStreamerLibvpxVP9(GStreamer10Video):
    """GStreamer VP9 Libvpx decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " vp9dec  "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "libvpx"
    hw_acceleration = False


@register_decoder
class GStreamerVaapiVP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 VAAPI decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " vaapivp9dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "VAAPI"
    hw_acceleration = True


@register_decoder
class GStreamerVaapiAV1Gst10Decoder(GStreamer10Video):
    """GStreamer AV1 VAAPI decoder implementation for GStreamer 1.0"""

    codec = Codec.AV1
    check_decoder_bin = " vaapiav1dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "VAAPI"
    hw_acceleration = True


@register_decoder
class GStreamerDav1dAV1Decoder(GStreamer10Video):
    """GStreamer AV1 dav1d decoder implementation for GStreamer 1.0"""

    codec = Codec.AV1
    check_decoder_bin = " dav1ddec "
    decoder_bin = f" parsebin ! {check_decoder_bin} "
    api = "dav1d"
    hw_acceleration = False


@register_decoder
class GStreamerVaVP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 VA decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " vavp9dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "VA"
    hw_acceleration = True


@register_decoder
class GStreamerD3d11VP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 D3D11 decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " d3d11vp9dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "D3D11"
    hw_acceleration = True


@register_decoder
class GStreamerD3d12VP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 D3D12 decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " d3d12vp9dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "D3D12"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecVP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 NVDEC decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " nvvp9dec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "NVDEC"
    hw_acceleration = True


@register_decoder
class GStreamerNvdecSLVP9Gst10Decoder(GStreamer10Video):
    """GStreamer VP9 NVDEC stateless decoder implementation for GStreamer 1.0"""

    codec = Codec.VP9
    check_decoder_bin = " nvvp9sldec "
    decoder_bin = f" parsebin ! {check_decoder_bin}"
    api = "NVDECSL"
    hw_acceleration = True


@register_decoder
class GStreamerVVdeCH266Decoder(GStreamer10Video):
    """GStreamer H.266/VVC VVdeC decoder implementation for GStreamer 1.0"""

    codec = Codec.H266
    decoder_bin = " h266parse ! vvdec "
    api = "VVdeC"
    hw_acceleration = False


@register_decoder
class FluendoH265Gst10Decoder(GStreamer10Video):
    """Fluendo H.265 software decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! fluh265dec "
    provider = "Fluendo"
    api = "SW"


@register_decoder
class FluendoH265Gst010Decoder(GStreamer010Video):
    """Fluendo H.265 software decoder implementation for GStreamer 0.10"""

    codec = Codec.H265
    decoder_bin = " h265parse ! fluh265dec "
    provider = "Fluendo"
    api = "SW"


@register_decoder
class FluendoH264Gst10Decoder(GStreamer10Video):
    """Fluendo H.264 software decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! fluh264dec "
    provider = "Fluendo"
    api = "SW"


@register_decoder
class FluendoH264Gst010Decoder(GStreamer010Video):
    """Fluendo H.264 software decoder implementation for GStreamer 0.10"""

    codec = Codec.H264
    decoder_bin = " fluh264dec "
    provider = "Fluendo"
    api = "SW"


@register_decoder
class FluendoH264VAGst10Decoder(GStreamer10Video):
    """Fluendo H.264 hardware decoder implementation for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! fluhwvadec "
    provider = "Fluendo"
    api = "HW"
    hw_acceleration = True


class FluendoH265VAGst10DecoderBase(GStreamer10Video):
    """Fluendo H.265 hardware decoder implementation for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin_tmpl = (
        " h265parse !"
        " video/x-h265,stream-format={stream_format},alignment={alignment} !"
        " fluhwvadec "
    )
    provider = "Fluendo"
    api = "HW"
    hw_acceleration = True
    stream_format: str = ""
    alignment: str = ""

    def __init__(self) -> None:
        super().__init__()
        self.name = self._translator(self.name)
        self.description = self._translator(self.description)
        self.decoder_bin = self.decoder_bin_tmpl.format(
            stream_format=self.stream_format, alignment=self.alignment
        )

    def _translator(self, target_val: str) -> str:
        new_val = f"{self.codec.value}-{self.stream_format}-{self.alignment}"
        return target_val.replace(self.codec.value, new_val)


@register_decoder
class FluendoH265ByteStreamAuVAGst10Decoder(FluendoH265VAGst10DecoderBase):
    """
    Fluendo H.265 (byte-stream/au) hardware decoder implementation for GStreamer 1.0
    """

    stream_format = "byte-stream"
    alignment = "au"


@register_decoder
class FluendoH265ByteStreamNalVAGst10Decoder(FluendoH265VAGst10DecoderBase):
    """
    Fluendo H.265 (byte-stream/nal) hardware decoder implementation for GStreamer 1.0
    """

    stream_format = "byte-stream"
    alignment = "nal"


@register_decoder
class FluendoH265Hev1AuVAGst10Decoder(FluendoH265VAGst10DecoderBase):
    """
    Fluendo H.265 (hev1/au) hardware decoder implementation for GStreamer 1.0
    """

    stream_format = "hev1"
    alignment = "au"


@register_decoder
class FluendoH265Hvc1AuVAGst10Decoder(FluendoH265VAGst10DecoderBase):
    """
    Fluendo H.265 (hvc1/au) hardware decoder implementation for GStreamer 1.0
    """

    stream_format = "hvc1"
    alignment = "au"


@register_decoder
class FluendoH265Hev1NalVAGst10Decoder(FluendoH265VAGst10DecoderBase):
    """
    Fluendo H.265 (hev1/nal) hardware decoder implementation for GStreamer 1.0
    """

    stream_format = "hev1"
    alignment = "nal"


@register_decoder
class FluendoH265Hvc1NalVAGst10Decoder(FluendoH265VAGst10DecoderBase):
    """
    Fluendo H.265 (hvc1/nal) hardware decoder implementation for GStreamer 1.0
    """

    stream_format = "hvc1"
    alignment = "nal"


@register_decoder
class FluendoFluVAH265DecGst10Decoder(GStreamer10Video):
    """Fluendo H.265 separated plugin hardware decoder for GStreamer 1.0"""

    codec = Codec.H265
    decoder_bin = " h265parse ! fluhwvah265dec "
    provider = "Fluendo"
    api = "HW"
    hw_acceleration = True
    name = f"{provider}-{codec.value}-{api}-hwvah265dec-Gst1.0"


@register_decoder
class FluendoFluVAH264DecGst10Decoder(GStreamer10Video):
    """Fluendo H.264 separated plugin hardware decoder for GStreamer 1.0"""

    codec = Codec.H264
    decoder_bin = " h264parse ! fluhwvah264dec "
    provider = "Fluendo"
    api = "HW"
    hw_acceleration = True
    name = f"{provider}-{codec.value}-{api}-hwvah264dec-Gst1.0"


@register_decoder
class FluendoFluAACDecGst10Decoder(GStreamer10Audio):
    """Fluendo AAC plugin decoder for GStreamer 1.0"""

    def __init__(self) -> None:
        self.codec = Codec.AAC
        self.decoder_bin = "fluaacdec trim=0"
        self.provider = "Fluendo"
        self.api = "SW"
        self.caps = self.caps + ",format=S16LE"
        super().__init__()
