## ------------------------------------------------------------------------
## iSiLK - A python-based graphical user interface for SiLK
## ------------------------------------------------------------------------
## Copyright (c) 2007-2008 Carnegie Mellon University. All Rights Reserved.
## ------------------------------------------------------------------------
## Authors: Andrew Kompanek (ajk@cert.org)
## ------------------------------------------------------------------------
## GNU General Public License (GPL) Rights pursuant to Version 2, June 1991
## Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013
## ------------------------------------------------------------------------
## See the LICENSE file included in this distribution
## ------------------------------------------------------------------------

import wx, ws
import copy
import config, config.silk
import string

DISABLE_EXTERNAL_SSH = True

class RecordViewOptionsDialog(wx.Dialog):

    def __init__(self, opts, *args, **kargs):
        wx.Dialog.__init__(self, *args, **kargs)
        self.opts = opts
        panel = wx.Panel(self)

        self.chkShowNames = wx.CheckBox(panel, -1,
             " Show names of common ports and protocols")
        self.chkShowHostNames = wx.CheckBox(panel, -1,
             " Show resolved host names")

        # Create button sizer
        if False:
            szBtns =  wx.StdDialogButtonSizer()
            ok = wx.Button(self, wx.ID_OK)
            cancel = wx.Button(self, wx.ID_CANCEL)
            ok.SetDefault()
            szBtns.AddButton(ok)
            szBtns.AddButton(cancel)
            szBtns.Realize()
        else:
            szBtns = self.CreateButtonSizer(wx.OK|wx.CANCEL)
        sz = wx.BoxSizer(wx.VERTICAL) #FlexGridSizer(3, 1) #wx.VERTICAL)           
        sz.Add(self.chkShowNames, flag=wx.ALL|wx.ALIGN_TOP, border=10)
        sz.Add(self.chkShowHostNames, flag=wx.ALL|wx.ALIGN_TOP, border=10)        
        panel.SetSizerAndFit(sz)
        # Set value
        if opts.get("RECVIEW_SHOW_NAMES", True):
            self.chkShowNames.SetValue(wx.CHK_CHECKED)
        else:
            self.chkShowNames.SetValue(wx.CHK_UNCHECKED)
        if opts.get("RECVIEW_SHOW_HOSTNAMES", True):
            self.chkShowHostNames.SetValue(wx.CHK_CHECKED)
        else:
            self.chkShowHostNames.SetValue(wx.CHK_UNCHECKED)
        self.columns = ["nhip", "class", "type", "sensor","sip","dip","sport","dport",
                        "icmp_type", "icmp_code", "scc", "dcc", "sval", "dval", "proto",
                        "packets","bytes","records", "flags","stime",
                        "dur","etime"]
        self.checkboxes = {}
        szColumns = wx.FlexGridSizer(3, 4)
        hide_columns = opts.get("RECVIEW_HIDE_COLUMNS", [])
        for c in self.columns:
            chk = ws.SmCheckBox(self, -1, c)
            # FIXME: use configuraiton option
            if c in hide_columns:
                chk.SetValue(wx.CHK_UNCHECKED)
            else:
                chk.SetValue(wx.CHK_CHECKED)
            self.checkboxes[c] = chk
            szColumns.Add(chk, flag=wx.ALL, border=4)
        szOuter = wx.BoxSizer(wx.VERTICAL)
        szOuter.Add(panel)
        szOuter.Add(wx.StaticText(self, -1, "Show columns"), flag=wx.LEFT|wx.RIGHT|wx.TOP, border=10)
        szOuter.Add(szColumns, flag=wx.ALL, border=12)
        szOuter.Add(szBtns, flag=wx.ALIGN_RIGHT|wx.ALL, border=12)
        self.SetSizerAndFit(szOuter)
                    
    def ShowModal(self):
        rv = wx.Dialog.ShowModal(self)
        if rv == wx.ID_OK:
            self.opts["RECVIEW_SHOW_NAMES"] = (self.chkShowNames.GetValue() == wx.CHK_CHECKED) 
            self.opts["RECVIEW_SHOW_HOSTNAMES"] = (self.chkShowHostNames.GetValue() == wx.CHK_CHECKED)
            cols = []
            for c in self.columns:
                if self.checkboxes[c].GetValue() == wx.CHK_UNCHECKED:
                  cols.append(c)  
            self.opts["RECVIEW_HIDE_COLUMNS"] = cols
            print "Hide columns are: %s" % cols
        return


