"""
 File:  
 JetCtrls.py
 
 Contents and purpose:
 Auditions a jet file to simulate interactive music functions
 
 Copyright (c) 2008 Android Open Source Project
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
      http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
"""

import wx
import sys

from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin, ColumnSorterMixin
from JetUtils import *
from JetDefs import *

class JetSpin(wx.SpinCtrl):
    """ Spin control """
    def __init__(self, parent, id=-1,value=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize,style=wx.SP_ARROW_KEYS,min=0,max=100,initial=0):
        wx.SpinCtrl.__init__(self, parent, id=id,value=value,pos=(pos[0]-MacOffset(),pos[1]),size=size,style=style,min=min,max=max,initial=initial)

    def SetValue(self, val):
        try:
            if type(val).__name__=='str':
                wx.SpinCtrl.SetValue(self, int(val))
            else:
                wx.SpinCtrl.SetValue(self, val)
        except:
            wx.SpinCtrl.SetValue(self, 0)
            
class JetSpinOneBased(JetSpin):
    """ Spin control that's one based """
    def __init__(self, parent, id=-1,value=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize,style=wx.SP_ARROW_KEYS,min=0,max=100,initial=0):
        wx.SpinCtrl.__init__(self, parent, id=id,value=value,pos=(pos[0]-MacOffset(),pos[1]),size=size,style=style,min=min,max=max,initial=initial)

    def SetValue(self, val):
        try:
            if type(val).__name__=='str':
                wx.SpinCtrl.SetValue(self, int(val) + 1)
            else: 
                wx.SpinCtrl.SetValue(self, val + 1)
        except:
            wx.SpinCtrl.SetValue(self, 1)
            
    def GetValue(self):
        val = wx.SpinCtrl.GetValue(self)
        val = val - 1
        return val
            
class JetCheckBox(wx.CheckBox):
    """ Checkbox control """
    def __init__(self, parent, id=-1,label=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize):
        wx.CheckBox.__init__(self, parent, id=id, label=label, pos=pos, size=size)
        
    def SetValue(self, val):
        try:
            if type(val).__name__=='str':
                if val == 'True':
                    val = True
                else:
                    val = False
                wx.CheckBox.SetValue(self, val)
            else:
                wx.CheckBox.SetValue(self, val)
        except:
            wx.CheckBox.SetValue(self, False)
             
class JetRadioButton(wx.RadioButton):
    """ Radio button control """
    def __init__(self, parent, id=-1,label=wx.EmptyString,pos=wx.DefaultPosition,size=wx.DefaultSize):
        wx.RadioButton.__init__(self, parent, id=id, label=label, pos=pos, size=size)
        
    def SetValue(self, val):
        try:
            if type(val).__name__=='str':
                if val == 'True':
                    val = True
                else:
                    val = False
                wx.RadioButton.SetValue(self, val)
            else:
                wx.RadioButton.SetValue(self, val)
        except:
            wx.RadioButton.SetValue(self, False)
             
class JetListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin):
    """ List control """
    def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.ListCtrl.__init__(self, parent, id, pos=pos, size=size, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
        ListCtrlAutoWidthMixin.__init__(self)
        self.iCol = 0
        self.iWidth = 0
        self.OnSortOrderChangedAlert = None
        self.iInitialized = False

    def AddCol(self, title, width):
        self.InsertColumn(self.iCol, title)
        if width > 0:
            self.SetColumnWidth(self.iCol, width)
        else:
            width = self.GetColumnWidth(self.iCol)
        self.iCol += 1
        self.iWidth = self.iWidth + width
        self.SetSize((self.iWidth + 10, -1))

    def AddRows(self, values):
        for value in values:
            iCol = 0
            for row in value:
                if iCol == 0:
                    index = self.InsertStringItem(sys.maxint, row)
                else:
                    self.SetStringItem(index, iCol, row) 
                iCol = iCol + 1

    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
    def GetListCtrl(self):
        return self
    
    def InitSorting(self, cols):
        if not self.iInitialized:
            ColumnSorterMixin.__init__(self, cols)
            self.iInitialized = True
        
    def OnSortOrderChanged(self):
        if self.OnSortOrderChangedAlert is not None:
            self.OnSortOrderChangedAlert()

    def __OnColClick(self, evt):
        oldCol = self._col
        self._col = col = evt.GetColumn()
        self._colSortFlag[col] = int(not self._colSortFlag[col])
        self.OnSortOrderChanged()
        
class JetCheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin, ColumnSorterMixin):
    """ List control with checkboxes on each line """
    def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LC_REPORT | wx.SUNKEN_BORDER):
        wx.ListCtrl.__init__(self, parent, id, pos=pos, size=size, style=style)
        CheckListCtrlMixin.__init__(self)
        ListCtrlAutoWidthMixin.__init__(self)

        self.iCol = 0
        self.iWidth = 0
        self.OnSortOrderChangedAlert = None
        self.iInitialized = False

    def AddCol(self, title, width):
        self.InsertColumn(self.iCol, title)
        if width > 0:
            self.SetColumnWidth(self.iCol, width)
        else:
            width = self.GetColumnWidth(self.iCol)
        self.iCol += 1
        self.iWidth = self.iWidth + width
        self.SetSize((self.iWidth + 10, -1))

    def OnCheckItem(self, index, flag):
        if hasattr(self, 'BindCheckBoxFct'):
            self.BindCheckBoxFct(index, flag)
        
    def BindCheckBox(self, fct):
        self.BindCheckBoxFct = fct

    def AddRows(self, values):
        for value in values:
            iCol = 0
            for row in value:
                if iCol == 0:
                    index = self.InsertStringItem(sys.maxint, row)
                else:
                    self.SetStringItem(index, iCol, row) 
                iCol = iCol + 1

    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
    def GetListCtrl(self):
        return self
    
    def InitSorting(self, cols):
        if not self.iInitialized:
            ColumnSorterMixin.__init__(self, cols)
            self.iInitialized = True
     
    def OnSortOrderChanged(self):
        if self.OnSortOrderChangedAlert is not None:
            self.OnSortOrderChangedAlert()
        
    def __OnColClick(self, evt):
        oldCol = self._col
        self._col = col = evt.GetColumn()
        self._colSortFlag[col] = int(not self._colSortFlag[col])
        self.OnSortOrderChanged()

class JetTrackCtrl(JetCheckListCtrl):
    """ List control specifically designed to show tracks in midi file """
    def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LC_REPORT | wx.SUNKEN_BORDER):
        wx.ListCtrl.__init__(self, parent, id, pos=pos, size=size, style=style)
        CheckListCtrlMixin.__init__(self)
        ListCtrlAutoWidthMixin.__init__(self)

        self.iCol = 0
        self.iWidth = 0
        self.muteFlags = 0

    def SetValue(self, muteFlags):
        self.muteFlags = muteFlags
        
    def GetValue(self):
        return self.muteFlags

    def CheckTracks(self, muteFlags):
        num = self.GetItemCount()
        for iRow in range(num):
            track_num = self.GetTrackNumber(iRow)
            self.CheckItem(iRow, GetMute(track_num, muteFlags))
            
    def AddTrackRow(self, track, loadEmpty=False):
        if loadEmpty or not track.empty: 
            index = self.InsertStringItem(sys.maxint, str(track.track))
            self.SetStringItem(index, 1, str(track.channel))
            self.SetStringItem(index, 2, str(track.name))
               
    def GetTrackNumber(self, index):
        return getColumnValue(self, index, 0)
                          
