"""
    Flowblade Movie Editor is a nonlinear video editor.
    Copyright 2012 Janne Liljeblad.

    This file is part of Flowblade Movie Editor <https://github.com/jliljebl/flowblade/>.

    Flowblade Movie Editor 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, or
    (at your option) any later version.

    Flowblade Movie Editor 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 Flowblade Movie Editor. If not, see <http://www.gnu.org/licenses/>.
"""

try:
    import pgi
    pgi.install_as_gi()
except ImportError:
    pass
    
import gi

gi.require_version('Gtk', '3.0')

try:
    import mlt7 as mlt
except:
    import mlt
import os
import threading
import time

import appconsts
import ccrutils
import editorstate
import editorpersistance
import gmicplayer
import mltinit
import mltprofiles
import renderconsumer
import respaths
import toolsencoding
import userfolders
import utils


CLIP_FRAMES_DIR = appconsts.CC_CLIP_FRAMES_DIR
RENDERED_FRAMES_DIR = appconsts.CC_RENDERED_FRAMES_DIR

COMPLETED_MSG_FILE = ccrutils.COMPLETED_MSG_FILE
STATUS_MSG_FILE = ccrutils.STATUS_MSG_FILE
ABORT_MSG_FILE = ccrutils.ABORT_MSG_FILE
RENDER_DATA_FILE = ccrutils.RENDER_DATA_FILE

_render_thread = None


# ----------------------------------------------------- module interface to render process with message files, used by main app
# We are using message files to communicate with application.
def clear_flag_files(parent_folder, session_id):
    ccrutils.clear_flag_files(parent_folder, session_id)
    
def set_render_data(parent_folder, session_id, video_render_data):
    ccrutils.set_render_data(parent_folder, session_id, video_render_data)
    
def session_render_complete(parent_folder, session_id):
    return ccrutils.session_render_complete(parent_folder, session_id)

def get_session_status(parent_folder, session_id):
    msg = ccrutils.get_session_status_message(parent_folder, session_id)
    if msg == None:
        return None
    step, frame, length, elapsed = msg.split(" ")
    return (step, frame, length, elapsed)
    
def abort_render(parent_folder, session_id):
    ccrutils.abort_render(parent_folder, session_id)


# --------------------------------------------------- render process
def main(root_path, session_id, parent_folder, script, clip_path, range_in, range_out, profile_desc, gmic_frame_offset):
    
    os.nice(10) # make user configurable

    try:
        editorstate.mlt_version = mlt.LIBMLT_VERSION
    except:
        editorstate.mlt_version = "0.0.99" # magic string for "not found"

    # Set paths.
    respaths.set_paths(root_path)

    if os.path.exists("/usr/bin/gmic") == True: # distro install an dev.
        editorstate.gmic_path = "/usr/bin/gmic"
    elif os.path.exists("/app/bin/gmic") == True: # Flatpak
        editorstate.gmic_path = "/app/bin/gmic"
    else:
        print("No G'Mic in gmicheadless main(), something is wrong.") # Should not be possible

    userfolders.init()
    editorpersistance.load()

    repo = mltinit.init_with_translations()
    
    ccrutils.init_session_folders(parent_folder, session_id)
    
    ccrutils.load_render_data()
    render_data = ccrutils.get_render_data()
    
    # This needs to have render data loaded to know if we are using external folders.
    ccrutils.maybe_init_external_session_folders()

    global _render_thread
    _render_thread = GMicHeadlessRunnerThread(script, render_data, clip_path, range_in, range_out, profile_desc, gmic_frame_offset)
    _render_thread.start()


