#!/usr/bin/env python3

# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
#
# This file is part of DeVeDe-NG
#
# DeVeDe-NG is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License.
#
# DeVeDe-NG 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

import subprocess
import os
import devedeng.configuration_data
import devedeng.executor
import devedeng.mux_dvd_menu
import devedeng.project


class ffmpeg(devedeng.executor.executor):

    supports_analize = False
    supports_play = False
    supports_convert = True
    supports_menu = True
    supports_mkiso = False
    supports_burn = False
    display_name = "FFMPEG"
    disc_types = []

    @staticmethod
    def check_is_installed():
        try:
            handle = subprocess.Popen(
                ["ffmpeg", "-codecs"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (stdout, stderr) = handle.communicate()
            if 0 == handle.wait():
                mp2 = False
                mp3 = False
                ac3 = False
                mpeg1 = False
                mpeg2 = False
                divx = False
                h264 = False
                hevc = False
                for line in stdout.decode("latin-1").split("\n"):
                    parts = line.strip().split(" ")
                    if len(parts) < 2:
                        continue
                    if len(parts[0]) != 6:
                        continue
                    capabilities = parts[0]
                    codec = parts[1]

                    if capabilities[1] != 'E':
                        continue

                    if (codec == "mpeg1video"):
                        mpeg1 = True
                        continue
                    if (codec == "mpeg2video"):
                        mpeg2 = True
                        continue
                    if (codec == "mp2"):
                        mp2 = True
                        continue
                    if (codec == "mp3"):
                        mp3 = True
                        continue
                    if (codec == "ac3"):
                        ac3 = True
                        continue
                    if (codec == "h264") or (codec == "H264"):
                        h264 = True
                        continue
                    if (codec == "hevc") or (codec == "HEVC"):
                        hevc = True
                        continue
                    if (codec == "mpeg4"):
                        divx = True
                        continue

                if (mpeg1 and mp2):
                    devedeng.ffmpeg.ffmpeg.disc_types.append("vcd")
                if (mpeg2 and mp2):
                    devedeng.ffmpeg.ffmpeg.disc_types.append("svcd")
                    devedeng.ffmpeg.ffmpeg.disc_types.append("cvd")
                if (mpeg2 and mp2 and ac3):
                    devedeng.ffmpeg.ffmpeg.disc_types.append("dvd")
                if (divx and mp3):
                    devedeng.ffmpeg.ffmpeg.disc_types.append("divx")
                if (h264 and mp3):
                    devedeng.ffmpeg.ffmpeg.disc_types.append("mkv")
                if (hevc and mp3):
                    devedeng.ffmpeg.ffmpeg.disc_types.append("hevc")

                return True
            else:
                return False
        except:
            return False

    def __init__(self):

        devedeng.executor.executor.__init__(self)
        self.config = devedeng.configuration_data.configuration.get_config()
        try:
            version_number = 0
            handle = subprocess.Popen(
                ["ffmpeg", "-version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (stdout, stderr) = handle.communicate()
            if 0 == handle.wait():
                for line in stdout.decode("latin-1").split("\n"):
                    line = line.strip()
                    if line.startswith("ffmpeg version "):
                        version_number = int(line[15:].split(".")[0])
        except:
            pass
        print(version_number)
        if version_number <= 6:
            self._filter_sep = ",fifo,"
        else:
            self._filter_sep = ","


    def convert_file(self, file_project, output_file, video_length, pass2=False):

        if file_project.two_pass_encoding:
            if pass2:
                self.text = _("Converting %(X)s (pass 2)") % {
                    "X": file_project.title_name}
            else:
                self.text = _("Converting %(X)s (pass 1)") % {
                    "X": file_project.title_name}
                # Prepare the converting process for the second pass
                tmp = devedeng.ffmpeg.ffmpeg()
                tmp.convert_file(file_project, output_file, video_length, True)
                # it deppends of this process
                tmp.add_dependency(self)
                # add it as a child process of this one
                self.add_child_process(tmp)
        else:
            self.text = _("Converting %(X)s") % {"X": file_project.title_name}

        if (pass2 == False) and (file_project.two_pass_encoding == True):
            # this is the first pass in a 2-pass codification
            second_pass = False
        else:
            # second_pass is TRUE in the second pass of a 2-pass codification, and also when not doing 2-pass codification
            # It is used to remove unnecessary steps during the first pass, but
            # that are needed on the second pass, or when not using 2-pass
            # codification
            second_pass = True

        if (video_length == 0):
            self.final_length = file_project.original_length
        else:
            self.final_length = video_length
        self.command_var = []
        self.command_var.append("ffmpeg")
        self.command_var.append("-i")
        self.command_var.append(file_project.file_name)

        if (file_project.volume != 100) and second_pass:
            self.command_var.append("-vol")
            self.command_var.append(
                str(int((256 * file_project.volume) / 100)))

        if (file_project.audio_delay != 0.0) and (file_project.copy_sound == False) and (file_project.no_reencode_audio_video == False) and second_pass:
            self.command_var.append("-itsoffset")
            self.command_var.append(str(file_project.audio_delay))

        self.command_var.append("-i")
        self.command_var.append(file_project.file_name)
        self.command_var.append("-map")
        self.command_var.append("1:" + str(file_project.video_list[0]))
        if (not file_project.copy_sound) and (not file_project.no_reencode_audio_video):
            for l in file_project.audio_list:
                self.command_var.append("-map")
                self.command_var.append("0:" + str(l))

        if (file_project.no_reencode_audio_video == False) and second_pass:
            cmd_line = ""

            if file_project.deinterlace == "deinterlace_yadif":
                cmd_line += "yadif"

            vflip = False
            hflip = False

            if (file_project.prerotation == 90):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                cmd_line += "transpose=1"
            elif (file_project.prerotation == -90) or (file_project.prerotation == 270):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                cmd_line += "transpose=2"
            elif (file_project.prerotation == 180):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                vflip = not vflip
                hflip = not hflip

            if (file_project.rotation == "rotation_90"):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                cmd_line += "transpose=1"
            elif (file_project.rotation == "rotation_270"):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                cmd_line += "transpose=2"
            elif (file_project.rotation == "rotation_180"):
                vflip = not vflip
                hflip = not hflip

            if (file_project.mirror_vertical):
                vflip = not vflip
            if (file_project.mirror_horizontal):
                hflip = not hflip

            if (vflip):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                cmd_line += "vflip"
            if (hflip):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                cmd_line += "hflip"

            if (file_project.width_midle != file_project.original_width) or (file_project.height_midle != file_project.original_height):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                x = int((file_project.width_midle - file_project.original_width) / 2)
                y = int((file_project.height_midle - file_project.original_height) / 2)
                if (x > 0) or (y > 0):
                    cmd_line += "pad=" + str(file_project.width_midle) + ":" + str(file_project.height_midle) + ":" + str(x) + ":" + str(y) + ":0x000000"
                else:
                    cmd_line += "crop=" + str(file_project.width_midle) + ":" + str(file_project.height_midle) + ":" + str(x) + ":" + str(y)

            if (file_project.width_final != file_project.width_midle) or (file_project.height_final != file_project.height_midle):
                if (cmd_line != ""):
                    cmd_line += self._filter_sep
                cmd_line += "scale=" + \
                    str(file_project.width_final) + ":" + \
                    str(file_project.height_final)

            if cmd_line != "":
                self.command_var.append("-vf")
                self.command_var.append(cmd_line)

        self.command_var.append("-y")

        vcd = False

        maxrate = int(file_project.video_rate_final * 1500)
        minrate = int(file_project.video_rate_final * 666.66)

        if (self.config.disc_type == "divx"):
            self.command_var.append("-vcodec")
            self.command_var.append("mpeg4")
            self.command_var.append("-acodec")
            self.command_var.append("libmp3lame")
            self.command_var.append("-f")
            self.command_var.append("avi")
            if not pass2:
                self.command_var.append("-maxrate:v")
                self.command_var.append(str(maxrate))
                self.command_var.append("-minrate:v")
                if minrate < (devedeng.project.devede_project.divx_min_bps * 1000):
                    minrate = devedeng.project.devede_project.divx_min_bps * 1000
                self.command_var.append(str(minrate))
        elif (self.config.disc_type == "mkv") or (self.config.disc_type == "hevc"):
            self.command_var.append("-vcodec")
            self.command_var.append("h264" if self.config.disc_type == "mkv" else "hevc")
            self.command_var.append("-acodec")
            self.command_var.append("libmp3lame")
            self.command_var.append("-f")
            self.command_var.append("matroska")
            if not pass2:
                self.command_var.append("-maxrate:v")
                self.command_var.append(str(maxrate))
                self.command_var.append("-minrate:v")
                if minrate < (devedeng.project.devede_project.mkv_min_bps * 1000):
                    minrate = devedeng.project.devede_project.mkv_min_bps * 1000
                self.command_var.append(str(minrate))
        elif (self.config.disc_type == "dvd"):
            if not file_project.no_reencode_audio_video:
                self.command_var.append("-c:v")
                self.command_var.append("mpeg2video")
                if not file_project.copy_sound:
                    if file_project.sound5_1:
                        self.command_var.append("-c:a")
                        self.command_var.append("ac3")
                    else:
                        self.command_var.append("-c:a")
                        if file_project.format_pal:
                            self.command_var.append("ac3")
                        else:
                            self.command_var.append("mp2")
            self.command_var.append("-f")
            self.command_var.append("dvd")
            self.command_var.append("-r")
            if file_project.format_pal:
                self.command_var.append("25")
            else:
                if (file_project.original_fps == 24):
                    self.command_var.append("24000/1001")
                else:
                    self.command_var.append("30000/1001")
            self.command_var.append("-pix_fmt")
            self.command_var.append("yuv420p")
            self.command_var.append("-maxrate:v")
            if maxrate > (devedeng.project.devede_project.dvd_max_bps_base[0] * 1000):
                maxrate = devedeng.project.devede_project.dvd_max_bps_base[0] * 1000
            self.command_var.append(str(maxrate))
            if not pass2:
                self.command_var.append("-minrate:v")
                if minrate < (devedeng.project.devede_project.dvd_min_bps * 1000):
                    minrate = devedeng.project.devede_project.dvd_min_bps * 1000
                self.command_var.append(str(minrate))
            self.command_var.append("-bufsize")
            self.command_var.append("1835008")
            self.command_var.append("-packetsize")
            self.command_var.append("2048")
            self.command_var.append("-muxrate")
            self.command_var.append("10080000")
            self.command_var.append("-ar")
            self.command_var.append("48000")
        elif (self.config.disc_type == "vcd"):
            vcd = True
            if not file_project.no_reencode_audio_video:
                self.command_var.append("-c:v")
                self.command_var.append("mpeg1video")
                if not file_project.copy_sound:
                    self.command_var.append("-c:a")
                    self.command_var.append("mp2")
            self.command_var.append("-b:v")
            self.command_var.append("1150000")
            self.command_var.append("-b:a")
            self.command_var.append("224000")
            self.command_var.append("-f")
            self.command_var.append("vcd")
            self.command_var.append("-r")
            if file_project.format_pal:
                self.command_var.append("25")
            else:
                if (file_project.original_fps == 24):
                    self.command_var.append("24000/1001")
                else:
                    self.command_var.append("30000/1001")
            self.command_var.append("-g")
            if file_project.format_pal:
                self.command_var.append("15")
            else:
                self.command_var.append("18")
            self.command_var.append("-s")
            if file_project.format_pal:
                self.command_var.append("352x288")
            else:
                self.command_var.append("352x240")
            self.command_var.append("-maxrate:v")
            self.command_var.append("1150000")
            self.command_var.append("-minrate:v")
            self.command_var.append("1150000")
            self.command_var.append("-bufsize")
            self.command_var.append("327680")
            self.command_var.append("-packetsize")
            self.command_var.append("2324")
            self.command_var.append("-muxrate")
            self.command_var.append("1411200")
            self.command_var.append("-ar")
            self.command_var.append("44100")
        elif (self.config.disc_type == "svcd") or (self.config.disc_type == "cvd"):
            if not file_project.no_reencode_audio_video:
                self.command_var.append("-c:v")
                self.command_var.append("mpeg2video")
                if not file_project.copy_sound:
                    self.command_var.append("-c:a")
                    self.command_var.append("mp2")
            self.command_var.append("-f")
            self.command_var.append("svcd")
            self.command_var.append("-r")
            if file_project.format_pal:
                self.command_var.append("25")
            else:
                if (file_project.original_fps == 24):
                    self.command_var.append("24000/1001")
                else:
                    self.command_var.append("30000/1001")
            self.command_var.append("-g")
            if file_project.format_pal:
                self.command_var.append("12")
            else:
                self.command_var.append("15")
            self.command_var.append("-s")
            if self.config.disc_type == "cvd":
                if file_project.format_pal:
                    self.command_var.append("352x576")
                else:
                    self.command_var.append("352x480")
            else:
                if file_project.format_pal:
                    self.command_var.append("480x576")
                else:
                    self.command_var.append("480x480")
            self.command_var.append("-pix_fmt")
            self.command_var.append("yuv420p")
            self.command_var.append("-maxrate:v")
            if maxrate > (devedeng.project.devede_project.svcd_max_bps_base[0] * 1000):
                maxrate = devedeng.project.devede_project.svcd_max_bps_base[0] * 1000
            self.command_var.append(str(maxrate))
            if not pass2:
                self.command_var.append("-minrate:v")
                if minrate < (devedeng.project.devede_project.svcd_min_bps * 1000):
                    minrate = (devedeng.project.devede_project.svcd_min_bps * 1000)
                self.command_var.append(str(minrate))
            self.command_var.append("-bufsize")
            self.command_var.append("1835008")
            self.command_var.append("-packetsize")
            self.command_var.append("2324")
            self.command_var.append("-ar")
            self.command_var.append("44100")
            self.command_var.append("-scan_offset")
            self.command_var.append("1")

        if (not file_project.no_reencode_audio_video):
            self.command_var.append("-sn")  # no subtitles

        if file_project.copy_sound or file_project.no_reencode_audio_video:
            self.command_var.append("-c:a")
            self.command_var.append("copy")

        if file_project.no_reencode_audio_video:
            self.command_var.append("-c:v")
            self.command_var.append("copy")

        if (not vcd):
            if not file_project.format_pal:
                if (file_project.original_fps == 24) and ((self.config.disc_type == "dvd")):
                    keyintv = 15
                else:
                    keyintv = 18
            else:
                keyintv = 15

            if (not file_project.gop12) and (not(self.config.disc_type == "divx")) and (not(self.config.disc_type == "mkv") and (not(self.config.disc_type == "hevc"))):
                self.command_var.append("-g")
                self.command_var.append(str(keyintv))

            if (self.config.disc_type == "divx") or (self.config.disc_type == "mkv") or (self.config.disc_type == "hevc"):
                self.command_var.append("-g")
                self.command_var.append("300")
            elif file_project.gop12 and (not file_project.no_reencode_audio_video):
                self.command_var.append("-g")
                self.command_var.append("12")

        self.command_var.append("-bf")
        self.command_var.append("2")
        self.command_var.append("-strict")
        self.command_var.append("1")

        if video_length != 0:
            self.command_var.append("-t")
            self.command_var.append(str(video_length))

        self.command_var.append("-ac")
        if (file_project.sound5_1) and ((self.config.disc_type == "dvd") or (self.config.disc_type == "divx") or (self.config.disc_type == "mkv") or (self.config.disc_type == "hevc")):
            self.command_var.append("6")
        else:
            self.command_var.append("2")

        self.command_var.append("-aspect")
        self.command_var.append(str(file_project.aspect_ratio_final))

        if self.config.disc_type == "divx":
            self.command_var.append("-vtag")
            self.command_var.append("DX50")

        if (file_project.deinterlace == "deinterlace_ffmpeg") and (file_project.no_reencode_audio_video == False) and second_pass:
            self.command_var.append("-deinterlace")

        if (file_project.no_reencode_audio_video == False) and (vcd == False) and second_pass:
            self.command_var.append("-s")
            self.command_var.append(
                str(file_project.width_final) + "x" + str(file_project.height_final))

        if second_pass:
            self.command_var.append("-trellis")
            self.command_var.append("1")
            self.command_var.append("-mbd")
            self.command_var.append("2")
        else:
            self.command_var.append("-trellis")
            self.command_var.append("0")
            self.command_var.append("-mbd")
            self.command_var.append("0")

        if (vcd == False) and (file_project.no_reencode_audio_video == False):
            self.command_var.append("-b:a")
            self.command_var.append(str(self._adjust_audio_bitrate(file_project.audio_rate_final)*1000))

            self.command_var.append("-b:v")
            self.command_var.append(str(file_project.video_rate_final*1000))

        if file_project.two_pass_encoding == True:
            self.command_var.append("-passlogfile")
            self.command_var.append(output_file + ".passlog")
            self.command_var.append("-pass")
            if pass2:
                self.command_var.append("2")
            else:
                self.command_var.append("1")

        self.command_var.append(output_file)
        print(self.command_var)

    def _adjust_audio_bitrate(self, bitrate):
        br = [32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384]
        for a in br:
            if a >= bitrate:
                return a
        return 384

    def create_menu_mpeg(self, n_page, background_music, sound_length, pal, video_rate, audio_rate, output_path, use_mp2, widescreen):

        self.n_page = n_page
        self.final_length = float(sound_length)
        self.text = ("Creating menu %(X)d") % {"X": self.n_page}

        self.command_var = []
        self.command_var.append("ffmpeg")

        self.command_var.append("-loop")
        self.command_var.append("1")

        self.command_var.append("-f")
        self.command_var.append("image2")
        self.command_var.append("-i")
        self.command_var.append(os.path.join(
            output_path, "menu_" + str(n_page) + "_bg.png"))
        self.command_var.append("-i")
        self.command_var.append(background_music)

        self.command_var.append("-y")
        self.command_var.append("-target")
        if pal:
            self.command_var.append("pal-dvd")
        else:
            self.command_var.append("ntsc-dvd")
        self.command_var.append("-acodec")
        if use_mp2:
            self.command_var.append("mp2")
            if (audio_rate > 384):
                audio_rate = 384  # max bitrate for mp2
        else:
            self.command_var.append("ac3")
        self.command_var.append("-s")
        if pal:
            self.command_var.append("720x576")
        else:
            self.command_var.append("720x480")
        self.command_var.append("-g")
        self.command_var.append("12")
        self.command_var.append("-b:v")
        self.command_var.append(str(video_rate) + "k")
        self.command_var.append("-b:a")
        self.command_var.append(str(self._adjust_audio_bitrate(audio_rate)) + "k")
        self.command_var.append("-aspect")
        if widescreen:
            self.command_var.append("16:9")
        else:
            self.command_var.append("4:3")

        self.command_var.append("-t")
        self.command_var.append(str(1 + sound_length))

        movie_path = os.path.join(output_path, "menu_" + str(n_page) + ".mpg")
        self.command_var.append(movie_path)

        muxer = devedeng.mux_dvd_menu.mux_dvd_menu()
        final_path = muxer.create_mpg(n_page, output_path, movie_path)
        # the muxer process depends of the converter process
        muxer.add_dependency(self)
        self.add_child_process(muxer)

        return (final_path)

    def process_stdout(self, data):

        return

    def process_stderr(self, data):

        pos = data[0].find("time=")
        if (pos != -1):
            pos += 5
            pos2 = data[0].find(" ", pos)
            if (pos2 != -1):
                parts = data[0][pos:pos2].split(":")
                t = 0.0
                for e in parts:
                    try:
                        v = float(e)
                    except:
                        continue
                    t = t * 60.0 + v
                t /= self.final_length
                self.progress_bar[1].set_fraction(t)
                self.progress_bar[1].set_text("%.1f%%" % (100.0 * t))