class JetFileCombo():
    """ Combo box with file open button """
    def __init__(self, parent, pos=(0,0), size=(200,-1), title='Open File', spec='*.*', id=-1):
        self.spec = spec
        self.title = title
        self.EventFire = False
        BUTWIDTH = 20
        BORDER = 5
        w = size[0] - (BUTWIDTH + BORDER)
        col = pos[0] + w + BORDER
        
        self.cmb = wx.ComboBox(parent, id, "", pos=(pos[0]-MacOffset(),pos[1]), size=(w, -1), style=wx.CB_DROPDOWN)
        self.btn = wx.Button(parent, -1, "...", pos=(col, pos[1]+MacOffset()), size=(BUTWIDTH,self.cmb.GetSize()[1]))
        self.btn.Bind(wx.EVT_BUTTON, self.OnBrowse, self.btn) 

    def OnBrowse(self, event):
        os = __import__('os')
        defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, 'str', str(os.getcwd()))
        if OsWindows():
            defDir = defDir.replace('/','\\')
        else:
            defDir = defDir.replace('\\', '/')
            
        dlg = wx.FileDialog(None, self.title, defDir, '', self.spec, wx.FD_OPEN)
        ret = dlg.ShowModal()
        if ret == wx.ID_OK:
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, str(FileJustPath(dlg.GetPath())))
            val = dlg.GetPath()

            self.Append(val)
            self.cmb.SetValue(val)
            
            if self.EventFire:
                SendEvent(self.cmb, wx.EVT_COMBOBOX.evtType[0])
        dlg.Destroy()
        
    def SetEventFire(self, fire):
        self.EventFire = fire
        
    def GetValue(self):
        return StrNoneChk(self.cmb.GetValue())   
    
    def SetValue(self, val):
        try:
            self.cmb.SetValue(val)
        except:
            pass

    def Append(self, val):
        try:
            self.cmb.Append(val)
        except:
            pass
        
    def SetFocus(self):
        self.cmb.SetFocus()
        
    def SetListValues(self, list):
        self.cmb.AppendItems(list)
        
    def Enable(self, enable):
        self.cmb.Enable(enable)
        self.btn.Enable(enable)
                
    def SetHelpText(self, Lbl):
        self.cmb.SetHelpText(Lbl)
        self.btn.SetHelpText(Lbl)
        
class JetFileText():
    """ Capture a filename with a button to browse for a file """
    def __init__(self, parent, pos=(0,0), size=(200,-1), title='Open File', spec='*.*', id=-1):
        self.spec = spec
        self.title = title
        BUTWIDTH = 20
        BORDER = 5
        w = size[0] - (BUTWIDTH + BORDER)
        col = pos[0] + w + BORDER
        
        self.txt = wx.TextCtrl(parent, id, "", pos=(pos[0]-MacOffset(),pos[1]), size=(w, -1))
        self.btn = wx.Button(parent, -1, "...", pos=(col, pos[1]), size=(BUTWIDTH,self.txt.GetSize()[1]))
        self.btn.Bind(wx.EVT_BUTTON, self.OnBrowse, self.btn) 
            
    def OnBrowse(self, event):
        os = __import__('os')
        defDir = IniGetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, 'str', str(os.getcwd()))
        if OsWindows():
            defDir = defDir.replace('/','\\')
        else:
            defDir = defDir.replace('\\', '/')
        
        dlg = wx.FileDialog(None, self.title, defDir, '', self.spec, wx.FD_OPEN)
        ret = dlg.ShowModal()
        if ret == wx.ID_OK:
            IniSetValue(JetDefs.JETCREATOR_INI, JetDefs.INI_DEFAULTDIRS, self.spec, str(FileJustPath(dlg.GetPath())))
            val = dlg.GetPath()
            self.txt.SetValue(val)
        dlg.Destroy()
        
    def GetValue(self):
        return StrNoneChk(self.txt.GetValue())   
    
    def SetValue(self, val):
        try:
            self.txt.SetValue(val)
        except:
            pass

    def Append(self, val):
        try:
            self.txt.Append(val)
        except:
            pass
        
    def SetFocus(self):
        self.txt.SetFocus()

    def Enable(self, enable):
        self.txt.Enable(enable)
        self.btn.Enable(enable)

    def SetHelpText(self, Lbl):
        self.txt.SetHelpText(Lbl)
        self.btn.SetHelpText(Lbl)
        
def YesNo(title, question, default):
    """ Simple Yes/No question box """
    dlg = wx.MessageDialog(None, question, title, wx.YES_NO | wx.ICON_QUESTION) 
    if dlg.ShowModal() == wx.ID_YES:
        result = True
    else:
        result = False
    dlg.Destroy()
    return result

