#==============================================================================#
#        GeoLogger - Open-source, Field Geotechnical Database Tool             #
#                                                                              #
#    Copyright (c)  2012 David Merritt/Midway Gold Corp.                       #
#                                                                              #
#                                                                              #
#    This file is part of GeoLogger.                                           #
#                                                                              #
#    GeoLogger 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.                                           #
#                                                                              #
#    GeoLogger 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 GeoLogger.  If not, see <http://www.gnu.org/licenses/>.        #
#                                                                              #
#                                                                              #
#                                                                              #
#  Developed by: David Merritt                                                 #
#                Midway Gold Corporation                                       #
#                                                                              #
#                                                                              #
#      Requirements:                                                           #
#         Python version 2.5 or higher                                         #
#         wxPython version 2.8.10 or higher                                    #
#==============================================================================#
#    rungrid.py provides the data entry screen for entering a new core run     #
#    with its features and saving it in the database.                          #
#==============================================================================#


import wx
import wx.grid
import validators
import dbi

ALPHA = 1
DIGIT = 2
DECIMAL = 3

class CoreRunEntry(wx.Panel):
    """Provides the fields and a grid for entering run data for a borehole
    interval, and a grid for the structural features within that run"""
    
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        
        self.SetBackgroundColour(parent.appcolor)
        self.logo = parent.geologo
        self.logoImage = wx.StaticBitmap(self, -1, wx.BitmapFromImage(self.logo))
        
        parent.Title = "Add New Run"

        self.parent = parent
        self.db_cursor = parent.db_cursor
        
        # initialize reference variables
        self.boholeref = ''
        self.projectref = ''
        self.depthref = 0
        self.startref = 0
        self.endref = 0
        self.botdepthref = 0
        self.depthMatch = False
        self.previouscell = (0, 0)
        self.featuredepth = True
        self.count = 0

        
        # Drop down lists
        self.strengthIndex = ["NA", "R1", "R2", "R3", "R4", "R5", "R6", 
                    "S1", "S2", "S3", "S4", "S5", "S6"]
        self.alterationIndex = ["NA", "A1", "A2", "A3", "A4", "A5", 
                    "S1", "S2", "S3", "S4", "S5"]
                    
        self.loginmsg = wx.StaticText(self, -1, "Add Core Run ")
        self.loginmsg.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = ""))
        
        # Spacer
        self.spacer = wx.StaticText(self, -1, "   ")
        
        # Borehole Selection Widget
        self.idLabel = wx.StaticText(self, -1, "Borehole ID: ")
        self.idLabel.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.idLabel.SetSize(self.idLabel.GetBestSize())
        self.idField = wx.TextCtrl(self, -1, '', size=(200, -1), style=wx.TE_LEFT)
        self.idField.SetInsertionPoint(0)
        self.idField.Bind(wx.EVT_KILL_FOCUS, self.OnRetrieveBorehole)
        if self.parent.current_borehole:
            self.idField.SetValue(self.parent.current_borehole)
            
        self.dateLabel = wx.StaticText(self, -1, "Run Date: ")
        self.dateLabel.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.dateLabel.SetSize(self.idLabel.GetBestSize())
        self.dateField = wx.DatePickerCtrl(self, size=(120,-1),
                                style = wx.TAB_TRAVERSAL
                                      | wx.DP_DROPDOWN
                                      | wx.DP_SHOWCENTURY
                                      | wx.DP_ALLOWNONE )
        self.Bind(wx.EVT_DATE_CHANGED, self.OnDateChanged, self.dateField)
        
        # Header Labels
        self.drillDataLabel = wx.StaticText(self, -1, "Drill Data")
        self.drillDataLabel.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.geologyLabel = wx.StaticText(self, -1, "Geology")
        self.geologyLabel.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.rmrLabel = wx.StaticText(self, -1, "Rock Mass Rating (RMR76)")
        self.rmrLabel.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.faultsLabel = wx.StaticText(self, -1, "Faults")
        self.faultsLabel.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        
        
        # Field Labels
        self.runFromLabel = wx.StaticText(self, -1, "From")
        self.runFromLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.runToLabel = wx.StaticText(self, -1, "To")
        self.runToLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.drilledLabel = wx.StaticText(self, -1, "Drilled")
        self.drilledLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.recoveredLabel = wx.StaticText(self, -1, "Rcvd")
        self.recoveredLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.lithologyLabel = wx.StaticText(self, -1, "Lith")
        self.lithologyLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.alterationLabel = wx.StaticText(self, -1, "Alt")
        self.alterationLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.rqdLabel = wx.StaticText(self, -1, "RQD")
        self.rqdLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.strengthLabel = wx.StaticText(self, -1, "Strgth")
        self.strengthLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.fracturesLabel = wx.StaticText(self, -1, "# Fracs")
        self.fracturesLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.jcrLabel = wx.StaticText(self, -1, "JCR")
        self.jcrLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.solidLabel = wx.StaticText(self, -1, "Solid")
        self.solidLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.rubbleLabel = wx.StaticText(self, -1, "Rubble")
        self.rubbleLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.gougeLabel = wx.StaticText(self, -1, "Gouge")
        self.gougeLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        self.runCommentLabel = wx.StaticText(self, -1, "Run Comment")
        self.runCommentLabel.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = ""))
        
        # Data Fields
        self.runFromField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DECIMAL))
        self.runFromField.Bind(wx.EVT_KILL_FOCUS, self.FormatFloat)
        self.runToField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DECIMAL))
        self.runToField.Bind(wx.EVT_KILL_FOCUS, self.OnMatchDepths)
        self.runToField.Bind(wx.EVT_KILL_FOCUS, self.FormatFloat)
        self.drilledField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_READONLY,
                validator=validators.MyValidator(DECIMAL))
        self.recoveredField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DECIMAL))
        self.recoveredField.Bind(wx.EVT_KILL_FOCUS, self.FormatFloat)
        self.recoveredField.Bind(wx.EVT_KILL_FOCUS, self.OnNoRecovery)
        self.lithologyField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_LEFT)
        self.alterationField = wx.Choice(self, -1, size=(52, -1), choices=self.alterationIndex)
        self.rqdField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DECIMAL))
        self.rqdField.Bind(wx.EVT_KILL_FOCUS, self.FormatFloat)
        self.strengthField = wx.Choice(self, -1, size=(52, -1), choices=self.strengthIndex)
        self.fracturesField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DIGIT))
        self.fracturesField.Bind(wx.EVT_KILL_FOCUS, self.FormatInt)
        self.jcrField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DIGIT))
        self.jcrField.Bind(wx.EVT_KILL_FOCUS, self.FormatInt)
        self.solidField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DECIMAL))
        self.solidField.Bind(wx.EVT_KILL_FOCUS, self.FormatFloat)
        self.rubbleField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DECIMAL))
        self.rubbleField.Bind(wx.EVT_KILL_FOCUS, self.FormatFloat)
        self.gougeField = wx.TextCtrl(self, -1, '', size=(45, -1), style=wx.TE_RIGHT,
                validator=validators.MyValidator(DECIMAL))
        self.gougeField.Bind(wx.EVT_KILL_FOCUS, self.FormatFloat)
        self.runCommentField = wx.TextCtrl(self, -1, '', size=(150, -1), style=wx.TE_RIGHT | wx.TE_WORDWRAP)

        
        # Buttons
        self.savebutton = wx.Button(self, -1, "Save", size=(100,40))
        self.Bind(wx.EVT_BUTTON, self.OnRunSave, self.savebutton)
        self.nextbutton = wx.Button(self, -1, "Next Run", size=(100,40))
        self.Bind(wx.EVT_BUTTON, self.OnNextRun, self.nextbutton)
        self.cancelbutton = wx.Button(self, -1, "Cancel", size=(100,40))
        self.Bind(wx.EVT_BUTTON, parent.OnPortalReturn, self.cancelbutton)
        
        
        # Column labels for features in run            
        self.colLabels = ["Depth", "Type", "Quant", "Alpha", "Beta", "Conf", 
                    "Infill", "Thick", "Plan", "Rough", "Feature Comment"]
        self.rowcount = 9
        self.featuregrid = wx.grid.Grid(self)
        self.featuregrid.SetDefaultColSize(50, True)
        self.featuregrid.SetDefaultRowSize(20, True)
        self.featuregrid.CreateGrid(self.rowcount,11)
        self.featuregrid.SetRowLabelSize(0)     # Don't display row labels
        self.featuregrid.SetColLabelSize(20)
        self.featuregrid.SetColSize(0,45)
        self.featuregrid.SetColSize(2,45)
        self.featuregrid.SetColSize(3,45)
        self.featuregrid.SetColSize(4,45)
        self.featuregrid.SetColSize(7,45)
        self.featuregrid.SetColSize(10,200)
        self.featuregrid.SetDefaultCellFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
        self.featuregrid.SetDefaultCellBackgroundColour((240,240,240))
        
        self.typecode = ["NA", "J1", "J2", "J3", "B1", "B2", "B3", "BT", "FO1", "FO2",
                    "FO3", "FOT", "V1", "V2", "V3", "VT", "FB1", "FB2", "FB3",
                    "FBT", "C1", "C2", "C3", "CT", "F", "S"]
                    
        self.confcode = ["1", "2", "3"]
        self.infillcode = ["NA", "ca", "cl", "si", "qtz", "chl", "g", "bx", "hm", "lm", "fe", "ox"]
        self.plancode = ["NA", "Pl", "C", "U", "ST", "I"]
        self.roughcode = ["NA", "P", "K", "SM", "R", "VR"]
        
        for row in range(self.rowcount):
            self.featuregrid.SetCellEditor(row,0,wx.grid.GridCellFloatEditor(precision=1))
            self.featuregrid.SetCellEditor(row,1,wx.grid.GridCellChoiceEditor(self.typecode))
            self.featuregrid.SetCellEditor(row,2,wx.grid.GridCellNumberEditor())
            self.featuregrid.SetCellEditor(row,3,wx.grid.GridCellNumberEditor())
            self.featuregrid.SetCellEditor(row,4,wx.grid.GridCellNumberEditor())  
            self.featuregrid.SetCellEditor(row,5,wx.grid.GridCellChoiceEditor(self.confcode))
            self.featuregrid.SetCellEditor(row,6,wx.grid.GridCellChoiceEditor(self.infillcode))
            self.featuregrid.SetCellEditor(row,7,wx.grid.GridCellFloatEditor(precision=1))
            self.featuregrid.SetCellEditor(row,8,wx.grid.GridCellChoiceEditor(self.plancode))
            self.featuregrid.SetCellEditor(row,9,wx.grid.GridCellChoiceEditor(self.roughcode))
            
        for col in range(11):
            self.featuregrid.SetColLabelValue(col, self.colLabels[col])
            self.featuregrid.SetLabelFont(wx.Font(8, wx.SWISS, wx.ITALIC, wx.NORMAL))
        
        self.featuregrid.SetColFormatFloat(0,2,1)
        self.featuregrid.SetColFormatFloat(7,2,1)
          
            
        # call method for handling different cell events
        self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnCellChange) 
 

        # Sizers and Screen arrangement
        self.outersizer = wx.BoxSizer(wx.VERTICAL)
        self.loginsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.bhHeadersizer = wx.BoxSizer(wx.HORIZONTAL)
        self.runsizer = wx.FlexGridSizer(rows=2, cols=14, vgap=1, hgap=5)
        self.featuregridsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.buttonsizer = wx.BoxSizer(wx.VERTICAL)
        
        self.loginsizer.Add(self.loginmsg, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 20)
        self.loginsizer.Add(self.idLabel, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 30)
        self.loginsizer.Add(self.idField, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
        self.loginsizer.Add(self.spacer, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
        self.loginsizer.Add(self.dateLabel, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 10)
        self.loginsizer.Add(self.dateField, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
        
        self.bhHeadersizer.Add((20, 0))
        self.bhHeadersizer.Add(self.drillDataLabel, 0, wx.TOP | wx.BOTTOM, 20)
        self.bhHeadersizer.Add((110, 0))
        self.bhHeadersizer.Add(wx.StaticLine(self, size=(1, 60), style=wx.LI_VERTICAL), 0, wx.LEFT |wx.RIGHT, 10)
        self.bhHeadersizer.Add(self.geologyLabel, 0, wx.TOP | wx.BOTTOM, 20)
        self.bhHeadersizer.Add(wx.StaticLine(self, size=(1, 60), style=wx.LI_VERTICAL), 0, wx.LEFT, 22)
        self.bhHeadersizer.Add((10, 0))
        self.bhHeadersizer.Add(self.rmrLabel, 0, wx.TOP | wx.BOTTOM, 20)
        self.bhHeadersizer.Add(wx.StaticLine(self, size=(1, 60), style=wx.LI_VERTICAL), 0, wx.LEFT | wx.RIGHT, 12)
        self.bhHeadersizer.Add(self.faultsLabel, 0, wx.TOP | wx.BOTTOM, 20)
        
        self.runsizer.Add(self.runFromLabel, 0, 0)
        self.runsizer.Add(self.runToLabel, 0, 0)
        self.runsizer.Add(self.drilledLabel, 0, 0)
        self.runsizer.Add(self.recoveredLabel, 0, 0)
        self.runsizer.Add(self.lithologyLabel, 0, 0)
        self.runsizer.Add(self.alterationLabel, 0, 0)
        self.runsizer.Add(self.rqdLabel, 0, 0)
        self.runsizer.Add(self.strengthLabel, 0, 0)
        self.runsizer.Add(self.fracturesLabel, 0, 0)
        self.runsizer.Add(self.jcrLabel, 0, 0)
        self.runsizer.Add(self.solidLabel, 0, 0)
        self.runsizer.Add(self.rubbleLabel, 0, 0)
        self.runsizer.Add(self.gougeLabel, 0, 0)
        self.runsizer.Add(self.runCommentLabel, 0, 0)
        
        self.runsizer.Add(self.runFromField, 0, 0)
        self.runsizer.Add(self.runToField, 0, 0)
        self.runsizer.Add(self.drilledField, 0, 0)
        self.runsizer.Add(self.recoveredField, 0, 0)
        self.runsizer.Add(self.lithologyField, 0, 0)
        self.runsizer.Add(self.alterationField, 0, 0)
        self.runsizer.Add(self.rqdField, 0, 0)
        self.runsizer.Add(self.strengthField, 0, 0)
        self.runsizer.Add(self.fracturesField, 0, 0)
        self.runsizer.Add(self.jcrField, 0, 0)
        self.runsizer.Add(self.solidField, 0, 0)
        self.runsizer.Add(self.rubbleField, 0, 0)
        self.runsizer.Add(self.gougeField, 0, 0)
        self.runsizer.Add(self.runCommentField, 0, 0)
        
        self.featuregridsizer.Add(self.featuregrid, 0, wx.ALL, 5)
        self.buttonsizer.Add(self.nextbutton, 0, wx.ALL, 5)
        self.buttonsizer.Add(self.savebutton, 0, wx.ALL, 5)
        self.buttonsizer.Add(self.cancelbutton, 0, wx.ALL, 5)
        self.featuregridsizer.Add(self.buttonsizer, 0, wx.ALL, 5)
        
        self.outersizer.Add(self.loginsizer, 0, wx.EXPAND | wx.TOP, 90)
        self.outersizer.Add(wx.StaticLine(self, size=(995, 2)), 0, wx.TOP, 20)
        self.outersizer.Add(self.bhHeadersizer, 0, 0)
        self.outersizer.Add(self.runsizer, 0, wx.LEFT, 20)
        self.outersizer.Add(self.featuregridsizer, 0, wx.ALL, 20)
        
        self.SetSizer(self.outersizer)
        self.outersizer.Fit(self)
        
        # set tab order
        taborder = (self.idField, self.dateField, self.runFromField, self.runToField,self.recoveredField, 
                    self.lithologyField, self.alterationField, self.rqdField, self.strengthField, 
                    self.fracturesField, self.jcrField, self.solidField, self.rubbleField, 
                    self.gougeField, self.runCommentField, self.featuregrid)
        for i in xrange(len(taborder)-1):
            taborder[i+1].MoveAfterInTabOrder(taborder[i])
    
    
    def OnRunSave(self, evt):
        """Checks data validity and saves run record"""
        self.saved = False
        self.success = False
        # Check that all required fields are entered
        self.emptyfield = ''
        if not self.runFromField.GetValue():
            self.emptyfield = "Depth From"
        elif not self.runToField.GetValue():
            self.emptyfield = "Depth To"
        elif not self.drilledField.GetValue():
            self.emptyfield = "Drilled"
        elif not self.recoveredField.GetValue():
            self.emptyfield = "Rcvd"
        elif not self.lithologyField.GetValue():
            self.emptyfield = "Lith"
        elif not self.alterationField.GetStringSelection():
            self.emptyfield = "Alt"
        elif not self.rqdField.GetValue():
            self.emptyfield = "RQD"
        elif not self.strengthField.GetStringSelection():
            self.emptyfield = "Strgth"
        elif not self.fracturesField.GetValue():
            self.emptyfield = "Fracs"
        elif not self.jcrField.GetValue():
            self.emptyfield = "JCR"
        elif not self.solidField.GetValue():
            self.emptyfield = "Solid"
        elif not self.rubbleField.GetValue():
            self.emptyfield = "Rubble"
        elif not self.gougeField.GetValue():
            self.emptyfield = "Gouge"
            
        else:      
            # Fields are entered, now check for depth errors
            self.depthErrorMessage = ''
            
            # Format run data
            runFrom = float(self.runFromField.GetValue())
            runTo = float(self.runToField.GetValue())
            drilled = float(self.drilledField.GetValue())
            recovered = float(self.recoveredField.GetValue())
            lithology = self.lithologyField.GetValue()
            alteration = self.alterationField.GetStringSelection()
            rqd = self.rqdField.GetValue()
            strength = self.strengthField.GetStringSelection()
            fractures = int(self.fracturesField.GetValue())
            jcr = int(self.jcrField.GetValue())
            solid = float(self.solidField.GetValue())
            rubble = float(self.rubbleField.GetValue())
            gouge = float(self.gougeField.GetValue())
            runComment = self.runCommentField.GetValue()
            
            if runFrom > self.botdepthref:
                self.depthErrorMessage = "Starting depth exceeds the last recorded depth from this hole \n"
                self.depthErrorMessage += "Please Correct depth gap"
            elif runFrom < self.botdepthref:
                self.depthErrorMessage = "Starting depth overlaps the last recorded depth from this hole \n"
                self.depthErrorMessage += "Please Correct depth overlap"
            elif runTo < runFrom:
                self.depthErrorMessage = "The end depth of a run cannot be less than its starting depth!"
            elif runFrom > self.depthref:
                self.depthErrorMessage = "Starting depth exceeds the total depth of this hole"
            elif runTo > self.depthref:
                self.depthErrorMessage = "Ending depth exceeds the total depth of this hole"
            elif solid + rubble + gouge > recovered:
                self.depthErrorMessage = "Solid, rubble, and gouge sum is greater than the recovered interval"
            elif solid + rubble + gouge < recovered:
                self.depthErrorMessage = "Solid, rubble, and gouge sum is less than the recovered interval"
                
            else:
                
                self.runfields = (runFrom, runTo, drilled, recovered, lithology,
                    alteration, rqd, strength, fractures, jcr, solid, rubble,
                    gouge, runComment)
                    
                # Verify the feature grid data
                self.featurefields = self.returnfeatures(self.featuregrid)
                if self.featurefields:
                    self.updateresult = dbi.runcreate(self.runfields, self.db_cursor, self.parent.geologger_db)
                
                    if self.updateresult == "success":
                        self.saved = True
                        updatemsg = "Core Run Successfully Saved."
                        dlg = wx.MessageDialog(self, updatemsg, "Core Run Saved!", wx.OK|wx.ICON_INFORMATION)
                        dlg.ShowModal()
                        dlg.Destroy()
                    
                    # If the run is saved, so is the feature grid
                
                
            if self.depthErrorMessage:
                dlg = wx.MessageDialog(self, self.depthErrorMessage, "Depth Error!", wx.OK|wx.ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
                
        if self.emptyfield:
            self.emptyfieldmessage = "Please complete the field " + self.emptyfield
            dlg = wx.MessageDialog(self, self.emptyfieldmessage, "Empty Field!", wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            
        
        
    def OnNextRun(self, event):
        pass

    
    def OnRetrieveBorehole(self, evt):
        """Gets relevant data from the borehole that runs are being added to,
        allowing for field validation"""

        searchvalue = (self.idField.GetValue(), )
        bhcommand1 = "select borehole_id, of_project, depth, start_date, end_date "
        bhcommand2 = "from borehole where borehole_id = ?"
        self.db_cursor.execute(bhcommand1+bhcommand2, searchvalue)
        row = self.db_cursor.fetchone()
        if row != None:
            self.boholeref = row[0]
            self.projectref = row[1]
            self.depthref = row[2]
            self.startref = row[3]
            self.endref = row[4]
            # retrieve any previous runs for this borehole
            runcommand = "select bottom_depth from core_run where of_borehole = ?"
            self.db_cursor.execute(runcommand, searchvalue)
            row = self.db_cursor.fetchone()
            if row != None:
                self.botdepthref = row[0]
        else:
            self.failuremessage = "The requested borehole does not exist in the database"
            dlg = wx.MessageDialog(self, self.failuremessage, "Retrieval Fail!", wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
                
                
            
    def returnfeatures(self, featuregrid):
        """Checks the data in the run features section and readies it for the database"""
        # There should be 11 columns and undefined number of rows
        # Loop through the rows
        self.goodcells = True
        self.failuremessage = 'features ok'
        self.title = 'Success!'
        featuredepths = []
        for row in range(self.rowcount):
            if (float(featuregrid.GetCellValue(row, 0)) > float(self.runfields[1]) or 
                float(featuregrid.GetCellValue(row, 0)) < float(self.runfields[0])):
                self.failuremessage = "Feature depth in row " + str(self.rowcount+1) + " is outside run interval"
                self.title = "Depth Error!"
                self.goodcells = False
                break
            
            elif featuregrid.GetCellValue(row, 1) == "":
                self.failuremessage = "Feature Type cannot be blank"
                self.title = "No Feature Type"
                self.goodcells = False
                break       
                
            elif int(featuregrid.GetCellValue(row, 3)) > 90 or int(featuregrid.GetCellValue(row, 3)) < 0:
                self.failuremessage = "Alpha Angle  in row " + str(self.rowcount+1) + " must be between 0 and 90 degrees"
                self.title = "Bad Alpha!"
                self.goodcells = False
                break
                
            elif int(featuregrid.GetCellValue(row, 4)) > 360 or int(featuregrid.GetCellValue(row, 3)) < 0:
                self.failuremessage = "Beta Angle  in row " + str(self.rowcount+1) + " must be between 0 and 360 degrees"
                self.title = "Bad Beta!"
                self.goodcells = False
                break
            
            else:
                featuredepths.append(featuregrid.GetCellValue(row, 0))
            

        # Check for duplicated feature depths
        for depth in range(len(featuredepths)):
            if featuredepths.count(featuredepths[depth]) > 1:
                # We have a duplicate depth
                self.failuremessage = "Depth at " + str(featuredepths[depth]) + " is not unique!"
                self.title = "Duplicate Depths!"
                self.goodcells = False
                break
            
        if not self.goodcells:
            dlg = wx.MessageDialog(self, self.failuremessage, self.title, wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()                 
            
        return self.goodcells
            
            
    def OnCellChange(self, event):
        """Check the validity of entered values within the feature grid"""
        # identify the cell that has become active
        featurerow = event.GetRow()
        featurecol = event.GetCol() 
        cellvalue = self.featuregrid.GetCellValue(event.GetRow(), event.GetCol())

        # Ensure the feature depth is within the run interval
        if featurecol == 0:
            if float(cellvalue) < float(self.runFromField.GetValue()) or float(cellvalue) > float(self.runToField.GetValue()):
                errormessage = "Depth provided is outside the current run interval"
                dlg = wx.MessageDialog(self, errormessage, "Bad Feature Depth", wx.OK|wx.ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
                self.featuregrid.SetCellValue(featurerow, featurecol, self.runFromField.GetValue())
                self.featuredepth = False
                event.Skip()
                
            if featurerow == (self.rowcount - 1):
                self.featuregrid.AppendRows(numRows=1)
                self.featuregrid.SetCellEditor(self.rowcount,0,wx.grid.GridCellFloatEditor(precision=1))
                self.featuregrid.SetCellEditor(self.rowcount, 1, wx.grid.GridCellChoiceEditor(self.typecode))
                self.featuregrid.SetCellEditor(self.rowcount, 2, wx.grid.GridCellNumberEditor())
                self.featuregrid.SetCellEditor(self.rowcount, 3, wx.grid.GridCellNumberEditor())
                self.featuregrid.SetCellEditor(self.rowcount, 4, wx.grid.GridCellNumberEditor())  
                self.featuregrid.SetCellEditor(self.rowcount, 5, wx.grid.GridCellChoiceEditor(self.confcode))
                self.featuregrid.SetCellEditor(self.rowcount, 6, wx.grid.GridCellChoiceEditor(self.infillcode))
                self.featuregrid.SetCellEditor(self.rowcount, 7, wx.grid.GridCellFloatEditor(precision=1))
                self.featuregrid.SetCellEditor(self.rowcount, 8, wx.grid.GridCellChoiceEditor(self.plancode))
                self.featuregrid.SetCellEditor(self.rowcount, 9, wx.grid.GridCellChoiceEditor(self.roughcode))
                self.rowcount +=1
                self.featuregrid.MovePageDown()
                
            else:
                self.featuredepth = True

            
    
    def OnMatchDepths(self, event):
        """Check that the bottom depth of a run is greater than its top depth.  
        If not, generate an error that prevents saving the record.  
        Otherwise, insert the calculated value into the 'drilled' field."""
        top = self.runFromField.GetValue()
        bottom = self.runToField.GetValue()
        if top > bottom:
            badDepth = "Bottom run depth cannot be less than the top!"
            badtitle = "Run Depth Mismatch!"  
            dlg = wx.MessageDialog(self, badDepth, badtitle, wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            
        else:
            # Put value into "drilled field
            self.drilledField.SetValue(str(float(bottom)-float(top)))
            self.depthMatch = True
            
    def OnNoRecovery(self, event):
        """Disables Feature Grid and places zeros in given fields if no recovery was obtained during the run"""
        if self.recoveredField.GetValue() == "0" or self.recoveredField.GetValue() == "0.0":
            # Put zeros in the quantity fields
            self.rqdField.SetValue("0.0")
            self.fracturesField.SetValue("0")
            self.jcrField.SetValue("0")
            self.solidField.SetValue("0.0")
            self.rubbleField.SetValue("0.0")
            self.gougeField.SetValue("0.0")
            self.featuregrid.EnableEditing(False)
        else:
            self.featuregrid.EnableEditing(True)
            
        event.Skip()
            
            
            
    def OnDateChanged(self, event):
        self.log.write("OnDateChanged: %s\n" % event.GetDate())
        pass
    
    def FormatFloat(self, event):
        # Puts the given number in a decimal format
        try:
            txtvalue = event.GetEventObject().GetValue()
            floatvalue = str(1.0 * float(txtvalue))
            event.GetEventObject().SetValue(floatvalue)
        except TypeError:
            pass
        event.Skip()
        
        
    def FormatInt(self, event):
        # Puts the given number in a decimal format
        try:
            txtvalue = event.GetEventObject().GetValue()
            intvalue = str(int(txtvalue))
            event.GetEventObject().SetValue(intvalue)
        except TypeError:
            pass
        event.Skip()
        