class OptionValidator(ws.BasicValidator):
     def __init__(self, dialog, chars):
         self.chars = chars
         ws.BasicValidator.__init__(self, valid_chars=chars)
         self.dialog = dialog
     def Clone(self):
         return OptionValidator(self.dialog, self.chars)
     def Validate(self, win):
         textCtrl = self.GetWindow()
         text = textCtrl.GetValue()
         if not ws.BasicValidator.Validate(self, win):
             self.dialog.SetErrorMessage("Invalid value for the %s option" % textCtrl.optionName)
             textCtrl.SetFocus()
             textCtrl.Refresh()
         if len(text) == 0:
             self.dialog.SetErrorMessage("Please supply a value for the %s option" % textCtrl.optionName)
             textCtrl.SetFocus()
             textCtrl.Refresh()
             return False
         else:
             textCtrl.SetBackgroundColour(
                 wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
             textCtrl.Refresh()
             return True
     def TransferToWindow(self):
         return True
     def TransferFromWindow(self):
         return True # Prevent wxDialog from complaining.

# FIXME: I was having a variety of issues getting the layout
# to work properly (not sure why).  The result is a little convoluted
# Should make a pass at this and clean up so that it's easy to
# add new preference settings without much work.

class PreferencesDialog(wx.Dialog):

    def __init__(self, opts, parent, requireEdits=False,
        headingText = None,
        helpText = None, testFailed=False, defaultsOnly=False, **kargs):
        wx.Dialog.__init__(self, parent, **kargs)
        self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY)
        self.requireEdits = requireEdits
        self.helpText = helpText
        self.testFailed = testFailed
        self.headingText = headingText
        self.defaultsOnly = defaultsOnly
        self.ctrls = {}
        self.filebuttons = {}
        self.sshOptions = opts # reference
        self.opts = copy.copy(opts)
        # Expand with tabs when there are more preferences
        # Create notebook, pages and buttons
        #self.nb = wx.Notebook(self, style=wx.BK_DEFAULT)
        self.toolsPanel = self._createToolsPanel(self)
        #self.graphsPanel = self._createGraphsPanel()
        #self.nb.AddPage(self.toolsPanel, "SiLK Tools")
        # FIXME: create this manually?
        szBtns = wx.BoxSizer(wx.HORIZONTAL)
        if not defaultsOnly:
            btnSetDefaults = wx.Button(self, -1, "Use Settings as Default")
        btnCancel = wx.Button(self, -1, "Cancel")
        if self.defaultsOnly:
            btnOK = wx.Button(self, -1, "Use Settings as Default")
        else:
            btnOK = wx.Button(self, -1, "Apply Settings")
        btnOK.SetDefault()
        self.SetEscapeId(btnCancel.GetId())
        szBtns.Add(btnCancel, flag=wx.RIGHT, border=16)
        if not defaultsOnly:
            szBtns.Add(btnSetDefaults, flag=wx.RIGHT, border=16)
        szBtns.Add(btnOK, flag=wx.RIGHT, border=8)
        #self.CreateButtonSizer(wx.YES | wx.NO| wx.CANCEL)
        
        if requireEdits:
            btnCancel.SetLabel("Quit")
            #self.FindWindowById(wx.ID_CANCEL).SetLabel("Quit")
        #self.FindWindowById(wx.ID_YES).SetLabel("Configure")
        #self.FindWindowById(wx.ID_YES).SetLabel("Use Settings as Defaults")
        # Layout notebook and buttons in pref_sz
        pref_sz = wx.BoxSizer(wx.VERTICAL)
        pref_sz.Add(self.toolsPanel, flag=wx.ALL, border=16)
        # Common buttons
        pref_sz.Add(szBtns, flag=wx.ALIGN_RIGHT|wx.ALL, border=10)
        pref_sz.Add((5,5), flag=wx.ALL, border=2)
        self.SetSizerAndFit(pref_sz)
        self.ctrls["REMOTE_HOST"][1].SetFocus()
        self.Bind(wx.EVT_BUTTON, self.OnOK, btnOK)
        if not defaultsOnly:
            self.Bind(wx.EVT_BUTTON, self.OnSaveDefaults, btnSetDefaults)
        
    # FIXME: Why doesn't wx.Dialog do this for me?
    def Validate(self):
        print "In Validate"
        return wx.Dialog.Validate(self)
    
    # Save as the defaults
    def OnSaveDefaults(self, event):
        if self.Validate():
            self.SaveDefaultPreferences()
            if (self.IsModal()):
                self.EndModal(wx.ID_OK)
            else:
                self.SetReturnCode(wx.ID_OK)
                self.Show(False)
        else:
            print "Failed to validate"
    
    def OnOK(self, event):
        if self.Validate():
            if (self.IsModal()):
                self.EndModal(wx.ID_OK)
            else:
                self.SetReturnCode(wx.ID_OK)
                self.Show(False)
                
    def SetErrorMessage(self, msg):
        self.message.SetLabel(msg)
        self.message.SetForegroundColour(wx.RED)
        
    def _createToolsPanel(self, parent):
        self.required = [
        "REMOTE_HOST", "REMOTE_PORT",
        "REMOTE_USER",  "RSH_KEY", 
        "REMOTE_OUTPUT_DIR", "REMOTE_LIBRARY_PATH"
        ]
        
        self.prefs = [#"WORK_REMOTE",
        #"LOCAL_SILK_BIN", "LOCAL_OUTPUT_DIR",
            "REMOTE_HOST", "REMOTE_PORT",
            "REMOTE_USER",  "RSH_KEY", #"REMOTE_BIN", 
            "REMOTE_OUTPUT_DIR", "REMOTE_LIBRARY_PATH"]
            
        self.valid_chars = {
            "REMOTE_PORT" : "0123456789",
            "REMOTE_OUTPUT_DIR" : string.letters + string.digits + "/\\+-.",
            "REMOTE_LIBRARY_PATH" : string.letters + string.digits + "/\\+-.:"
        }
                 
        if not DISABLE_EXTERNAL_SSH:         
            self.prefs.extend(["USE_EXTERNAL_SSH",
                "RSH_CMD", "FILE_DOWNLOAD_CMD"])         
                 
        toolsPanel = wx.Panel(parent)
        sz = wx.GridBagSizer()
        def _ctrls(opt_name):
            if opt_name == "WORK_REMOTE":
                    self.ctrls[opt_name] = (None,
                            wx.CheckBox(toolsPanel, -1,
                             " Work remotely (leave results on remote host)",
                             size=(250,-1)
                             ))
                    val = self.opts[opt_name]
                    print "Option = '%s'" % val 
                    if val == "True":
                        self.ctrls[opt_name][1].SetValue(wx.CHK_CHECKED)
                    else:
                        self.ctrls[opt_name][1].SetValue(wx.CHK_UNCHECKED)
            elif opt_name == "USE_EXTERNAL_SSH":
                self.ctrls[opt_name] = (None,
                        wx.CheckBox(toolsPanel, -1,
                         " Use an external SSH client"))
                val = self.opts[opt_name]
                print "Option = '%s'" % val 
                if val == "True":
                    self.ctrls[opt_name][1].SetValue(wx.CHK_CHECKED)
                else:
                    self.ctrls[opt_name][1].SetValue(wx.CHK_UNCHECKED)                
            else:
                label = wx.StaticText(toolsPanel, -1, opt_name,
                            style=wx.ALIGN_RIGHT, size=(150,-1))
                if opt_name in self.required:
                    chars = self.valid_chars.get(opt_name, None)
                    ctrl =  wx.TextCtrl(toolsPanel, -1,
                               self.opts.get(opt_name, ""), size=(300,-1),
                               validator=OptionValidator(self, chars))
                else:
                    ctrl =  wx.TextCtrl(toolsPanel, -1,
                               self.opts[opt_name], size=(300,-1))
                
                #ctrl.SetLabel(opt_name)
                ctrl.optionName = opt_name
                self.ctrls[opt_name] = (label, ctrl)
            return self.ctrls[opt_name]
        # Create each of the controls
        for o in self.prefs:
            c = _ctrls(o)
            
        def _addstrctrl(opt_name, _row):
            label_flags = wx.RIGHT|wx.TOP|wx.BOTTOM|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL
            szRow = wx.FlexGridSizer(1,2)   
            szRow.Add(self.ctrls[opt_name][0], flag=label_flags, border=8)
            szRow.Add(self.ctrls[opt_name][1], flag=wx.ALL, border = 8)
            sz.Add(szRow, (_row,0), (1,2))          
            return _row + 1
        def _addchoosefilebutton(opt_name, _row):
            szButton = wx.FlexGridSizer(1,2)
            self.filebuttons[opt_name] = wx.Button(toolsPanel, -1, "Choose key file...")
            self.Bind(wx.EVT_BUTTON,  self.OnChooseFile, self.filebuttons[opt_name])
            szButton.Add((0,0))
            szButton.Add(self.filebuttons[opt_name], flag=wx.ALL|wx.ALIGN_RIGHT, border=8)
            sz.Add(szButton, (_row,0), (1,2), flag=wx.ALL|wx.ALIGN_RIGHT)          
            return _row + 1
        def _addboolctrl(opt_name, _row):
            sz.Add(self.ctrls[opt_name][1], (_row,0), (1,2), flag=wx.LEFT|wx.ALIGN_LEFT, border = 64)
            return _row + 1
        def _addheading(heading, _row, enable=True):
            "toolsPanel = %s" % toolsPanel
            txt = wx.StaticText(toolsPanel, -1, heading)
            txt.SetFont(ws.Font(wx.SWISS, wx.NORMAL, wx.BOLD))
            if not enable: txt.Enable(False)
            szHeading = wx.FlexGridSizer(1,2)    
            szHeading.Add(txt, flag=wx.ALL|wx.ALIGN_LEFT, border=4)
            szHeading.Add(wx.StaticLine(toolsPanel, -1, size=(300,1),
                style=wx.LI_HORIZONTAL),
                flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=4)
            #sz.Add((0,0), (_row, 0), (1,2))
            #_row = _row + 1
            sz.Add(szHeading, (_row,0), (1,2), flag=wx.ALL|wx.ALIGN_LEFT, border=8 )
            szHeading.AddGrowableCol(1)
            return _row + 1
        def _addtitle(text, _row):
            self.title = txt = ws.XLgStaticText(toolsPanel, -1, text)
            print self.title
            sz.Add(txt, (_row,0), (1,2), flag=wx.ALL|wx.ALIGN_LEFT, border=4)
            return _row + 1
        def _setmessage(text, _row):
            self.message = txt = wx.StaticText(toolsPanel, -1, text)
            if self.testFailed:
                self.message.SetFont(ws.Font(wx.SWISS, wx.NORMAL, wx.NORMAL))
            self.message.Wrap(425)
            sz.Add(txt, (_row,0), (1,2), flag=wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.ALIGN_LEFT, border=8)
            return _row + 1
                        
        def _addhelptext(text, _row):
            self.helptext = txt = ws.XSmStaticText(toolsPanel, -1, text, style=wx.ALIGN_LEFT)
            self.helptext.Wrap(400)
            sz.Add(txt, (_row,0), (1,2), flag=wx.LEFT|wx.ALIGN_LEFT, border=32)
            return _row + 1
            
        # Pack everything
        _row = 0
        
        if self.headingText:
            _row = _addtitle(self.headingText, _row)

        if self.helpText:
            _row = _setmessage(self.helpText, _row)
        else:
            _row = _setmessage("Specify the SSH configuration for this problem set.", _row)   
        #_row = _addheading("General Preferences", _row, False)
        #_row = _addboolctrl("WORK_REMOTE", _row)
        #_row = _addheading("Local Tool Config", _row, False)
        #_row = _addstrctrl("LOCAL_SILK_BIN", _row)
        #_row = _addstrctrl("LOCAL_OUTPUT_DIR", _row)
        _row = _addheading("SSH Configuration", _row)
        _row = _addstrctrl("REMOTE_HOST", _row)
        _row = _addstrctrl("REMOTE_PORT", _row)
        _row = _addstrctrl("REMOTE_USER", _row)      
        _row = _addhelptext("iSiLK will connect as REMOTE_USER@REMOTE_HOST on REMOTE_PORT.", _row)
        _row = _addstrctrl("RSH_KEY", _row)
        _row = _addhelptext("Local path to your private key file.", _row)
        _row = _addchoosefilebutton("RSH_KEY", _row)
        #_row = _addstrctrl("REMOTE_BIN", _row)
        #_row = _addhelptext("Directory on the server where SiLK is installed (useful if not in your PATH).", _row)        
        _row = _addheading("Remote Directories", _row)        
        _row = _addstrctrl("REMOTE_OUTPUT_DIR", _row)
        _row = _addhelptext("Remote directory where iSiLK should store your output files.", _row)
        _row = _addstrctrl("REMOTE_LIBRARY_PATH", _row)  
        _row = _addhelptext("Directories iSiLK should search for library and import files.\nSeparate multiple directories with colons.", _row)      
        
        if not DISABLE_EXTERNAL_SSH:
            _row = _addboolctrl("USE_EXTERNAL_SSH", _row)
            _row = _addstrctrl("RSH_CMD", _row)
            _row = _addstrctrl("FILE_DOWNLOAD_CMD", _row)
        
        # FIXME: Only enable working remotely for right now