class GMicHeadlessRunnerThread(threading.Thread):

    def __init__(self, script, render_data, clip_path, range_in, range_out, profile_desc, gmic_frame_offset):
        threading.Thread.__init__(self)

        self.script_path = script
        self.render_data = render_data # toolsencoding.ToolsRenderData object
        self.clip_path = clip_path
        self.range_in = int(range_in)
        self.range_out = int(range_out)
        self.length = self.range_out - self.range_in + 1
        self.profile_desc = profile_desc
        self.gmic_frame_offset = int(gmic_frame_offset) # Note this not used currently can't MLT to find frame seq if not starting from 0001
    
        self.abort = False
        
    def run(self):
        self.start_time = time.monotonic()
        
        self.render_player = None
        self.frames_range_writer = None
        
        self.script_renderer = None
       
        if self.render_data.save_internally == True:
            frame_name = "frame"            
        else:
            frame_name = self.render_data.frame_name

        clip_frames_folder = ccrutils.clip_frames_folder()
        rendered_frames_folder = ccrutils.rendered_frames_folder()

        profile = mltprofiles.get_profile(self.profile_desc)

        # Delete old clip frames
        for frame_file in os.listdir(clip_frames_folder):
            file_path = os.path.join(clip_frames_folder, frame_file)
            os.remove(file_path)

        # Delete old rendered frames
        for frame_file in os.listdir(rendered_frames_folder):
            file_path = os.path.join(rendered_frames_folder, frame_file)
            os.remove(file_path)
            
        self.frames_range_writer = gmicplayer.FramesRangeWriter(self.clip_path, self.frames_update, profile)
        self.frames_range_writer.write_frames(clip_frames_folder + "/", frame_name, self.range_in, self.range_out)

        if self.abort == True:
            return

        script_file = open(self.script_path)
        user_script = script_file.read()

        while len(os.listdir(clip_frames_folder)) != self.length:
            time.sleep(0.5)
        
        # Render frames with gmic script
        self.script_renderer = gmicplayer.FolderFramesScriptRenderer(   user_script, 
                                                                        clip_frames_folder,
                                                                        rendered_frames_folder + "/",
                                                                        frame_name,
                                                                        self.script_render_update_callback, 
                                                                        self.script_render_output_callback,
                                                                        10,
                                                                        False,  # this is not useful until we get MLT to find frames sequences not starting from 0001
                                                                        0)
        self.script_renderer.write_frames()

        ccrutils.delete_clip_frames()

        if self.abort == True:
            return
            
        # Render video
        if self.render_data.do_video_render == True:
            # Render consumer
            args_vals_list = toolsencoding.get_args_vals_list_for_render_data(self.render_data)
            profile = mltprofiles.get_profile_for_index(self.render_data.profile_index) 
            
            if self.render_data.save_internally == True:
                file_path = ccrutils.session_folder_saved_global() + "/" + appconsts.CONTAINER_CLIP_VIDEO_CLIP_NAME + self.render_data.file_extension
            else:
                file_path = self.render_data.render_dir +  "/" + self.render_data.file_name + self.render_data.file_extension
        
            consumer = renderconsumer.get_mlt_render_consumer(file_path, profile, args_vals_list)
            
            # Render producer
            frame_file = rendered_frames_folder + "/" + frame_name + "_0000.png"
            resource_name_str = utils.get_img_seq_resource_name(frame_file)

            resource_path = rendered_frames_folder + "/" + resource_name_str
            producer = mlt.Producer(profile, str(resource_path))

            frames_length = len(os.listdir(rendered_frames_folder))

            self.render_player = renderconsumer.FileRenderPlayer("", producer, consumer, 0, frames_length - 1)
            self.render_player.wait_for_producer_end_stop = False
            self.render_player.start()

            while self.render_player.stopped == False:
                
                self.abort_requested()
                
                if self.abort == True:
                    self.render_player.shutdown()
                    return
                
                fraction = self.render_player.get_render_fraction()
                self.video_render_update_callback(fraction)
                
                time.sleep(0.3)
            
            ccrutils.delete_rendered_frames()
            
        # Write out completed flag file.
        ccrutils.write_completed_message()

    def abort_requested(self):
        self.abort = ccrutils.abort_requested()
        return self.abort

    def frames_update(self, frame):
        if self.abort_requested() == True:
             self.frames_range_writer.shutdown()
             return
             
        # step 1, frame , range
        elapsed = time.monotonic() - self.start_time
        msg = "1 " + str(frame) + " " + str(self.length) + " " + str(elapsed)
        self.write_status_message(msg)
        
    def script_render_update_callback(self, frame_count):
        if self.abort_requested() == True:
             self.script_renderer.abort = True
             return
        
        # step 1, frame , range
        elapsed = time.monotonic() - self.start_time
        msg = "2 " + str(frame_count) + " " + str(self.length) + " " + str(elapsed)
        self.write_status_message(msg)

    def video_render_update_callback(self, fraction):
        # step 1, frame , range
        elapsed = time.monotonic() - self.start_time
        msg = "3 " + str(int(fraction * self.length)) + " " + str(self.length) + " " + str(elapsed)
        self.write_status_message(msg)
        
    def write_status_message(self, msg):
        ccrutils.write_status_message(msg)
            
    def script_render_output_callback(self, p, out):
        if p.returncode != 0:
            # TODO: handling errors.
            pass