def YesNoCancel(title, question, default):
    """ Simple Yes/No question box """
    dlg = wx.MessageDialog(None, question, title, wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
    result = dlg.ShowModal() 
    dlg.Destroy()
    return result

def ErrorMsg(title, message):
    """ Dipslay an error message """
    dlg = wx.MessageDialog(None, message, title, wx.ICON_ERROR) 
    dlg.ShowModal()
    dlg.Destroy()

def InfoMsg(title, message):
    """ Displays an informational message """
    dlg = wx.MessageDialog(None, message, title, wx.ICON_INFORMATION) 
    dlg.ShowModal()
    dlg.Destroy()

class TimeCtrl(wx.Frame):
    """ Combination of controls to capture measure, beat, tick times """
    def __init__(self, parent, pos=(0,0), minimums=(1,1,0), maximums=(999,4,480), value=JetDefs.MBT_DEFAULT, ctlName=''):
        wx.Frame.__init__(self, parent, -1)
        
        self.ChangeCallbackFct = None
        self.ctlName = ctlName
        self.mx = maximums
        self.mn = minimums
        self.maxTicks = 0
        self.iCtrl = 0
        p1 = pos[0]
        top = pos[1] + MacOffset()
        w1 = 30
        self.time = (wx.TextCtrl(parent, -1, str(value[0]), pos=(p1, top), size=(w1, -1), style=wx.TE_NOHIDESEL),
                wx.TextCtrl(parent, -1, str(value[1]), pos=(p1 + (w1 + 3), top), size=(w1, -1), style=wx.TE_NOHIDESEL),
                wx.TextCtrl(parent, -1, str(value[2]), pos=(p1 + (w1 + 3) *2, top), size=(w1, -1), style=wx.TE_NOHIDESEL),
                )
        h = self.time[2].GetSize().height
        w = self.time[2].GetSize().width + self.time[2].GetPosition().x + 8
        
        self.spin = wx.SpinButton(parent, -1, (w, top), (h*2/3, h), wx.SP_VERTICAL)
        self.spin.SetValue(1)
        self.spin.SetRange(-999,999)
        
        self.spin.Bind(wx.EVT_SPIN_UP, self.OnSpinUp, self.spin)
        self.spin.Bind(wx.EVT_SPIN_DOWN, self.OnSpinDown, self.spin)
        
        self.time[0].Bind(wx.EVT_SET_FOCUS, self.OnFocusMeasure, self.time[0] )
        self.time[1].Bind(wx.EVT_SET_FOCUS, self.OnFocusBeat, self.time[1] )
        self.time[2].Bind(wx.EVT_SET_FOCUS, self.OnFocusTick, self.time[2] ) 
                
        self.time[0].Bind(wx.EVT_KILL_FOCUS, self.OnChangeVal, self.time[0] )
        self.time[1].Bind(wx.EVT_KILL_FOCUS, self.OnChangeVal, self.time[1] )
        self.time[2].Bind(wx.EVT_KILL_FOCUS, self.OnChangeVal, self.time[2] )         

        self.SetValue(value)

    def UnBindKillFocus(self):
        self.time[0].Unbind(wx.EVT_KILL_FOCUS, self.time[0])
        self.time[1].Unbind(wx.EVT_KILL_FOCUS, self.time[1])
        self.time[2].Unbind(wx.EVT_KILL_FOCUS, self.time[2])         
                
    def SetChangeCallbackFct(self, ChangeCallbackFct):
        self.ChangeCallbackFct = ChangeCallbackFct
        
    def OnChangeVal(self, event=None):
        if not OsWindows():
            self.time[self.iCtrl].SetSelection(-1,-1)
        if int(self.time[self.iCtrl].GetValue()) > self.mx[self.iCtrl]:
            self.time[self.iCtrl].SetValue(str(self.mx[self.iCtrl]))
        if int(self.time[self.iCtrl].GetValue()) < self.mn[self.iCtrl]:
            self.time[self.iCtrl].SetValue(str(self.mn[self.iCtrl]))
        if self.ChangeCallbackFct is not None:
            self.ChangeCallbackFct()
        if event is not None:
            event.Skip()
        
    def OnSpinUp(self, event):
        if int(self.time[self.iCtrl].GetValue()) < self.mx[self.iCtrl]:
            self.time[self.iCtrl].SetValue(str(int(self.time[self.iCtrl].GetValue()) + 1))
            self.OnChangeVal()
                                             
    def OnSpinDown(self, event):
        if int(self.time[self.iCtrl].GetValue()) > self.mn[self.iCtrl]:
            self.time[self.iCtrl].SetValue(str(int(self.time[self.iCtrl].GetValue()) - 1))
            self.OnChangeVal()
                                               
    def OnFocusMeasure(self, event):
        self.iCtrl = 0

    def OnFocusBeat(self, event):
        self.iCtrl = 1

    def OnFocusTick(self, event):
        self.iCtrl = 2
        
    def SetValue(self, mbt):
        try:
            if type(mbt).__name__=='str' or type(mbt).__name__=='unicode':
                mbt = ConvertStrTimeToTuple(mbt)
            mbt = mbtFct(mbt, 1)
            self.time[0].SetValue(str(mbt[0]))
            self.time[1].SetValue(str(mbt[1]))
            self.time[2].SetValue(str(mbt[2]))
        except:
            self.time[0].SetValue(str(self.mn[0]))
            self.time[1].SetValue(str(self.mn[1]))
            self.time[2].SetValue(str(self.mn[2]))
        if not OsWindows():
            self.time[0].SetSelection(-1,-1)
            self.time[1].SetSelection(-1,-1)
            self.time[2].SetSelection(-1,-1)
           
    def GetValue(self, typ='str'):
        try:
            if typ == 'str':
                ret = "%d:%d:%d" % (int(self.time[0].GetValue()), int(self.time[1].GetValue()), int(self.time[2].GetValue()))
            else:
                ret = (int(self.time[0].GetValue()), int(self.time[1].GetValue()), int(self.time[2].GetValue()))
        except:
            ret = self.minimums
        return mbtFct(ret, -1)

    def Enable(self, enable):
        self.time[0].Enable(enable)
        self.time[1].Enable(enable)
        self.time[2].Enable(enable)
        self.spin.Enable(enable)

    def SetFocus(self):
        self.time[0].SetFocus()
        
    def SetMaxMbt(self, m, b, t):
        self.mx = (m,b,t)

    def GetMaxMbt(self):
        return "%d:%d:%d" % self.mx

    def SetMinMbt(self, m, b, t):
        self.mn = (m,b,t)
          
    def SetMaxTicks(self, maxTicks): 
        self.maxTicks = maxTicks
        
    def GetMaxTicks(self): 
        return self.maxTicks
    
    def SetHelpText(self, Lbl):
        self.spin.SetHelpText(Lbl)
        self.time[0].SetHelpText(Lbl)
        self.time[1].SetHelpText(Lbl)
        self.time[2].SetHelpText(Lbl)
        
    def GetMeasure(self):
        return int(self.time[0].GetValue())
    
    def GetBeat(self):
        return int(self.time[1].GetValue())
    
    def GetTick(self):
        return int(self.time[2].GetValue())
 
if __name__ == '__main__':
    """ Test code for controls """
    class TestFrame(wx.Frame):
        def __init__(self, parent, id, title):
            wx.Frame.__init__(self, parent, id, title, size=(350, 220))
    
            panel = wx.Panel(self, -1)
            
            self.tc = TimeCtrl(panel, pos=(30, 20), maximums=(25, 4, 120), value=(2, 3, 4))
            #tc.Enable(True)
            #tc.SetValue((2,3,4))
            #tc.SetValue("1:2:3")
            #print(tc.GetValue())
            
            js = JetSpin(panel, -1, pos=(30, 100))
            js.SetValue("1")
            #js.SetValue(1)
            
            #fl = JetFileCombo(panel)

            wx.EVT_CLOSE(self, self.OnClose)
            
            self.Centre()
            self.Show(True)
    
        def OnClose(self, event):
            self.tc.UnBindKillFocus()
            self.Destroy()
            
    app = wx.App(None)
    TestFrame(None, -1, 'TestFrame')
    app.MainLoop()