#        self.ctrls["WORK_REMOTE"][0].Enable(False)        
        #self.ctrls["LOCAL_SILK_BIN"][0].Enable(False)
        #self.ctrls["LOCAL_OUTPUT_DIR"][0].Enable(False)        
        #self.ctrls["WORK_REMOTE"][1].Enable(False)        
        #self.ctrls["LOCAL_SILK_BIN"][1].Enable(False)
        #self.ctrls["LOCAL_OUTPUT_DIR"][1].Enable(False)                
        
        toolsPanel.SetSizerAndFit(sz)
        #width = self.GetSize()[0] *1.2
        #if self.helpText:
        ##    self.helptext.SetLabel(self.helpText)
        #    self.helptext.Wrap(width)
        #toolsPanel.SetSizerAndFit(sz)
        return toolsPanel
        
        
    def OnChooseFile(self, evt):
        print "Choosing file"
        dlg = wx.FileDialog(self, "Select key file")
        rv = dlg.ShowModal()
        if rv != wx.ID_OK: return
        self.ctrls["RSH_KEY"][1].SetValue(dlg.GetPath())
        
        
    def SavePreferences(self):
        for o in self.prefs:
            print "Updating silk option: %s = %s" % (o, self.ctrls[o][1].GetValue())
            if o == "WORK_REMOTE" or o == "USE_EXTERNAL_SSH":
                self.sshOptions[o] = str(self.ctrls[o][1].GetValue()==wx.CHK_CHECKED)
            else:
                self.sshOptions[o] = self.ctrls[o][1].GetValue()
        if self.defaultsOnly:
            self.SaveDefaultPreferences()

    def SaveDefaultPreferences(self):
        # Validate these are good first
        
        
        for o in self.prefs:
            print "Updating silk option: %s = %s" % (o, self.ctrls[o][1].GetValue())
            if o == "WORK_REMOTE" or o == "USE_EXTERNAL_SSH":
                config.silk.opts[o] = str(self.ctrls[o][1].GetValue()==wx.CHK_CHECKED)
            else:
                config.silk.opts[o] = self.ctrls[o][1].GetValue()
        config._save_prefs("silk", config.silk.opts, self.prefs)
        config.silk.dump()
    
    def GetOpts(self):
        pass
