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

    This file is part of Flowblade Movie Editor <http://code.google.com/p/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/>.
"""

from gi.repository import Gtk
from gi.repository import GObject

import os

import appconsts
import guiutils
import mltprofiles
import renderconsumer
import utils



# NOTE: 
# On module globals:
# - when used by gmic.py for GMic Tool we have no problem with e.g widgets being module global
#   because gmic.py runs in own process.
# - when this module is used in main application process it is important that e.g.widgets object 
#   is created each time module is used to set rendering values for e.g. container clips.
# - when used by a rendering process like gmicheadless.py we are again good to use module freely,
#   G'Mic tool process controls values in this module fully.
widgets = None
disable_audio_encoding = False
default_profile_index = None


class ToolsRenderData():
    """
    This is used to save and communicate render selections defined by user
    for renders other the main application timeline render such as G'Mic tool and Container 
    clips renders.
    """
    def __init__(self):
        self.profile_index = None
        self.use_default_profile = None # NOT USED, 'profile_index' is the meaningful data here, this one should not have been included in this data struct.
        self.use_preset_encodings = None
        self.presets_index = None
        self.encoding_option_index = None
        self.quality_option_index = None
        self.render_dir = None
        self.file_name = None
        self.file_extension = None
        
        # Used by container clips only.
        self.do_video_render = True
        self.save_internally = True
        self.frame_name = "frame"
        self.is_preview_render = False
        self.is_flatpak_render = False
    
    
    
def get_args_vals_list_for_render_data(render_data):
    profile = mltprofiles.get_profile_for_index(render_data.profile_index)

    args_vals_list = renderconsumer.get_args_vals_tuples_list_for_encoding_and_quality( profile, 
                                                                                        render_data.encoding_option_index, 
                                                                                        render_data.quality_option_index)
    # sample rate not supported
    # args rendering not supported

    return args_vals_list


def create_container_clip_default_render_data_object(profile):
    # When ToolsRenderData is used by G'Mic tool we need to have default values be 'None', for container clis we need different
    # default values.
    # 
    # When first render is attempted this created to have data availeble for render process
    # even if user has not set any values.
    render_data = ToolsRenderData()
    render_data.profile_index = mltprofiles.get_profile_index_for_profile(profile)
    render_data.use_default_profile = True
    render_data.use_preset_encodings = False
    render_data.presets_index = 0
    render_data.encoding_option_index = 0
    render_data.quality_option_index = 10
    render_data.render_dir = os.path.expanduser("~")
    render_data.file_name = appconsts.CONTAINER_CLIP_VIDEO_CLIP_NAME
    render_data.file_extension = ".mp4"

    return render_data
    
# ------------------------------------------------------------ GUI interface
def create_widgets(def_profile_index, disable_audio=True, create_container_file_panel=False):
    """
    Widgets for editing render properties and viewing render progress.
    """
    global widgets, disable_audio_encoding, default_profile_index
    default_profile_index = def_profile_index
    if disable_audio:
        disable_audio_encoding = True

    widgets = utils.EmptyClass()
     
    widgets.file_panel = RenderFilePanel(create_container_file_panel)
    widgets.profile_panel = RenderProfilePanel(_out_profile_changed)
    widgets.encoding_panel = RenderEncodingPanel(widgets.file_panel.extension_label)

    widgets.profile_panel.out_profile_combo.set_initial_selection()
    _display_default_profile()
    
def get_encoding_panel(render_data, create_container_file_panel=False):
    # We are making two kinds of panels here:
    # - panel for G'Mic tool
    # - panels for Clip Containers render settings

    if create_container_file_panel == False:
        file_panel_title = _("File")
    else:
        file_panel_title = _("Save Location")

    file_opts_panel = guiutils.get_named_frame(file_panel_title, widgets.file_panel.vbox, 4)
    if create_container_file_panel == False:      
        profile_panel = guiutils.get_named_frame(_("Render Profile"), widgets.profile_panel.vbox, 4)
    encoding_panel = guiutils.get_named_frame(_("Encoding Format"), widgets.encoding_panel.vbox, 4)

    if create_container_file_panel == True:
        widgets.video_clip_panel = RenderVideoClipPanel(encoding_panel)
        video_clip_panel = guiutils.get_named_frame(_("Video Clip"), widgets.video_clip_panel.vbox, 4)
    else:
        widgets.video_clip_panel = None
        
    render_panel = Gtk.VBox()
    render_panel.pack_start(file_opts_panel, False, False, 0)
    if create_container_file_panel == False:
        render_panel.pack_start(profile_panel, False, False, 0)
        render_panel.pack_start(encoding_panel, False, False, 0)
    else:
        render_panel.pack_start(video_clip_panel, False, False, 0)

    if render_data != None:
        widgets.file_panel.movie_name.set_text(render_data.file_name)
        widgets.file_panel.extension_label.set_text(render_data.file_extension)
        widgets.file_panel.out_folder.set_current_folder(render_data.render_dir + "/")
        widgets.encoding_panel.encoding_selector.categorised_combo.set_selected(mltprofiles.get_profile_for_index(render_data.encoding_option_index).description())
        widgets.encoding_panel.quality_selector.widget.set_active(render_data.quality_option_index)
        profile_desc = mltprofiles.get_profile_for_index(render_data.profile_index).description()
        widgets.profile_panel.out_profile_combo.categories_combo.set_selected(profile_desc)
        widgets.profile_panel.use_project_profile_check.set_active(render_data.use_default_profile)
        
        if create_container_file_panel == True:
            video_clip_combo_index = render_location_combo_index = 0
            if render_data.do_video_render == False:
                video_clip_combo_index = 1
            if render_data.save_internally == False:
                render_location_combo_index = 1
             
            widgets.video_clip_panel.video_clip_combo.set_active(video_clip_combo_index)
            widgets.file_panel.render_location_combo.set_active(render_location_combo_index)
            widgets.file_panel.frame_name.set_text(render_data.frame_name)
            
    return render_panel

def get_profile_info_small_box(profile):
    text = get_profile_info_text(profile)
    label = Gtk.Label(label=text)

    hbox = Gtk.HBox()
    hbox.pack_start(label, False, False, 0)
    
    return hbox

def get_profile_info_text(profile):
    str_list = []
    str_list.append(str(profile.width()))
    str_list.append(" x ")    
    str_list.append(str(profile.height()))
    str_list.append(", " + str(profile.display_aspect_num()))
    str_list.append(":")
    str_list.append(str(profile.display_aspect_den()))
    str_list.append(", ")
    if profile.progressive() == True:
        str_list.append(_("Progressive"))
    else:
        str_list.append(_("Interlaced"))
        
    str_list.append("\n")
    str_list.append(_("Fps: ") + str(profile.fps()))
    pix_asp = float(profile.sample_aspect_num()) / profile.sample_aspect_den()
    pa_str =  "%.2f" % pix_asp
    str_list.append(", " + _("Pixel Aspect: ") + pa_str)

    return ''.join(str_list)

def get_render_data_for_current_selections():
    render_data = ToolsRenderData()
    profile_desc = widgets.profile_panel.out_profile_combo.categories_combo.get_selected()
    render_data.profile_index = mltprofiles.get_profile_index_for_profile(mltprofiles.get_profile(profile_desc))
    render_data.use_default_profile = widgets.profile_panel.use_project_profile_check.get_active()
    render_data.encoding_option_index = widgets.encoding_panel.encoding_selector.get_selected_encoding_index()
    render_data.quality_option_index = widgets.encoding_panel.quality_selector.widget.get_active()
    render_data.presets_index = 0 # presents rendering not available
    render_data.use_preset_encodings = False # presents rendering not available
    render_data.render_dir = "/" + widgets.file_panel.out_folder.get_uri().lstrip("file:/")
    render_data.file_name = widgets.file_panel.movie_name.get_text()
    render_data.file_extension = widgets.file_panel.extension_label.get_text()
    
    if widgets.video_clip_panel != None:
        render_data.do_video_render = (widgets.video_clip_panel.video_clip_combo.get_active() == 0)
        render_data.save_internally = (widgets.file_panel.render_location_combo.get_active() == 0)
        render_data.frame_name = widgets.file_panel.frame_name.get_text()

    return render_data


def get_encoding_desc(args_vals_list):
    print(args_vals_list)
    vcodec = ""
    vb = ""
    for arg_val in args_vals_list:
        k, v = arg_val
        if k == "vcodec":
            vcodec = v
        if k == "vb":
            vb = v

    if vb  == "":
        vb = "lossless"

    return vcodec + ", " + vb


# ----------------------------------------------------------------- helper functions
"""
def _render_type_changed(w):
    if w.get_active() == 0: # User Defined
        widgets.render_type_panel.presets_selector.widget.set_sensitive(False)
        widgets.encoding_panel.encoding_selector.encoding_selection_changed()
    else: # Preset Encodings
        widgets.render_type_panel.presets_selector.widget.set_sensitive(True)
        _preset_selection_changed(widgets.render_type_panel.presets_selector.widget)

