#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: FFMPEG Class
Author: K4YT3X
Date Created: Feb 24, 2018
Last Modified: March 19, 2019

Description: This class handles all FFMPEG related
operations.
"""
from avalon_framework import Avalon
import json
import subprocess


class Ffmpeg:
    """This class communicates with ffmpeg

    This class deals with ffmpeg. It handles extracitng
    frames, stripping audio, converting images into videos
    and inserting audio tracks to videos.
    """

    def __init__(self, ffmpeg_settings):
        self.ffmpeg_settings = ffmpeg_settings
        self._parse_settings()
    
    def _parse_settings(self):
        """ Parse ffmpeg settings
        """
        self.ffmpeg_path = self.ffmpeg_settings['ffmpeg_path']
        # add a forward slash to directory if not present
        # otherwise there will be a format error
        if self.ffmpeg_path[-1] != '/' and self.ffmpeg_path[-1] != '\\':
            self.ffmpeg_path = '{}\\'.format(self.ffmpeg_path)

        self.ffmpeg_binary = '{}ffmpeg.exe'.format(self.ffmpeg_path)
        self.ffmpeg_hwaccel = self.ffmpeg_settings['ffmpeg_hwaccel']
        self.extra_arguments = self.ffmpeg_settings['extra_arguments']

    def get_video_info(self, input_video):
        """ Gets input video information

        This method reads input video information
        using ffprobe in dictionary.

        Arguments:
            input_video {string} -- input video file path

        Returns:
            dictionary -- JSON text of input video information
        """
        execute = [
            '{}ffprobe.exe'.format(self.ffmpeg_path),
            '-v',
            'quiet',
            '-print_format',
            'json',
            '-show_format',
            '-show_streams',
            '-i',
            '{}'.format(input_video)
        ]

        Avalon.debug_info('Executing: {}'.format(' '.join(execute)))
        json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout
        return json.loads(json_str.decode('utf-8'))

    def extract_frames(self, input_video, extracted_frames):
        """Extract every frame from original videos

        This method extracts every frame from videoin
        using ffmpeg

        Arguments:
            input_video {string} -- input video path
            extracted_frames {string} -- video output folder
        """
        execute = [
            self.ffmpeg_binary,
            '-i',
            '{}'.format(input_video),
            '{}\\extracted_%0d.png'.format(extracted_frames),
            '-y'
        ]
        execute += self.extra_arguments

        Avalon.debug_info('Executing: {}'.format(' '.join(execute)))
        subprocess.run(execute, shell=True, check=True)

    def convert_video(self, framerate, resolution, upscaled_frames):
        """Converts images into videos

        This method converts a set of images into a
        video.

        Arguments:
            framerate {float} -- target video framerate
            resolution {string} -- target video resolution
            upscaled_frames {string} -- source images folder
        """
        execute = [
            self.ffmpeg_binary,
            '-r',
            str(framerate),
            '-f',
            'image2',
            '-s',
            resolution,
            '-i',
            '{}\\extracted_%d.png'.format(upscaled_frames),
            '-vcodec',
            'libx264',
            '-crf',
            '25',
            '-pix_fmt',
            'yuv420p',
            '{}\\no_audio.mp4'.format(upscaled_frames),
            '-y'
        ]
        execute += self.extra_arguments

        Avalon.debug_info('Executing: {}'.format(' '.join(execute)))
        subprocess.run(execute, shell=True, check=True)

    def migrate_audio_tracks_subtitles(self, input_video, output_video, upscaled_frames):
        """ Migrates audio tracks and subtitles from input video to output video

        Arguments:
            input_video {string} -- input video file path
            output_video {string} -- output video file path
            upscaled_frames {string} -- directory containing upscaled frames
        """
        execute = [
            self.ffmpeg_binary,
            '-i',
            '{}\\no_audio.mp4'.format(upscaled_frames),
            '-i',
            '{}'.format(input_video),
            '-map',
            '0:v:0?',
            '-map',
            '1?',
            '-c',
            'copy',
            '-map',
            '-1:v?',
            '{}'.format(output_video),
            '-y'
        ]
        execute += self.extra_arguments

        Avalon.debug_info('Executing: {}'.format(' '.join(execute)))
        subprocess.run(execute, shell=True, check=True)