#!/usr/bin/env python2.3

# just a comment to test cvs

import wx

ID_ABOUT_MENU = wx.NewId()          
ID_EXIT_MENU  = wx.NewId()

class ConverterPanel(wx.Panel):
    def __init__(self, parent, id, Unit, Data_dict):
    
        wx.Panel.__init__(self, parent, id, wx.DefaultPosition,style=wx.SUNKEN_BORDER)

        self.ID_FROM_BOX   = wx.NewId()
        self.ID_TO_BOX     = wx.NewId()
        self.ID_FROM_UNITS = wx.NewId()
        self.ID_TO_UNITS   = wx.NewId()
        
        self.Unit = Unit
        self.Data_dict = Data_dict
        self.Units = Data_dict.keys()
        ##self.Units.sort()
        
        self.FromUnits = wx.Choice(self, self.ID_FROM_UNITS, wx.DefaultPosition,wx.DefaultSize,self.Units)
        self.ToUnits = wx.Choice(self, self.ID_TO_UNITS, wx.DefaultPosition,wx.DefaultSize,self.Units)
        self.ToUnits.SetSelection(0)
        self.FromUnits.SetSelection(0)
        #self.FromUnits.SetSize(self.FromUnits.GetBestSize())
        #self.ToUnits.SetSize(self.ToUnits.GetBestSize())
        
        self.InputBox = wx.TextCtrl(self, self.ID_FROM_BOX, "", wx.DefaultPosition,wx.DefaultSize)
        self.OutBox = wx.TextCtrl(self, self.ID_TO_BOX, "", wx.DefaultPosition, wx.DefaultSize,style = wx.TE_READONLY)
        
        Grid = wx.FlexGridSizer(6, 2, 5, 20)

        Grid.Add(wx.StaticText(self, -1, "Convert from:", wx.DefaultPosition, wx.DefaultSize),0,wx.ALIGN_LEFT)
        Grid.Add((20,1),1) # adding a spacer

        Grid.Add(self.InputBox, 0, wx.ALIGN_LEFT)
        Grid.Add(self.FromUnits,0, wx.ALIGN_RIGHT)

        Grid.Add((20,20),1) # adding a spacer
        Grid.Add((20,20),1) # adding a spacer

        Grid.Add(wx.StaticText(self, -1, "Convert To:", wx.DefaultPosition, wx.DefaultSize),0,wx.ALIGN_LEFT)
        Grid.Add((20,1), 1) # adding a spacer

        Grid.Add(self.OutBox, 0, wx.ALIGN_LEFT)
        Grid.Add(self.ToUnits,0, wx.ALIGN_RIGHT)

        Grid.Layout()
        OuterBox = wx.BoxSizer(wx.VERTICAL)
        OuterBox.Add((20,20), 0)
        Label = wx.StaticText(self, -1, Unit, wx.DefaultPosition, wx.DefaultSize)
        of = Label.GetFont()
        Font = wx.Font(int(of.GetPointSize() * 2), of.GetFamily(), wx.NORMAL, wx.NORMAL)
        Label.SetFont(Font)

        OuterBox.Add(Label,0,wx.ALIGN_CENTER)
        OuterBox.Add(Grid,0,wx.ALIGN_CENTER|wx.ALL,30)
        OuterBox.Layout()

        self.SetAutoLayout(True)
        self.SetSizer(OuterBox)
        self.Fit()

        wx.EVT_TEXT(self, self.ID_FROM_BOX, self.Recalculate)
        wx.EVT_CHOICE(self,self.ID_TO_UNITS , self.Recalculate)
        wx.EVT_CHOICE(self,self.ID_FROM_UNITS , self.Recalculate)
        
    def Recalculate(self,event):
        try:
            from_string = self.InputBox.GetValue()
            # this is not quite really sigfigs, as all zeros are counted, but it looks better
            # it is set to a minimum of 4 figures
            sigfigs = max(len(from_string.split("e")[0].replace(".","")),4)
            sigfigs = min(sigfigs,7) # there are no more than 7 digits in the conversion factors
            from_val = float(self.InputBox.GetValue())
            from_unit = self.FromUnits.GetStringSelection()
            to_unit = self.ToUnits.GetStringSelection()
            
            if self.Unit == "Temperature": # Special Case
                A1 = self.Data_dict[from_unit][0]
                B1 = self.Data_dict[from_unit][1]
                A2 = self.Data_dict[to_unit][0]
                B2 = self.Data_dict[to_unit][1]
                
                #to_val = (round((from_val * A1 + B1),13) - round(B2,13))*A2 # rounding to get rid of cancelation error
                to_val = ((from_val + B1)*A1/A2)-B2
                sigfigs += 2 # temp adds and subtracts, so it looks better to have a couple of extra digits.
            else:
                if from_unit == "API": # another Special case (could I do this the same as temp?)
                    from_val = 141.5/(from_val + 131.5)
                    from_unit = "Specific Gravity"
                    sigfigs += 2 # API is also weird, so it looks better to have a couple of extra digits.
                if to_unit == "API":
                    to_val = 141.5/(from_val * self.Data_dict[from_unit]) - 131.5
                    sigfigs += 2 # API is also wierd, so it looks better to have a couple of extra digits.
                else:
                    to_val = from_val * self.Data_dict[from_unit] / self.Data_dict[to_unit]
            format_string = "%%.%ig"%sigfigs
            self.OutBox.SetValue(format_string%(to_val,))# this displays only 6 sigfigs...
        except ValueError:
            self.OutBox.SetValue("")


