# -*- coding: UTF-8 -*-

#########################################################
# Name: presets_pan.py
# Porpose: ffmpeg's presets manager panel
# Compatibility: Python3, wxPython Phoenix
# Author: Gianluca Pernigoto <jeanlucperni@gmail.com>
# Copyright: (c) 2018/2019 Gianluca Pernigoto <jeanlucperni@gmail.com>
# license: GPL3
# Rev: Dec 28 2018, Aug.28 2019
#########################################################

# This file is part of Videomass.

#    Videomass 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.

#    Videomass 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 Videomass.  If not, see <http://www.gnu.org/licenses/>.
 
#########################################################

import wx
import os
import wx.lib.agw.floatspin as FS
import wx.lib.agw.gradientbutton as GB
from videomass3.vdms_IO.presets_manager_properties import json_data
from videomass3.vdms_IO.presets_manager_properties import supported_formats
from videomass3.vdms_IO.presets_manager_properties import delete_profiles
from videomass3.vdms_UTILS.utils import copy_restore
from videomass3.vdms_UTILS.utils import copy_backup
from videomass3.vdms_UTILS.utils import copy_on
from videomass3.vdms_IO.filenames_check import inspect
from videomass3.vdms_DIALOGS import presets_addnew
from videomass3.vdms_DIALOGS.epilogue import Formula
from videomass3.vdms_IO.IO_tools import volumeDetectProcess
from videomass3.vdms_FRAMES import shownormlist


cmd_opt = {"PEAK": "", "RMS": "","EBU": "",
           "AudioInMap": ['',''], "AudioOutMap": ['','']}
array = [] # Parameters of the selected profile

# set widget colours in some case with html rappresentetion:
azure = '#15a6a6'# rgb form (wx.Colour(217,255,255))
yellow = '#a29500'
red = '#ea312d'
orange = '#f28924'
greenolive = '#8aab3c'
green = '#268826'
###################################################################

