#!/usr/bin/env python

""" Converter.py

A simple application that provides unit conversions

"""
ID_ABOUT_MENU = 200           
ID_EXIT_MENU  = 201   


from wxPython.wx import *

class ConverterPanel(wxPanel):
    from wxPython.wx import *
    
    def __init__(self, parent, id, Unit, Data_dict):
    
        self.ID_FROM_BOX   = 100
        self.ID_TO_BOX     = 101
        self.ID_FROM_UNITS = 102
        self.ID_TO_UNITS   = 103
        
        self.Unit = Unit
        self.Data_dict = Data_dict
        self.Units = Data_dict.keys()
        ##self.Units.sort()
        
        wxPanel.__init__(self, parent, id, wxDefaultPosition)
        
        FromBox = wxBoxSizer(wxHORIZONTAL)
        
        FromBox.Add(20, 1, 1)
        self.InputBox = wxTextCtrl(self, self.ID_FROM_BOX, "", wxDefaultPosition,wxDefaultSize)
        FromBox.Add(self.InputBox, 0, wxALIGN_LEFT)
        FromBox.Add(20, 1, 1)
        self.FromUnits = wxChoice(self, self.ID_FROM_UNITS, wxDefaultPosition,wxDefaultSize,self.Units)
        FromBox.Add(self.FromUnits,0, wxALIGN_RIGHT)
        FromBox.Add(20, 1, 1)
        
        ToBox = wxBoxSizer(wxHORIZONTAL)
        
        ToBox.Add(20, 1, 1)
        self.OutBox = wxTextCtrl(self, self.ID_TO_BOX, "", wxDefaultPosition, wxDefaultSize,style = wxTE_READONLY)
        ToBox.Add(self.OutBox, 0, wxALIGN_LEFT)
        ToBox.Add(20, 1, 1)
        self.ToUnits = wxChoice(self, self.ID_TO_UNITS, wxDefaultPosition,wxDefaultSize,self.Units)
        ToBox.Add(self.ToUnits,0, wxALIGN_RIGHT)
        ToBox.Add(20, 1, 1)
        
        OuterBox = wxBoxSizer(wxVERTICAL)
        OuterBox.Add(0,0, 4)
        OuterBox.Add(wxStaticText(self, -1, "Convert from:", wxDefaultPosition, wxDefaultSize),0,wxALIGN_CENTER)
        OuterBox.Add(0,10, 0)
        OuterBox.Add(FromBox,0,wxALIGN_CENTER)
        OuterBox.Add(0,0, 2)
        OuterBox.Add(wxStaticText(self, -1, "To:", wxDefaultPosition, wxDefaultSize),0,wxALIGN_CENTER)
        OuterBox.Add(0,10, 0)
        OuterBox.Add(ToBox,0,wxALIGN_CENTER)
        OuterBox.Add(0,0, 8)
        
        self.SetAutoLayout(true)
        self.SetSizer(OuterBox)
        
        EVT_TEXT(self, self.ID_FROM_BOX, self.Recalculate)
        EVT_CHOICE(self,self.ID_TO_UNITS , self.Recalculate)
        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
            # iiit is set to a minimum of 4 figures
            sigfigs = max(len(from_string.split("e")[0].replace(".","")),3)
            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 wierd, 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 ConverterNB(wxNotebook):
    def __init__(self, parent, id):
        wxNotebook.__init__(self, parent, id,style=wxNB_LEFT )
        
        for (unit,data) in ConvertDataUnits.items():
            win = ConverterPanel(self, -1,unit,data)
            self.AddPage(win, unit)

            #		EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged)
            
            ##	def OnPageChanged(self, event):
            ##		print 'OnPageChanged'
            ##		event.Skip()
            
class ConverterFrame(wxFrame):
    def __init__(self,parent, id,title,position,size):
        wxFrame.__init__(self,parent, id,title,position, size)
        
        
        ## Set up the MenuBar
        MenuBar = wxMenuBar()
        
        file_menu = wxMenu()
        file_menu.Append(ID_EXIT_MENU, "E&xit","Terminate the program")
        EVT_MENU(self, ID_EXIT_MENU,self.OnQuit)
        MenuBar.Append(file_menu, "&File")
        
        
        help_menu = wxMenu()
        help_menu.Append(ID_ABOUT_MENU, "&About",
                         "More information About this program")
        EVT_MENU(self, ID_ABOUT_MENU, self.OnAbout)
        MenuBar.Append(help_menu, "&Help")
        
        self.SetMenuBar(MenuBar)
        
        
        EVT_CLOSE(self, self.OnCloseWindow)
        
        # Add the Notebook
        self.stuff = ConverterNB(self,-1)
        self.Show(true)
        
    def OnAbout(self, event):
        dlg = wxMessageDialog(self, "This is a small program that \n"
                              "Provides some handy conversions",
                              "About Me", wxOK | wxICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()
        
        
    def OnQuit(self,event):
        self.Close(true)
        
    def OnCloseWindow(self, event):
        self.Destroy()
        
        
class App(wxApp):
    def OnInit(self):
        frame = ConverterFrame(NULL,
                               -1,
                               "Unit Converter",
                               wxDefaultPosition,
                               (420,340))
        
        self.SetTopWindow(frame)
        return true
        
        
        ##This is the data stored and used by my conversion calculator
        
        ##Every Unit is stored in terms of SI units so that we can convert back
        ##and forth between anything through SI units
        
        
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 Kilometers" : 1e6,
          "Acres" : 4046.8564,
          "Square Miles" : 2589988.1,
          "Square Yards" : 0.83612736,
          "Hectares" : 10000.0
          },

# All volumes in terms of cubic meters
"Volume" : {"Cubic Meters" : 1.0,
            "Cubic Centimeters" : 1e-6,
            "Barrels"      : .1589873,
            "Liters"       : 1e-3,
            "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" : 1000
          },
# 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,
               "Galons 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
                   },
### Density in g/cc
"Density" : {"Specific Gravity" : 1.0,
             "Grams per Cubic Centimeter" : 1.0,
             "API" : 0}
}



if __name__ == "__main__":

    app = App(0)
    app.MainLoop()