class ConverterFrame(wx.Frame):
    def __init__(self,parent, id,title,position,size):
        wx.Frame.__init__(self,parent, id,title,position, size)

        ## Set up the MenuBar
        MenuBar = wx.MenuBar()
        
        file_menu = wx.Menu()
        file_menu.Append(ID_EXIT_MENU, "E&xit","Terminate the program")
        wx.EVT_MENU(self, ID_EXIT_MENU,self.OnQuit)
        MenuBar.Append(file_menu, "&File")
        
        help_menu = wx.Menu()
        help_menu.Append(ID_ABOUT_MENU, "&About",
                         "More information About this program")
        wx.EVT_MENU(self, ID_ABOUT_MENU, self.OnAbout)
        MenuBar.Append(help_menu, "&Help")
        
        self.SetMenuBar(MenuBar)
        
        
        wx.EVT_CLOSE(self, self.OnCloseWindow)
        
        MainSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.MainSizer = MainSizer

        ButtonSizer = wx.BoxSizer(wx.VERTICAL)

        self.Panels = {}
        BiggestSize = (0,0)
        for (unit,data) in ConvertDataUnits.items():
            # create Panel
            Panel = ConverterPanel(self, -1,unit,data)
            Panel.Show(False)
            size = Panel.GetBestSize()
            BiggestSize = ( max(size[0],BiggestSize[0]), max(size[1],BiggestSize[1]) )
            
            # create Button
            ID = wx.NewId()
            ButtonSizer.Add(wx.Button(self,ID,unit),0,wx.EXPAND|wx.ALL,3)
            self.Panels[ID] = Panel

            ### set up the events:
            wx.EVT_BUTTON(self, ID, self.OnButtonPress)

        ## Reset the sizes of all the Panels to match the biggest one
        for Panel in self.Panels.values():
            Panel.SetSize(BiggestSize)
        ButtonSizer.Layout()
        MainSizer.Add(ButtonSizer,0,wx.ALIGN_TOP|wx.ALL,6)

        ##Add all the Panels
        for ID in self.Panels:
            CurrentPanel =  self.Panels[ID]
            ##self.CurrentPanel.Show(True)
            MainSizer.Add(CurrentPanel,1,wx.GROW|wx.ALL,4)
            MainSizer.Hide(CurrentPanel)
        self.CurrentPanel = self.Panels.values()[0]
        MainSizer.Show(self.CurrentPanel)
        MainSizer.Layout()        
        self.SetSizer(MainSizer)
        self.Fit()
        self.Show(True)

            
    def OnButtonPress(self,event):
        ID = event.GetId()
        self.MainSizer.Hide(self.CurrentPanel)
        self.CurrentPanel = self.Panels[ID]
        self.MainSizer.Show(self.CurrentPanel)
        self.MainSizer.Layout()

    def OnAbout(self, event):
        dlg = wx.MessageDialog(self, "This is a small program that \n"
                              "Provides some handy conversions",
                              "About Me", wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()
        
        
    def OnQuit(self,event):
        self.Close(True)
        
    def OnCloseWindow(self, event):
        self.Destroy()


class App(wx.App):
    def OnInit(self):
        frame = ConverterFrame(None,
                               -1,
                               "Unit Converter",
                               wx.DefaultPosition,
                               wx.DefaultSize)
        
        self.SetTopWindow(frame)
        return True


ConvertDataUnits = {
# All lengths in terms of meters
"Length" : {"Meters"      : 1.0,
            "Centimeters" : 0.01,
            "Millimeters"  : 0.001,
            "Kilometers"  : 1000.0,
            "Feet"        : 0.3048,
            "Inches"      : 0.0254,
            "Yards"       : 0.9144,
            "Miles"       : 1609.344,
            "Nautical Miles": 1852.0,
            "Fathoms" : 1.8288,
            "Latitude Degrees": 111120.0,
            "Latitude Minutes": 1852.0
            },

# All Areas in terms of square meters
"Area" : {"Square Meters" : 1.0,
          "Square Centimeters": .0001,
          "Square Kilometers" : 1e6,
          "Acres" : 4046.8564,
          "Square Miles" : 2589988.1,
          "Square Yards" : 0.83612736,
          "Square Feet"  : 0.09290304,
          "Hectares" : 10000.0
          },

# All volumes in terms of cubic meters
"Volume" : {"Cubic Meters" : 1.0,
            "Cubic Centimeters" : 1e-6,
            "Barrels (Petroleum)"      : .1589873,
            "Liters"       : 1e-3,
            "US Gallons"      : 0.0037854118,
            "Cubic Feet"   : 0.028316847
            },

# All Temperature units in K (multiply by, add)
"Temperature" : {"Kelvin" : (1.0, 0.0),
                 "Centigrade"    : (1.0, 273.16),
                 "Farenheight" : (0.55555555555555558, (273.16*9/5 - 32) )
                 },

# All Mass units in Kg (weight is taken to be mass at standard g)
"Mass" : {"Kilograms" : 1.0,
          "Pounds"    : 0.45359237,
          "Grams" : .001
          },
# All Time In Seconds
"Time" : {"Seconds" : 1.0,
                "Minutes" : 60.0,
                "Hours"   : 3600.0,
                "Days"    : 86400.0
                },
# All Velocities in meters per second
"Velocity" : {"Meters per Second" : 1.0,
              "Centimeters per Second" : .01,
              "Kilometers per Hour" : 0.277777,
              "Knots" : 0.514444,
              "Miles per Hour" : 0.44704,
              "Feet per Second" : 0.3048
              },
# All Discharges in cubic meters per second
"Discharge" : {"Cubic Meters per Second" : 1.0,
               "Cubic Feet per Second"   : .02831685,
               "Gallons per Hour" : 1.0515032833333335e-06,
               "Gallons per Minute": 6.3090197000000006e-05,
               "Gallons per Second" :  0.0037854118 
               },

# All Oil Thicknesses in meters
"Oil Thickness" : {"Meters" : 1.0,
                   "Millimeters"     : 0.001,
                   "Barrels per Acre" : 3.92866176324e-05,
                   "Barrels per Square Meter" : .1589873,
                   "Liters per Square Kilometer" : 1e-9
                   },
"Kinematic Viscosity" : {"Stokes": 1.0,
                         "Centistokes": .01,
                         "Square centimeters per second": 1.0,
                         "Square meters per second": 10000,
                         #"Poise" : (["P"])
                         },

### Density in g/cc
"Density" : {"Specific Gravity" : 1.0,
             "Grams per Cubic Centimeter" : 1.0,
             "Kilograms per cubic Meter" : .001,
             "API" : 0}
}
        
if __name__ == "__main__":
    app = App(0)
    app.MainLoop()
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