class PrstPan(wx.Panel):
    """
    Interface for using and managing presets in the FFmpeg syntax. 
    Each presets is a JSON file (Javascript object notation) which is 
    a list object with a variable number of items (called profiles)
    of type <class 'dict'>, each of which collect 5 keys object in 
    the following form:
    
    {'Name': "", 
    "Descritpion": "", 
    "First_pass": "", 
    "Second_pass": "",
    "Supported_list": "",
    "Output_extension": "",
    }
    
    """
    def __init__(self, parent, path_srcShare, path_confdir, 
                 PWD, OS, iconanalyzes, iconpeaklevel, btn_color, 
                 fontBtncolor):
        """
        
        """
        self.src_prst = os.path.join(path_srcShare, 'presets')#origin/share
        self.user_prst = os.path.join(path_confdir, 'presets')#conf/videomass
        self.PWD = PWD #current work of videomass
        self.OS = OS
        self.parent = parent
        self.txtcmdedited = False # show dlg if cmdline is edited
        self.normdetails = []
        prst = sorted([os.path.splitext(x)[0] for x in 
                      os.listdir(self.user_prst) if 
                      os.path.splitext(x)[1] == '.prst'])
        self.btnC = btn_color
        self.fBtnC = fontBtncolor

        wx.Panel.__init__(self, parent, -1) 
        """constructor"""
        sizer_base = wx.BoxSizer(wx.VERTICAL)
        self.list_ctrl = wx.ListCtrl(self, wx.ID_ANY, style=wx.LC_REPORT | 
                                                            wx.SUNKEN_BORDER
                                    )
        sizer_base.Add(self.list_ctrl, 1, wx.ALL | wx.EXPAND, 15)
        nb1 = wx.Notebook(self, wx.ID_ANY, style=0)
        sizer_base.Add(nb1, 0, wx.ALL | wx.EXPAND, 15)
        #------- page presets
        nb1_p1 = wx.Panel(nb1, wx.ID_ANY)
        grd_prst = wx.GridSizer(2, 1, 0, 0)
        lab_prfl = wx.StaticText(nb1_p1, wx.ID_ANY, _("Select a preset from "
                                                      "the drop down:"))
        grd_prst.Add(lab_prfl, 0, wx.ALIGN_CENTER_HORIZONTAL | 
                                  wx.ALIGN_CENTER_VERTICAL, 0
                                   )
        self.cmbx_prst = wx.ComboBox(nb1_p1,wx.ID_ANY, 
                                     choices=prst,
                                     size=(200,-1),
                                     style=wx.CB_DROPDOWN | 
                                           wx.CB_READONLY
                                     )
        grd_prst.Add(self.cmbx_prst, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
        nb1_p1.SetSizer(grd_prst)
        nb1.AddPage(nb1_p1, _("Presets"))
        #------- page commands
        nb1_p2 = wx.Panel(nb1, wx.ID_ANY)
        grd_cmd = wx.GridSizer(1, 2, 0, 0)
        box_cmd1 = wx.StaticBoxSizer(wx.StaticBox(nb1_p2, wx.ID_ANY,
                                                  _("1-PASS")), 
                                                    wx.VERTICAL
                                                    )
        grd_cmd.Add(box_cmd1,  0, wx.ALL | wx.EXPAND 
                                         | wx.ALIGN_CENTER_HORIZONTAL 
                                         | wx.ALIGN_CENTER_VERTICAL, 15
                    )
        self.txt_1cmd = wx.TextCtrl(nb1_p2, wx.ID_ANY,"",
                                    style=wx.TE_MULTILINE | wx.TE_PROCESS_ENTER
                                    )
        box_cmd1.Add(self.txt_1cmd, 1, wx.ALL | wx.EXPAND 
                                              | wx.ALIGN_CENTER_HORIZONTAL 
                                              | wx.ALIGN_CENTER_VERTICAL, 15
                    )
        box_cmd2 = wx.StaticBoxSizer(wx.StaticBox(nb1_p2, wx.ID_ANY, 
                                                  _("2-PASS")), 
                                                    wx.VERTICAL
                                    )
        grd_cmd.Add(box_cmd2, 0, wx.ALL | wx.EXPAND 
                                        | wx.ALIGN_CENTER_HORIZONTAL 
                                        | wx.ALIGN_CENTER_VERTICAL, 15
                    )
        self.txt_2cmd = wx.TextCtrl(nb1_p2, wx.ID_ANY,"", style=wx.TE_MULTILINE| 
                                                          wx.TE_PROCESS_ENTER
                                    )
        box_cmd2.Add(self.txt_2cmd, 1, wx.ALL | wx.EXPAND 
                                              | wx.ALIGN_CENTER_HORIZONTAL 
                                              | wx.ALIGN_CENTER_VERTICAL, 15
                    )
        nb1_p2.SetSizer(grd_cmd)
        nb1.AddPage(nb1_p2, (_("Command line FFmpeg")))
        #------- page Automations
        self.nb1_p3 = wx.Panel(nb1, wx.ID_ANY)
        size_auto = wx.BoxSizer(wx.HORIZONTAL)
        grd_autosx = wx.FlexGridSizer(2,1,5,5)
        size_auto.Add(grd_autosx, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.rdbx_norm = wx.RadioBox(self.nb1_p3,wx.ID_ANY,
                                     (_("Audio Normalization")), 
                                     choices=[('Off'), ('PEAK'), 
                                              ('RMS'), ('EBU R128'),
                                              ], 
                                     majorDimension=1, 
                                     style=wx.RA_SPECIFY_ROWS,
                                            )
        grd_autosx.Add(self.rdbx_norm, 0, wx.ALL, 10)  
        
        box_audioMap = wx.StaticBoxSizer(wx.StaticBox(self.nb1_p3, wx.ID_ANY, 
                                                    _("Audio Streams Mapping")), 
                                                      wx.VERTICAL
                                        )
        grd_autosx.Add(box_audioMap, 0, wx.ALL | wx.EXPAND, 10)
        grd_map = wx.FlexGridSizer(2,2,0,0)
        box_audioMap.Add(grd_map, 0, wx.ALL | wx.EXPAND, 5)
        txtAinmap = wx.StaticText(self.nb1_p3,wx.ID_ANY,_('Input Audio Index'))
        grd_map.Add(txtAinmap, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.cmb_A_inMap = wx.ComboBox(self.nb1_p3, wx.ID_ANY,
                                      choices=['Auto', '1','2','3', 
                                               '4','5','6','7','8'],
                                      size=(160,-1), style=wx.CB_DROPDOWN | 
                                                           wx.CB_READONLY
                                       )
        grd_map.Add(self.cmb_A_inMap, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 10)
        txtAoutmap = wx.StaticText(self.nb1_p3, wx.ID_ANY, 
                                   _('Output Audio Index')
                                   )
        grd_map.Add(txtAoutmap, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)

        self.cmb_A_outMap = wx.ComboBox(self.nb1_p3, wx.ID_ANY,
                                      choices=['Auto', 'All', '1','2','3', 
                                               '4','5','6','7','8'],
                                      size=(160,-1), style=wx.CB_DROPDOWN | 
                                                           wx.CB_READONLY
                                                           )
        grd_map.Add(self.cmb_A_outMap, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 10)
        size_panels = wx.BoxSizer(wx.VERTICAL)
        size_auto.Add(size_panels, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
        self.peakpanel = wx.Panel(self.nb1_p3,wx.ID_ANY,style=wx.TAB_TRAVERSAL)
        sizer_peak = wx.FlexGridSizer(1,4,15,15)
        analyzebmp = wx.Bitmap(iconanalyzes, wx.BITMAP_TYPE_ANY)
        self.btn_voldect = GB.GradientButton(self.peakpanel, 
                                             size=(-1,25),
                                             bitmap=analyzebmp,
                                             label=_("Volumedected"))
        self.btn_voldect.SetBaseColours(startcolour=wx.Colour(158,201,232),
                                    foregroundcolour=wx.Colour(self.fBtnC))
        self.btn_voldect.SetBottomEndColour(wx.Colour(self.btnC))
        self.btn_voldect.SetBottomStartColour(wx.Colour(self.btnC))
        self.btn_voldect.SetTopStartColour(wx.Colour(self.btnC))
        self.btn_voldect.SetTopEndColour(wx.Colour(self.btnC))
        sizer_peak.Add(self.btn_voldect, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        
        peaklevelbmp = wx.Bitmap(iconpeaklevel, wx.BITMAP_TYPE_ANY)
        self.btn_details = GB.GradientButton(self.peakpanel,
                                            size=(-1,25),
                                            bitmap=peaklevelbmp,
                                            label=_("Volume Statistics"))
        self.btn_details.SetBaseColours(startcolour=wx.Colour(158,201,232),
                                    foregroundcolour=wx.Colour(self.fBtnC))
        self.btn_details.SetBottomEndColour(wx.Colour(self.btnC))
        self.btn_details.SetBottomStartColour(wx.Colour(self.btnC))
        self.btn_details.SetTopStartColour(wx.Colour(self.btnC))
        self.btn_details.SetTopEndColour(wx.Colour(self.btnC))
        sizer_peak.Add(self.btn_details, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        
        self.lab_amplitude = wx.StaticText(self.peakpanel, wx.ID_ANY, (
                            _("Target level:")))
        sizer_peak.Add(self.lab_amplitude, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        self.spin_target = FS.FloatSpin(self.peakpanel, wx.ID_ANY, 
                                        min_val=-99.0, max_val=0.0, 
                                        increment=0.5, value=-1.0, 
                                        agwStyle=FS.FS_LEFT, size=(-1,-1)
                                        )
        sizer_peak.Add(self.spin_target, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        self.spin_target.SetFormat("%f"), self.spin_target.SetDigits(1)
        size_panels.Add(self.peakpanel, 0, wx.ALL|wx.EXPAND, 0)
        self.peakpanel.SetSizer(sizer_peak) # set panel
        self.ebupanel = wx.Panel(self.nb1_p3, wx.ID_ANY, style=wx.TAB_TRAVERSAL)
        sizer_ebu = wx.FlexGridSizer(3,2,5,5)
        self.lab_i = wx.StaticText(self.ebupanel, wx.ID_ANY, 
                                   _("Set integrated loudness target:  ")
                                   )
        sizer_ebu.Add(self.lab_i, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        self.spin_i = FS.FloatSpin(self.ebupanel, wx.ID_ANY, 
                                   min_val=-70.0, max_val=-5.0, 
                                   increment=0.5, value=-24.0, 
                                   agwStyle=FS.FS_LEFT, size=(-1,-1)
                                   )
        self.spin_i.SetFormat("%f"), self.spin_i.SetDigits(1)
        sizer_ebu.Add(self.spin_i, 0, wx.ALL, 0)
        
        self.lab_tp = wx.StaticText(self.ebupanel, wx.ID_ANY,
                                    _("Set maximum true peak:"))
        sizer_ebu.Add(self.lab_tp, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        self.spin_tp = FS.FloatSpin(self.ebupanel, wx.ID_ANY, 
                                    min_val=-9.0, max_val=0.0,
                                    increment=0.5, value=-2.0, 
                                    agwStyle=FS.FS_LEFT,size=(-1,-1)
                                    )
        self.spin_tp.SetFormat("%f"), self.spin_tp.SetDigits(1)
        sizer_ebu.Add(self.spin_tp, 0, wx.ALL, 0)
        self.lab_lra = wx.StaticText(self.ebupanel, wx.ID_ANY, 
                                    _("Set loudness range target:"))
        sizer_ebu.Add(self.lab_lra, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        self.spin_lra = FS.FloatSpin(self.ebupanel, wx.ID_ANY,
                                     min_val=1.0, max_val=20.0, 
                                     increment=0.5, value=7.0, 
                                     agwStyle=FS.FS_LEFT, size=(-1,-1)
                                     )
        self.spin_lra.SetFormat("%f"), self.spin_lra.SetDigits(1)
        sizer_ebu.Add(self.spin_lra, 0, wx.ALL, 0)
        size_panels.Add(self.ebupanel, 0, wx.ALL|wx.EXPAND, 0)
        self.ebupanel.SetSizer(sizer_ebu) # set panel
        self.nb1_p3.SetSizer(size_auto)
        nb1.AddPage(self.nb1_p3, _("Automations"))
        
        self.SetSizer(sizer_base)
        self.Layout()
        
        #----------------------Set Properties----------------------#
        if OS == 'Darwin':
            self.txt_1cmd.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD))
            self.txt_2cmd.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD))
        else:
            self.txt_1cmd.SetFont(wx.Font(8, wx.MODERN, wx.NORMAL, wx.BOLD))
            self.txt_2cmd.SetFont(wx.Font(8, wx.MODERN, wx.NORMAL, wx.BOLD))
            
        #------- tooltips
        self.txt_1cmd.SetToolTip(_('First pass parameters of the '
                                    'selected profile'))
        self.txt_2cmd.SetToolTip(_('Second pass parameters of the '
                                    'selected profile'))
        self.btn_voldect.SetToolTip(_('Gets maximum volume and average volume '
                'data in dBFS, then calculates the offset amount for audio '
                'normalization.'))
        self.spin_target.SetToolTip(_('Limiter for the maximum peak level or '
                'the mean level (when switch to RMS) in dBFS. From -99.0 to '
                '+0.0; default for PEAK level is -1.0; default for RMS is '
                '-20.0'))
        self.spin_i.SetToolTip(_('Integrated Loudness Target in LUFS. '
                                 'From -70.0 to -5.0, default is -24.0'
                                 ))
        self.spin_tp.SetToolTip(_('Maximum True Peak in dBTP. From -9.0 '
                                  'to +0.0, default is -2.0'
                                  ))
        self.spin_lra.SetToolTip(_('Loudness Range Target in LUFS. '
                                   'From +1.0 to +20.0, default is +7.0'
                                   ))
        self.cmb_A_inMap.SetToolTip(_('Choose from video a specific input '
                                      'audio stream to work out.'))
        self.cmb_A_outMap.SetToolTip(_('Map on the output index. Keep same '
                'input map if saving as video; to save as audio select to '
                '"all" or "Auto"'))
        
        #----------------------Binder (EVT)----------------------#
        self.Bind(wx.EVT_COMBOBOX, self.on_choice_profiles, self.cmbx_prst)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_select, self.list_ctrl)
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.parent.Run_Coding, 
                                              self.list_ctrl)
        self.Bind(wx.EVT_RADIOBOX, self.on_Enable_norm, self.rdbx_norm)
        self.Bind(wx.EVT_BUTTON, self.on_Analyzes, self.btn_voldect)
        self.Bind(wx.EVT_BUTTON, self.on_Show_normlist, self.btn_details)
        self.Bind(wx.EVT_SPINCTRL, self.enter_Amplitude, self.spin_target)
        self.Bind(wx.EVT_COMBOBOX, self.on_audioINstream, self.cmb_A_inMap)
        self.Bind(wx.EVT_COMBOBOX, self.on_audioOUTstream, self.cmb_A_outMap)
        
        #---------------------------- defaults
        self.cmbx_prst.SetSelection(0), self.cmb_A_inMap.SetSelection(0)
        self.cmb_A_outMap.SetSelection(1)
        self.set_listctrl()
        self.normalization_default()
        
    #-------------------------------------------------------------------#
    def normalization_default(self):
        """
        Set default normalization parameters. This method is called by 
        main_frame module on MainFrame.switch_audio_conv() during first 
        run and when there are changing on dragNdrop panel, 
        (like make a clear file list or append new file, etc)
        
        """
        self.rdbx_norm.SetSelection(0), 
        self.peakpanel.Hide(), self.ebupanel.Hide(), self.btn_details.Hide()
        self.spin_target.SetValue(-1.0)
        cmd_opt["PEAK"], cmd_opt["EBU"], cmd_opt["RMS"] = "", "", ""
        del self.normdetails[:]
    #------------------------------------------------------------------#
    
    def on_audioINstream(self, event):
        """
        sets the specified audio input stream as index to process e.g.
        for filters volumedected and loudnorm will map 0:N where N is
        digit from 0 to available audio index up to 8.
        See: http://ffmpeg.org/ffmpeg.html#Advanced-options 
        When changes this feature affect audio filter peak and rms analyzers
        and then re-enable volume dected button . 
        
        """
        sel = self.cmb_A_inMap.GetValue()
        if sel == 'Auto':
            cmd_opt["AudioInMap"] = ['','']
            self.cmb_A_outMap.SetSelection(1)
            self.on_audioOUTstream(self)
        else:
            cmd_opt["AudioInMap"] = ['-map 0:%s' % sel, sel]
            self.cmb_A_outMap.SetStringSelection(self.cmb_A_inMap.GetValue())
            self.on_audioOUTstream(self)
        if self.rdbx_norm.GetSelection() in  [1,2]:
            if not self.btn_voldect.IsEnabled():
                self.btn_voldect.Enable()
                self.btn_voldect.SetForegroundColour(wx.Colour(self.fBtnC))
    #-----------------------------------------------------------------------#
    
    def on_audioOUTstream(self, event):
        """
        Sets the audio stream index for the output file and sets
        audio codec to specified map.
        
        """
        sel = self.cmb_A_outMap.GetValue()
        if sel == 'Auto':
            cmd_opt["AudioOutMap"] = ['','']
        elif sel == 'All':
            cmd_opt["AudioOutMap"] = ['-map 0:a?', '']
        else:
            sel = int(sel) - 1
            cmd_opt["AudioOutMap"] = ['-map 0:a%s?' % str(sel),
                                      '%s'  % str(sel)]
    #------------------------------------------------------------------#
    
    def reset_list(self, reset_cmbx=False):
        """
        Clear all data and re-charging new. Used by selecting new preset
        and add/edit/delete profiles events.
        
        """
        if reset_cmbx:
            prst = sorted([os.path.splitext(x)[0] for x in 
                          os.listdir(self.user_prst) if 
                          os.path.splitext(x)[1] == '.prst'])
            self.cmbx_prst.Clear()
            self.cmbx_prst.AppendItems(prst)
            self.cmbx_prst.SetSelection(0)
        
        self.list_ctrl.ClearAll()
        self.txt_1cmd.SetValue(""), self.txt_2cmd.SetValue("")
        if array != []:
            del array[0:6]
        self.set_listctrl()
    #----------------------------------------------------------------#  
    
    def set_listctrl(self):
        """
        Populates Presets list with JSON data from *.prst files.
        See presets_manager_properties.py 
        """
        self.list_ctrl.InsertColumn(0, _('Profile Name'), width=150)
        self.list_ctrl.InsertColumn(1, _('Description'), width=250)
        self.list_ctrl.InsertColumn(2, _('Output Format'), width=100)
        self.list_ctrl.InsertColumn(3, _('Supported Formats List'), width=150)
        
        path = os.path.join('%s' % self.user_prst, 
                            '%s.prst' % self.cmbx_prst.GetValue()
                            )
        collections = json_data(path)
        if collections == 'error':
            return
        try:
            index = 0
            for name in collections:
                index+=1
                rows = self.list_ctrl.InsertItem(index, name['Name'])
                self.list_ctrl.SetItem(rows, 0, name['Name'])
                self.list_ctrl.SetItem(rows, 1, name["Description"])
                self.list_ctrl.SetItem(rows, 2, name["Output_extension"])
                self.list_ctrl.SetItem(rows, 3, name["Supported_list"])
        except KeyError as err:
            wx.MessageBox(_('ERROR: Typing error on JSON keys: {}\n\n'
                            'File: "{}"\nkey malformed ?'.format(err,path)), 
                            "Videomass", wx.ICON_ERROR, self)
            return
    #----------------------Event handler (callback)----------------------#
    def on_choice_profiles(self, event):
        """
        Combobox event.
        
        """
        self.reset_list()
        self.parent.statusbar_msg('{}'.format(self.cmbx_prst.GetValue()),None)
    #------------------------------------------------------------------#
    
    def on_select(self, event): # list_ctrl
        """
        By selecting a profile in the list_ctrl set new request 
        data in to the appropriate objects and sets parameters 
        to the text boxes.
        
        """
        path = os.path.join('%s' % self.user_prst, 
                            '%s.prst' % self.cmbx_prst.GetValue()
                            )
        collections = json_data(path)
        selected = event.GetText() # event.GetText is a Name Profile
        self.txt_1cmd.SetValue(""), self.txt_2cmd.SetValue("")
        del array[0:6] # delete all: [0],[1],[2],[3],[4],[5]
        
        try:
            for name in collections:
                if selected == name["Name"]:# profile name
                    array.append(name["Name"]) 
                    array.append(name["Description"])
                    array.append(name["First_pass"])
                    array.append(name["Second_pass"])
                    array.append(name["Supported_list"])
                    array.append(name["Output_extension"])
                    
        except KeyError as err:
            wx.MessageBox(_('ERROR: Typing error on JSON keys: {}\n\n'
                            'File: "{}"\nkey malformed ?'.format(err,path)), 
                            "Videomass", wx.ICON_ERROR, self)
            return
        
        self.txt_1cmd.AppendText('%s' %(array[2]))# cmd1 text ctrl
        if array[3]:
            self.txt_2cmd.Enable()
            self.txt_2cmd.AppendText('%s' %(array[3]))# cmd2 text ctrl
        else:
            self.txt_2cmd.Disable()
        
        sel = '{0} - {1}'.format(self.cmbx_prst.GetValue(), array[0])
        self.parent.statusbar_msg(sel, None)
    
    #------------------------------------------------------------------#
    def on_Enable_norm(self, event):
        """
        Sets a corresponding choice for audio normalization
        
        """
        msg_1 = (_('Activate peak level normalization, which will produce '
                   'a maximum peak level equal to the set target level.'
                   ))
        msg_2 = (_('Activate RMS-based normalization, which according to '
                   'mean volume calculates the amount of gain to reach same '
                   'average power signal.'
                   ))
        msg_3 = (_('Activate two passes normalization. It Normalizes the '
                   'perceived loudness using the "​loudnorm" filter, which '
                   'implements the EBU R128 algorithm.'
                   ))
        if self.rdbx_norm.GetSelection() in [1,2]: # PEAK or RMS
            
            if self.rdbx_norm.GetSelection() == 1:
                self.parent.statusbar_msg(msg_1, azure)
                self.spin_target.SetValue(-1.0)
            else:
                self.parent.statusbar_msg(msg_2, '#15A660')
                self.spin_target.SetValue(-20.0)
                
            self.peakpanel.Show(), self.btn_voldect.Enable()
            self.btn_voldect.SetForegroundColour(wx.Colour(self.fBtnC))
            self.ebupanel.Hide()
            cmd_opt["PEAK"], cmd_opt["RMS"], cmd_opt["EBU"] = "", "", ""
            del self.normdetails[:]
            
        elif self.rdbx_norm.GetSelection() == 3: # EBU
            self.parent.statusbar_msg(msg_3, '#87A615')
            self.peakpanel.Hide(), self.ebupanel.Show()
            cmd_opt["PEAK"], cmd_opt["RMS"], cmd_opt["EBU"] = "", "", ""
            del self.normdetails[:]

        else: # usually it is 0
            self.parent.statusbar_msg(_("Audio normalization off"), None)
            self.normalization_default()
            
        if self.btn_details.IsShown:
            self.btn_details.Hide()    
        self.nb1_p3.Layout()
    #------------------------------------------------------------------#
    
    def enter_Amplitude(self, event):
        """
        when spin_amplitude is changed enable 'Volumedected' to
        update new incomming
        
        """
        if not self.btn_voldect.IsEnabled():
            self.btn_voldect.Enable()
            self.btn_voldect.SetForegroundColour(wx.Colour(self.fBtnC))
    #------------------------------------------------------------------#
    
    def on_Analyzes(self, event):
        """
        ** Volumedected button 
        - normalizations based on PEAK Analyzes to get MAXIMUM peak levels 
          data to calculates offset in dBFS need for audio normalization 
          process.
        - normalizations based on RMS Analyzes to get MEAN peak levels data to 
          calculates RMS offset in dBFS need for audio normalization process.

        <https://superuser.com/questions/323119/how-can-i-normalize-audio-
        using-ffmpeg?utm_medium=organic>
        
        """
        msg2 = (_('Audio normalization is required only for some files'))
        msg3 = (_('Audio normalization is not required based to '
                  'set target level'))
        if self.normdetails:
            del self.normdetails[:]
        
        self.parent.statusbar_msg("",None)
        self.time_seq = self.parent.time_seq #from -ss to -t will be analyzed
        target = self.spin_target.GetValue()

        data = volumeDetectProcess(self.parent.file_src, 
                                   self.time_seq, 
                                   cmd_opt["AudioInMap"][0])
        if data[1]:
            wx.MessageBox(data[1], "ERROR! -Videomass", wx.ICON_ERROR)
            return
        else:
            volume = list()
            if self.rdbx_norm.GetSelection() == 1: # peak Analyzes
                for f, v in zip(self.parent.file_src, data[0]):
                    maxvol = v[0].split(' ')[0]
                    meanvol = v[1].split(' ')[0]
                    offset = float(maxvol) - float(target)
                    result = float(maxvol) - offset
                    if float(maxvol) == float(target):
                        volume.append('  ')
                    else:
                        volume.append("-filter:a:%s volume=%fdB" % (
                                                    cmd_opt["AudioOutMap"][1], 
                                                    -offset))
                    self.normdetails.append((f, 
                                            maxvol,
                                            meanvol,
                                            str(offset),
                                            str(result),
                                            ))
            elif self.rdbx_norm.GetSelection() == 2: # rms Analyzes
                for f, v in zip(self.parent.file_src, data[0]):
                    maxvol = v[0].split(' ')[0]
                    meanvol = v[1].split(' ')[0]
                    offset = float(meanvol) - float(target)
                    result = float(maxvol) - offset
                    if offset == 0.0:
                        volume.append('  ')
                    else:
                        volume.append("-filter:a:%s volume=%fdB" % (
                                                   cmd_opt["AudioOutMap"][1],
                                                   -offset))
                    self.normdetails.append((f, 
                                            maxvol,
                                            meanvol,
                                            str(offset),
                                            str(result),
                                            ))
        if [a for a in volume if not '  ' in a] == []:
             self.parent.statusbar_msg(msg3, orange)
        else:
            if len(volume) == 1 or not '  ' in volume:
                 pass
            else:
                self.parent.statusbar_msg(msg2, yellow)
        if self.rdbx_norm.GetSelection() == 1:
            cmd_opt["PEAK"] = volume
        elif self.rdbx_norm.GetSelection() == 2:
            cmd_opt["RMS"] = volume
        
        self.btn_voldect.Disable()
        self.btn_voldect.SetForegroundColour(wx.Colour(165,165, 165))
        self.btn_details.Show()
        self.nb1_p3.Layout()
    #------------------------------------------------------------------#
    
    def on_Show_normlist(self, event):
        """
        Show a wx.ListCtrl dialog with volumedected data
        """
        if self.rdbx_norm.GetSelection() == 1:# peak
            title = _('PEAK-based volume statistics')
        if self.rdbx_norm.GetSelection() == 2: # rms
            title = _('RMS-based volume statistics')
            
        audionormlist = shownormlist.NormalizationList(title, 
                                                       self.normdetails, 
                                                       self.OS)
        audionormlist.Show()
    #-----------------------------------------------------------------------#

    def New_preset_prst(self):
        """
        Create new empty preset '*.prst' on /presets path name
        
        """
        filename = None
        with wx.FileDialog(self, "Enter name for new preset", 
                           defaultDir=self.user_prst,
                           wildcard="Videomass presets (*.prst;)|*.prst;",
                           style=wx.FD_SAVE | 
                                wx.FD_OVERWRITE_PROMPT) as fileDialog:
                if fileDialog.ShowModal() == wx.ID_CANCEL:
                    return
                filename = "%s.prst" % fileDialog.GetPath()
                try:
                    with open(filename, 'w') as file:
                        file.write('[]')
                except IOError:
                    wx.LogError("Cannot save current "
                                "data in file '%s'." % filename)
                    return
        if filename:
            wx.MessageBox(_("'Successful!\n\n"
                            "A new preset has been created."), 
                            "Videomass ", wx.ICON_INFORMATION, self)
            
            self.reset_list(True)

    #------------------------------------------------------------------#
    def Del_preset_prst(self):
        """
        Remove or delete a preset '*.prst' on /presets path name
        and move on Removals folder
        
        """
        filename = self.cmbx_prst.GetValue()
        if wx.MessageBox(_('Are you sure you want to remove "{}" preset?\n\n '
                           'It will be moved to the "Removals" folder in the '
                           'presets directory'
                           ).format(filename), _('Videomass: Please confirm'), 
                            wx.ICON_QUESTION | 
                            wx.YES_NO, self) == wx.NO:
            return
        
        path = os.path.join('%s' % self.user_prst, 
                        '%s.prst' % self.cmbx_prst.GetValue()
                        )
        try: # if exist dir not exit OSError, go...
            if not os.path.exists(os.path.join(self.user_prst, 'Removals')):
                os.mkdir(os.path.join(self.user_prst, 'Removals'))
        except OSError as err:
            wx.MessageBox(_("{}\n\nSorry, removal failed, I can't "
                            "continue.").format(err), "ERROR !", 
                            wx.ICON_ERROR, self)
            return
        s = os.path.join(self.user_prst, '%s.prst' % filename)
        d = os.path.join(self.user_prst, 'Removals', '%s.prst' % filename)
        os.replace(s, d)
        self.reset_list(True)

    #------------------------------------------------------------------#
    def Saveme(self):
        """
        save a file copy preset
        
        """
        combvalue = self.cmbx_prst.GetValue()
        filedir = '%s/%s.prst' % (self.user_prst, combvalue)
        filename = combvalue
        
        dialsave = wx.DirDialog(self, _("Select a directory to save it"))
        if dialsave.ShowModal() == wx.ID_OK:
            dirname = dialsave.GetPath()
            copy_backup(filedir, '%s/%s.prst' % (dirname, filename))
            dialsave.Destroy()
            wx.MessageBox(_("Successfully saved"), "Info", wx.OK, self)
    #------------------------------------------------------------------#
    def Restore(self):
        """
        Replace preset by another
        
        """
        wildcard = "Source (*.prst)|*.prst| All files (*.*)|*.*"
        
        dialfile = wx.FileDialog(self,
                                 _("Videomass: Choose a videomass preset "
                                   "to restore "), '', "", wildcard, 
                                   wx.FD_OPEN | 
                                   wx.FD_FILE_MUST_EXIST
                                     )
        if dialfile.ShowModal() == wx.ID_OK:
            dirname = dialfile.GetPath()
            tail = os.path.basename(dirname)
            dialfile.Destroy()

            
            if wx.MessageBox(_("The following preset:\n\n"
                               "'{0}'\n\n"
                               "will be imported and will overwrite "
                               "the one in use.\n"
                               "Proceed ?").format(tail), 
                             _('Videomass: Please confirm'), 
                                                wx.ICON_QUESTION | 
                                                wx.YES_NO, 
                                                self) == wx.NO:
                return
            
            copy_restore('%s' % (dirname), 
                         '%s/%s' % (self.user_prst, tail))
            
            self.reset_list(True) # re-charging functions
    #------------------------------------------------------------------#
    def Default(self):
        """
        Replace the selected preset at default values. 
        
        """ 
        if wx.MessageBox(_("The selected preset will be overwritten with "
                           "default profiles!\nproceed?"), 
                           _("Videomass: Please confirm"), 
                            wx.ICON_QUESTION | 
                            wx.YES_NO, self) == wx.NO:
            return
        
        filename = self.cmbx_prst.GetValue()
        copy = copy_restore('%s/%s.prst' % (self.src_prst, filename), 
                            '%s/%s.prst' % (self.user_prst, filename))
        
        if copy:
            wx.MessageBox(_('Sorry, this preset is not part '
                            'of default Videomass presets.'), "ERROR !", 
                            wx.ICON_ERROR, self)
            return
            
        self.reset_list() # re-charging functions
    #------------------------------------------------------------------#
    def Default_all(self):
        """
        restore all preset files directory
        
        """
        if wx.MessageBox(_("WARNING: you are restoring all "
                           "default presets!\nProceed?"), 
                           _("Videomass: Please confirm"), 
                            wx.ICON_QUESTION | 
                            wx.YES_NO, self) == wx.NO:
            return

        copy_on('prst', self.src_prst, self.user_prst)
        
        self.reset_list(True) # re-charging functions
    #------------------------------------------------------------------#
    def Refresh(self):
        """ 
        Force to to re-charging
        """
        self.reset_list(True)
    
    #------------------------------------------------------------------#
    def Addprof(self):
        """
        Store new profiles in the selected preset
        
        """
        filename = self.cmbx_prst.GetValue()
        t = _('Create a new profile on "%s" preset') % filename

        prstdialog = presets_addnew.MemPresets(self, 
                                               'newprofile', 
                                               filename, 
                                               None,
                                               t)
        ret = prstdialog.ShowModal()
        
        if ret == wx.ID_OK:
            self.reset_list() # re-charging list_ctrl with newer
    #------------------------------------------------------------------#
    def Editprof(self, event):
        """
        Edit an existing profile
        
        """
        if array == []:
            self.parent.statusbar_msg(_("First select a profile in the list"), 
                                        yellow)
            return
        else:
            filename = self.cmbx_prst.GetValue()
            t = _('Edit profile of the "%s" preset: ') % (filename)
            
            prstdialog = presets_addnew.MemPresets(self, 
                                                   'edit', 
                                                   filename, 
                                                   array, 
                                                   t,
                                                   )
            ret = prstdialog.ShowModal()
            if ret == wx.ID_OK:
                self.reset_list() # re-charging list_ctrl with newer
    #------------------------------------------------------------------#
    def Delprof(self):
        """
        Delete a selected profile
        
        """
        if array == []:
            self.parent.statusbar_msg(_("First select a profile in the list"), 
                                         yellow)
        else:
            filename = self.cmbx_prst.GetValue()
            if wx.MessageBox(_("Are you sure you want to delete the "
                             "selected profile?"), 
                             _("Videomass: Please confirm"), 
                             wx.ICON_QUESTION | 
                             wx.YES_NO, self) == wx.NO:
                return
            
            path = os.path.join('%s' % self.user_prst, 
                            '%s.prst' % self.cmbx_prst.GetValue()
                            )
            delete_profiles(path, array[0])

            self.reset_list()
    #------------------------------------------------------------------#
    def on_Start(self):
        """
        File data redirecting .
        
        """
        # check normalization data offset, if enable.
        if self.rdbx_norm.GetSelection() in [1,2]: # PEAK or RMS
            if self.btn_voldect.IsEnabled():
                wx.MessageBox(_('Undetected volume values! use the '
                                '"Volumedetect" control button to analyze '
                                'data on the audio volume.'),
                                "Videomass", wx.ICON_INFORMATION)
                return
        self.time_seq = self.parent.time_seq
        dir_destin = self.parent.file_destin
        # used for file name log 
        self.logname = 'Videomass_PresetsManager.log'

        ######## ------------ VALIDAZIONI: --------------
        if array == []:
            self.parent.statusbar_msg(_("First select a profile in the list"),  
                                        yellow)
            return
        
        if (array[2].strip() != self.txt_1cmd.GetValue().strip() or 
            array[3].strip() != self.txt_2cmd.GetValue().strip()):
            if not self.txtcmdedited:
            
                msg = _("The selected profile command has been "
                        "changed manually.\n"
                        "Do you want to apply it "
                        "during the conversion process?")
                dlg = wx.RichMessageDialog(self, msg, 
                                           _("Videomass: Please confirm"), 
                                            wx.ICON_QUESTION | 
                                            wx.YES_NO,)

                dlg.ShowCheckBox(_("Don't show this dialog again"))

                if dlg.ShowModal() == wx.ID_NO:
                    if dlg.IsCheckBoxChecked():
                        # make sure we won't show it again the next time
                        self.txtcmdedited = True
                    return
                else:
                    if dlg.IsCheckBoxChecked():
                        # make sure we won't show it again the next time
                        self.txtcmdedited = True
                        
        outext = '' if array[5] == 'copy' else array[5] 
        extlst, outext = array[4], outext
        file_sources = supported_formats(extlst, self.parent.file_src)
        checking = inspect(file_sources, dir_destin, outext)
        
        if not checking[0]:
            # not supported, missing files or user has changed his mind
            return

        
        (batch, file_sources, dir_destin, fname, bname, cntmax) = checking
        # batch: batch or single process
        # fname: filename, nome file senza ext.
        # bname: basename, nome file con ext.
        # cntmax: count items for batch proc.
        if array[5] in ['jpg','png','bmp']:
            self.savepictures(dir_destin, file_sources)
        elif array[3]: # has double pass
            self.two_Pass(file_sources, dir_destin, cntmax, outext)
        else:
            self.one_Pass(file_sources, dir_destin, cntmax, outext)
    #----------------------------------------------------------------#
    
    def one_Pass(self, filesrc, destdir, cntmax, outext):
        """
        
        """
        audnorm = cmd_opt["RMS"] if not cmd_opt["PEAK"] else cmd_opt["PEAK"]
        command = (self.txt_1cmd.GetValue())
        valupdate = self.update_dict(cntmax, 'One passes')
        ending = Formula(self, valupdate[0], valupdate[1], _('Starts'))
        if ending.ShowModal() == wx.ID_OK:
            self.parent.switch_Process('onepass',
                                        filesrc, 
                                        outext, 
                                        destdir, 
                                        command, 
                                        None, 
                                        '',
                                        audnorm, 
                                        self.logname, 
                                        cntmax,
                                        )
    #------------------------------------------------------------------#
    
    def two_Pass(self, filesrc, destdir, cntmax, outext):
        """
        defines two-pass parameters
        """
        pass1 = " ".join(self.txt_1cmd.GetValue().split())
        pass2 = " ".join(self.txt_2cmd.GetValue().split())
        
        if 'loudnorm=' in pass1:
            if self.rdbx_norm.GetSelection() == 3:
                if wx.MessageBox(_('EBU normalization is enabled twice in '
                                   'automation and command line. Command line '
                                   'has priority.\n\nDo you wish to continue?'), 
                                _("Videomass: Please confirm"), 
                                wx.ICON_QUESTION | 
                                wx.YES_NO, self) == wx.NO:
                    return
            
            typeproc, audnorm = 'two pass EBU', ''
            loudnorm = [ebu for ebu in pass1.split() if 'loudnorm=' in ebu][0]
            
        elif self.rdbx_norm.GetSelection() == 3:
            loudnorm = ('loudnorm=I=%s:TP=%s:LRA=%s:print_format=summary' %(
                                            str(self.spin_i.GetValue()),
                                            str(self.spin_tp.GetValue()),
                                            str(self.spin_lra.GetValue())))
            pass1 = '-filter:a: %s ' % loudnorm + '%s' % pass1
            typeproc, audnorm = 'two pass EBU', ''
            
                
            
        else: # two-pass std
            typeproc, loudnorm = 'twopass', ''
            audnorm = cmd_opt["RMS"] if not cmd_opt["PEAK"] else cmd_opt["PEAK"]
        
        
        valupdate = self.update_dict(cntmax, typeproc)
        ending = Formula(self, valupdate[0], valupdate[1], _('Starts'))
        if ending.ShowModal() == wx.ID_OK:

            self.parent.switch_Process(typeproc,
                                        filesrc, 
                                        outext, 
                                        destdir, 
                                        None, 
                                        [pass1, pass2, loudnorm], 
                                        cmd_opt["AudioOutMap"],
                                        audnorm, 
                                        self.logname, 
                                        cntmax,
                                        )
    #--------------------------------------------------------------------#
    def savepictures(self, dest, file_sources):
        """
        Save as files image the selected video input. The saved 
        images are named as file name + a progressive number + .jpg 
        and placed in a folder with the same file name + a progressive 
        number in the chosen output path.
        
        """
        if len(file_sources) == 1:
            clicked = file_sources[0]
            
        elif not self.parent.filedropselected:
            wx.MessageBox(_("To export as pictures, select "
                            "one file at a time"), 'Videomass', 
                            wx.ICON_INFORMATION, self)
            return
        else:
            clicked = self.parent.filedropselected
        
        valupdate = self.update_dict(1, 'None')
        ending = Formula(self, valupdate[0], valupdate[1], _('Starts'))
        
        if ending.ShowModal() == wx.ID_OK:
            fname = os.path.basename(clicked.rsplit('.', 1)[0])
            dir_destin = dest[file_sources.index(clicked)]# specified dest
            
            try: 
                outputdir = "%s/%s-IMAGES_1" % (dir_destin, fname)
                os.mkdir(outputdir)
                
            except FileExistsError:
                lista = []
                for dir_ in os.listdir(dir_destin):
                    if "%s-IMAGES_" % fname in dir_:
                        lista.append(int(dir_.split('IMAGES_')[1]))
                        
                prog = max(lista) +1
                outputdir = "%s/%s-IMAGES_%d" % (dir_destin, fname, prog)
                os.mkdir(outputdir)

            fileout = "{0}-%d.{1}".format(fname,array[5])
            cmd = ('%s -y "%s"' % (self.txt_1cmd.GetValue(),
                                   os.path.join(outputdir, fileout))
                                        )
            command = " ".join(cmd.split())# compact string
            self.parent.switch_Process('savepictures',
                                        clicked, 
                                        None, 
                                        None, 
                                        command, 
                                        None, 
                                        None, 
                                        None, 
                                        self.logname, 
                                        1, 
                                        False,# do not use is reserved
                                        )
    #------------------------------------------------------------------#
    def update_dict(self, cntmax, passes):
        """
        Update information before send to epilogue
        
        """
        if cmd_opt["PEAK"]:
            normalize = 'PEAK'
        elif cmd_opt["RMS"]:
            normalize = 'RMS'
        elif cmd_opt["EBU"]:
            normalize = 'EBU R128'
        else:
            normalize = _('Off')
        if not self.parent.time_seq:
            time = _('Disable')
        else:
            t = list(self.parent.time_read.items())
            time = '{0}: {1} | {2}: {3}'.format(t[0][0], t[0][1][0], 
                                                t[1][0], t[1][1][0])
            
        numfile = "%s file in pending" % str(cntmax)
                    
        formula = (_("SUMMARY\n\nFile to Queue\nPass Encoding\
                      \nProfile Used\nOutput Format\nAudio Normalization\
                      \nSelected Input Audio index\nAudio Output Map index\
                      \nTime selection"
                      ))
        dictions = ("\n\n%s\n%s\n%s\n"
                    "%s\n%s\n%s\n%s\n%s" % (numfile,
                                            passes,
                                            array[0], 
                                            array[5],
                                            normalize,
                                            self.cmb_A_inMap.GetValue(),
                                            self.cmb_A_outMap.GetValue(),
                                            time,)
                    )

        return formula, dictions
    #--------------------------------------------------------------------#