def _preset_selection_changed(w):
    encs = renderconsumer.non_user_encodings
    if disable_audio_encoding == True:
        encs = renderconsumer.get_video_non_user_encodigs()

    enc_index = w.get_active()
    ext = encs[enc_index].extension
    widgets.file_panel.extension_label.set_text("." + ext)
"""

def _out_profile_changed(categorized_combo):
    profile = mltprofiles.get_profile(categorized_combo.get_selected())
    _fill_info_box(profile)

def _display_default_profile():
    profile = mltprofiles.get_profile_for_index(default_profile_index)
    _fill_info_box(profile)
    
def _fill_info_box(profile):
    info_panel = get_profile_info_small_box(profile)
    widgets.info_panel = info_panel
    widgets.profile_panel.out_profile_info_box.display_info(info_panel)



# ----------------------------------------------------- PANELS
class RenderFilePanel():

    def __init__(self, create_container_file_panel):

        if create_container_file_panel == True:
            self.render_location_combo = Gtk.ComboBoxText() # filled later when current sequence known
            self.render_location_combo.append_text(_("Save Rendered Container Media Internally"))
            self.render_location_combo.append_text(_("Save Rendered Container Media Externally"))
            self.render_location_combo.set_active(0)
            self.render_location_combo.connect('changed', lambda w: self.render_location_combo_changed(w))
        else:
            self.render_location_combo = None
            
        self.out_folder = Gtk.FileChooserButton(_("Select Folder"))
        self.out_folder.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
        self.out_folder.set_current_folder(os.path.expanduser("~") + "/")
        self.out_folder.set_local_only(True)
        self.out_folder_label = Gtk.Label(label=_("Folder:"))
        out_folder_row = guiutils.get_two_column_box(self.out_folder_label, self.out_folder, 60)
                              
        self.movie_name = Gtk.Entry()
        self.movie_name.set_text("movie")
        self.extension_label = Gtk.Label()
        if create_container_file_panel == False:
            self.name_label = Gtk.Label(label=_("Name:"))
        else:
            self.name_label = Gtk.Label(label=_("Clip Name:"))
            
        name_box = Gtk.HBox(False, 8)
        name_box.pack_start(self.movie_name, True, True, 0)
        name_box.pack_start(self.extension_label, False, False, 0)
          
        movie_name_row = guiutils.get_two_column_box(self.name_label, name_box, 60)

        self.frame_name_label = Gtk.Label(label=_("Frame Name:"))
        self.frame_name = Gtk.Entry()
        self.frame_name.set_text("frame")
    
        frame_name_row = guiutils.get_two_column_box(self.frame_name_label, self.frame_name, 60)
        
        self.vbox = Gtk.VBox(False, 2)
        if self.render_location_combo != None:
            self.vbox.pack_start(self.render_location_combo, False, False, 0)
        self.vbox.pack_start(out_folder_row, False, False, 0)
        self.vbox.pack_start(movie_name_row, False, False, 0)
        if create_container_file_panel == True:
            self.vbox.pack_start(frame_name_row, False, False, 0)
            
        self.out_folder.set_tooltip_text(_("Select folder to place rendered file in"))
        self.movie_name.set_tooltip_text(_("Give name for rendered file"))

    def enable_file_selections(self, enabled):
        self.movie_name.set_sensitive(enabled)
        self.extension_label.set_sensitive(enabled)
        self.out_folder.set_sensitive(enabled)
        self.out_folder_label.set_sensitive(enabled)
        self.name_label.set_sensitive(enabled)
        self.frame_name_label.set_sensitive(enabled)
        self.frame_name.set_sensitive(enabled)

    def render_location_combo_changed(self, combo):
        if combo.get_active() == 1:
            self.enable_file_selections(True)
        else:
            self.enable_file_selections(False)


class RenderVideoClipPanel:
    def __init__(self, encoding_panel):

        self.encoding_panel = encoding_panel

        self.video_clip_combo = Gtk.ComboBoxText() # filled later when current sequence known
        self.video_clip_combo.append_text(_("Render Video Clip"))
        self.video_clip_combo.append_text(_("Frame Sequence Only"))
        self.video_clip_combo.set_active(0)
        self.video_clip_combo.connect('changed', lambda w: self.video_clip_combo_changed(w))

        encoding_vbox = Gtk.VBox(False, 2)
        encoding_vbox.pack_start(encoding_panel, False, False, 0)

        encoding_hbox = Gtk.HBox(False, 2)
        encoding_hbox.pack_start(guiutils.pad_label(12, 12), False, False, 0)
        encoding_hbox.pack_start(encoding_vbox, True, True, 0)

        
        self.vbox = Gtk.VBox(False, 2)
        self.vbox.pack_start(self.video_clip_combo, False, False, 0)
        self.vbox.pack_start(guiutils.pad_label(12, 24), False, False, 0)
        self.vbox.pack_start(encoding_hbox, False, False, 0)

    def enable_video_encoding(self, enabled):
        self.encoding_panel.set_sensitive(enabled)
        
    def video_clip_combo_changed(self, combo):
        if combo.get_active() == 0:
            self.enable_video_encoding(True)
        else:
            self.enable_video_encoding(False)


class RenderTypePanel():
    
    def __init__(self, render_type_changed_callback, preset_selection_changed_callback):
        self.type_label = Gtk.Label(label=_("Type:"))
        self.presets_label = Gtk.Label(label=_("Presets:")) 
        
        self.type_combo = Gtk.ComboBoxText() # filled later when current sequence known
        self.type_combo.append_text(_("User Defined"))
        self.type_combo.append_text(_("Preset File type"))
        self.type_combo.set_active(0)
        self.type_combo.connect('changed', lambda w: render_type_changed_callback(w))
    
        self.presets_selector = PresetEncodingsSelector(preset_selection_changed_callback)

        self.vbox = Gtk.VBox(False, 2)
        self.vbox.pack_start(guiutils.get_two_column_box(self.type_label,
                                                         self.type_combo, 80), 
                                                         False, False, 0)
        self.vbox.pack_start(guiutils.get_two_column_box(self.presets_label,
                                                         self.presets_selector.widget, 80), 
                                                         False, False, 0)


class RenderProfilePanel():

    def __init__(self, out_profile_changed_callback):
        self.use_project_label = Gtk.Label(label=_("Use Default Profile:"))

        self.out_profile_info_box = ProfileInfoBox() # filled later when current sequence known

        self.use_project_profile_check = Gtk.CheckButton()
        self.use_project_profile_check.set_active(True)
        self.use_project_profile_check.connect("toggled", self.use_project_check_toggled)

        self.out_profile_combo = ProfileSelector(out_profile_changed_callback)

        use_project_profile_row = Gtk.HBox()
        use_project_profile_row.pack_start(self.use_project_label,  False, False, 0)
        use_project_profile_row.pack_start(self.use_project_profile_check,  False, False, 0)
        use_project_profile_row.pack_start(Gtk.Label(), True, True, 0)
    
        self.vbox = Gtk.VBox(False, 2)
        self.vbox.pack_start(use_project_profile_row, False, False, 0)
        self.vbox.pack_start(self.out_profile_combo.widget, False, False, 0)
        self.vbox.pack_start(self.out_profile_info_box, False, False, 0)

    def set_sensitive(self, value):
        self.use_project_profile_check.set_sensitive(value)
        self.use_project_label.set_sensitive(value)
        self.out_profile_combo.widget.set_sensitive(value)
        
    def use_project_check_toggled(self, checkbutton):
        self.out_profile_combo.widget.set_sensitive(checkbutton.get_active() == False)
        if checkbutton.get_active() == True:
            self.out_profile_combo.widget.set_active(default_profile_index)
            

class RenderEncodingPanel():
    
    def __init__(self, extension_label):
        self.quality_selector = RenderQualitySelector()
        self.quality_selector.widget.set_size_request(110, 34)
        self.quality_selector.update_quality_selection(0)
        self.audio_desc = Gtk.Label()
        self.encoding_selector = RenderEncodingSelector(self.quality_selector,
                                                        extension_label,
                                                        self.audio_desc)
        self.encoding_selector.encoding_selection_changed()

        quality_row  = Gtk.HBox()
        quality_row.pack_start(self.quality_selector.widget, False, False, 0)
        quality_row.pack_start(Gtk.Label(), True, False, 0)

        self.vbox = Gtk.VBox(False, 2)
        self.vbox.pack_start(self.encoding_selector.widget, False, False, 0)
        self.vbox.pack_start(quality_row, False, False, 0)

    def set_sensitive(self, value):
        self.quality_selector.widget.set_sensitive(value)
        self.audio_desc.set_sensitive(value)
        self.speaker_image.set_sensitive(value)
        self.encoding_selector.widget.set_sensitive(value)



class ProfileSelector():
    def __init__(self, out_profile_changed_callback=None):
        self.categories_combo = get_profiles_combo()
        self.widget = self.categories_combo.widget
        if out_profile_changed_callback != None:
            self.widget.connect('changed', lambda w:  out_profile_changed_callback(self.categories_combo))
        self.widget.set_sensitive(False)
        self.widget.set_tooltip_text(_("Select render profile"))
        
    def set_initial_selection(self):
        default_profile = mltprofiles.get_profile_for_index(default_profile_index)
        self.categories_combo.set_selected(default_profile.description())



class RenderQualitySelector():
    """
    Component displays quality option relevant for encoding slection.
    """
    def __init__(self):
        self.widget = Gtk.ComboBoxText()
        self.widget.set_tooltip_text(_("Select Render quality"))

    def update_quality_selection(self, enc_index):
        encoding = renderconsumer.encoding_options[enc_index]
        
        self.widget.get_model().clear()
        for quality_option in encoding.quality_options:
            self.widget.append_text(quality_option.name)

        if encoding.quality_default_index != None:
            self.widget.set_active(encoding.quality_default_index)
        else:
            self.widget.set_active(0)


class PresetEncodingsSelector():
    
     def __init__(self, selection_changed_callback):
        self.widget = Gtk.ComboBoxText()
        encs = renderconsumer.non_user_encodings
        
        if disable_audio_encoding == True:
            encs = renderconsumer.get_video_non_user_encodigs()
        
        for encoding in encs:
            self.widget.append_text(encoding.name)
        
        self.widget.set_active(0)
        self.widget.set_sensitive(False)
        self.widget.connect("changed", 
                             lambda w,e: selection_changed_callback(w), 
                             None)


class ProfileInfoBox(Gtk.VBox):
    def __init__(self):
        GObject.GObject.__init__(self)
        self.add(Gtk.Label()) # This is removed when we have data to fill this
        
    def display_info(self, info_panel):
        info_box_children = self.get_children()
        for child in info_box_children:
            self.remove(child)
    
        self.add(info_panel)
        self.show_all()


class RenderEncodingSelector():

    def __init__(self, quality_selector, extension_label, audio_desc_label):
        self.categorised_combo = get_encodings_combo()
        self.widget = self.categorised_combo.widget
        self.categorised_combo.set_selected(renderconsumer.DEFAULT_ENCODING_NAME)
        self.widget.connect("changed", 
                            lambda w,e: self.encoding_selection_changed(), 
                            None)
        self.widget.set_tooltip_text(_("Select Render encoding"))
    
        self.quality_selector = quality_selector
        self.extension_label = extension_label
        self.audio_desc_label = audio_desc_label
        
    def encoding_selection_changed(self):
        try:
            name, encoding = self.categorised_combo.get_selected()
            enc_index = renderconsumer.get_encoding_index(encoding)
            self.quality_selector.update_quality_selection(enc_index)
            
            self.extension_label.set_text("." + encoding.extension)

            if self.audio_desc_label != None:
                self.audio_desc_label.set_markup(encoding.get_audio_description())
        except:
            pass # this gets called too early on start-up

    def get_selected_encoding_index(self):
        name, encoding = self.categorised_combo.get_selected()
        return renderconsumer.get_encoding_index(encoding)
        


def get_profiles_combo():
    return CategoriesModelComboBox(mltprofiles._categorized_profiles)


class CategoriesModelComboBox:
    
    def __init__(self, categories_list):
        self.categories_list = categories_list # categories_list is list of form [("category_name", [category_items]), ...]
                                               # with category_items list of form ["item_name", ...]
        self.model = Gtk.TreeStore.new([str])
        
        for i in range(0, len(categories_list)):
            name, items = categories_list[i]
            self.model.append(None, [name])
            for item_name in items:
                category_iter = self.model.get_iter_from_string(str(i))
                self.model.append(category_iter, [item_name])

        self.widget = Gtk.ComboBox.new_with_model(self.model)
        renderer_text = Gtk.CellRendererText()
        self.widget.pack_start(renderer_text, True)
        self.widget.add_attribute(renderer_text, "text", 0)

    def set_selected(self, active_item_name):
        for i in range(0, len(self.categories_list)):
            name, items = self.categories_list[i]
            for j in range(0, len(items)):
                if items[j] == active_item_name:
                    iter = self.model.get_iter_from_string(str(i) + ":" + str(j))
                    self.widget.set_active_iter(iter)
                    
    def get_selected(self):        
        indices = self.model.get_path(self.widget.get_active_iter()).get_indices()
        name, items = self.categories_list[indices[0]]
        return items[indices[1]]

def get_encodings_combo():
    return CategoriesModelComboBoxWithData(renderconsumer.categorized_encoding_options)

class CategoriesModelComboBoxWithData:
    
    def __init__(self, categories_list):
        self.categories_list = categories_list # categories_list is list of form [("category_name", [category_items]), ...]
                                               # with category_items list of form [("item_name", data_object), ...]
        self.model = Gtk.TreeStore.new([str])
        
        for i in range(0, len(categories_list)):
            name, items = categories_list[i]
            self.model.append(None, [name])
            for item in items:
                item_name, item_data = item
                category_iter = self.model.get_iter_from_string(str(i))
                self.model.append(category_iter, [item_name])

        self.widget = Gtk.ComboBox.new_with_model(self.model)
        renderer_text = Gtk.CellRendererText()
        self.widget.pack_start(renderer_text, True)
        self.widget.add_attribute(renderer_text, "text", 0)

    def set_selected(self, active_item_name):
        for i in range(0, len(self.categories_list)):
            name, items = self.categories_list[i]
            for j in range(0, len(items)):
                item_name, item_data = items[j]
                if item_name == active_item_name:
                    iter = self.model.get_iter_from_string(str(i) + ":" + str(j))
                    self.widget.set_active_iter(iter)
                    
    def get_selected(self):        
        indices = self.model.get_path(self.widget.get_active_iter()).get_indices()
        name, items = self.categories_list[indices[0]]
        return items[indices[1]]
