import wx
import wx.grid
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as WXCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as WXToolbar
import matplotlib as mpl
import CoolProp as CP
from CoolProp.Plots.Plots import Ph, Ts
from CoolProp.Plots import PsychChart
import numpy as np

# Munge the system path if necessary to add the lib folder (only really needed
# for packaging using cx_Freeze)
#if os.path.exists('lib') and os.path.abspath(os.path.join(os.curdir,'lib')) not in os.:

class PlotPanel(wx.Panel):
    def __init__(self, parent, **kwargs):
        wx.Panel.__init__(self, parent, **kwargs)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.figure = mpl.figure.Figure(dpi=100)
        self.canvas = WXCanvas(self, -1, self.figure)
        self.ax = self.figure.add_axes((0.15,0.15,0.8,0.8))
        #self.toolbar = WXToolbar(self.canvas)
        #self.toolbar.Realize()
        sizer.Add(self.canvas,1,wx.EXPAND)
        #sizer.Add(self.toolbar)
        self.SetSizer(sizer)
        sizer.Layout()

class TSPlotFrame(wx.Frame):
    def __init__(self, Fluid):
        wx.Frame.__init__(self, None,title='T-s plot: '+Fluid)
        
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.PP = PlotPanel(self, size = (-1,-1))
        
        sizer.Add(self.PP, 1, wx.EXPAND)
        self.SetSizer(sizer)
        Ts(str(Fluid), 
           axis = self.PP.ax,
           Tmin = CP.CoolProp.Props(str(Fluid),'Ttriple')+0.01)
        sizer.Layout()
        
        self.add_menu()
    
    def add_menu(self):
        # Menu Bar
        self.MenuBar = wx.MenuBar()
        self.File = wx.Menu()
        
        mnuItem  = wx.MenuItem(self.File, -1, "Edit...", "", wx.ITEM_NORMAL)
        
        self.File.AppendItem(mnuItem)
        self.MenuBar.Append(self.File, "File")
        
        self.SetMenuBar(self.MenuBar)
   
class PsychOptions(wx.Dialog):
    def __init__(self,parent):
        wx.Dialog.__init__(self,parent)
    
        self.build_contents()
        self.layout()
        
    def build_contents(self):
        self.p_label = wx.StaticText(self,label='Pressure [kPa (absolute)]')
        self.p = wx.TextCtrl(self,value = '101.325')
        self.Tmin_label = wx.StaticText(self,label='Minimum dry bulb temperature [\xb0 C]')
        self.Tmin = wx.TextCtrl(self,value = '-10')
        self.Tmax_label = wx.StaticText(self,label='Maximum dry bulb temperature [\xb0 C]')
        self.Tmax = wx.TextCtrl(self,value = '60')
        self.GoButton = wx.Button(self,label='Accept')
        self.GoButton.Bind(wx.EVT_BUTTON,self.OnAccept)
        
    def OnAccept(self, event):
        self.EndModal(wx.ID_OK)
        
    def layout(self):
        sizer = wx.FlexGridSizer(cols = 2)
        sizer.AddMany([self.p_label,self.p,self.Tmin_label,self.Tmin,self.Tmax_label,self.Tmax])
        sizer.Add(self.GoButton)
        sizer.Layout()
        self.Fit()
            
class PsychPlotFrame(wx.Frame):
    def __init__(self,Tmin = 263.15,Tmax=333.15,p = 101.325, **kwargs):
            
        wx.Frame.__init__(self, None, title='Psychrometric plot', **kwargs)
        
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.PP = PlotPanel(self)
        
        self.PP.figure.delaxes(self.PP.ax)
        self.PP.ax = self.PP.figure.add_axes((0.1,0.1,0.85,0.85))
        
        sizer.Add(self.PP, 1, wx.EXPAND)
        self.SetSizer(sizer)
        
        PsychChart.p = p
        PsychChart.Tdb = np.linspace(Tmin,Tmax)
        
        SL = PsychChart.SaturationLine()
        SL.plot(self.PP.ax)
        
        RHL = PsychChart.HumidityLines([0.05,0.1,0.15,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9])
        RHL.plot(self.PP.ax)
        
        HL = PsychChart.EnthalpyLines(range(-20,100,10))
        HL.plot(self.PP.ax)
        
        PF = PsychChart.PlotFormatting()
        PF.plot(self.PP.ax)
        
        sizer.Layout()
        
        self.add_menu()
            
        self.PP.toolbar = WXToolbar(self.PP.canvas)
        self.PP.toolbar.Realize()
        self.PP.GetSizer().Add(self.PP.toolbar)
        
        self.PP.Layout()
    
    def add_menu(self):
        # Menu Bar
        self.MenuBar = wx.MenuBar()
        self.File = wx.Menu()
        
        mnuItem  = wx.MenuItem(self.File, -1, "Edit...", "", wx.ITEM_NORMAL)
        
        self.File.AppendItem(mnuItem)
        self.MenuBar.Append(self.File, "File")
        
        self.SetMenuBar(self.MenuBar)
        
class PHPlotFrame(wx.Frame):
    def __init__(self, Fluid):
        wx.Frame.__init__(self, None,title='p-h plot: '+Fluid)
        
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.PP = PlotPanel(self, size = (-1,-1))
        
        sizer.Add(self.PP, 1, wx.EXPAND)
        self.SetSizer(sizer)
        Ph(str(Fluid), 
           axis = self.PP.ax, 
           Tmin = CP.CoolProp.Props(str(Fluid),'Ttriple')+0.01)
        sizer.Layout()
        
        self.add_menu()
    
    def add_menu(self):
        # Menu Bar
        self.MenuBar = wx.MenuBar()
        self.File = wx.Menu()
        
        mnuItem  = wx.MenuItem(self.File, -1, "Edit...", "", wx.ITEM_NORMAL)
        
        self.File.AppendItem(mnuItem)
        self.MenuBar.Append(self.File, "File")
        
        self.SetMenuBar(self.MenuBar)
        
    def overlay_points(self):
        pass
    
    def overlay_cycle(self):
        pass

class SimpleGrid(wx.grid.Grid):
    def __init__(self, parent, ncol = 20, nrow = 8):
        wx.grid.Grid.__init__(self, parent)
        
        self.CreateGrid(ncol, nrow)
        [self.SetCellValue(i,j,'0.0') for i in range(20) for j in range(8)]
        
class SaturationTableDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self,parent)
        
        self.FluidLabel = wx.StaticText(self,label = "Fluid")
        self.FluidCombo = wx.ComboBox(self)
        self.FluidCombo.AppendItems(sorted(CP.__fluids__))
        self.FluidCombo.SetEditable(False)
        self.TtripleLabel = wx.StaticText(self,label = "Critical Temperature [K]")
        self.TtripleValue = wx.TextCtrl(self)
        self.TtripleValue.Enable(False)
        self.TcritLabel = wx.StaticText(self,label = "Critical Temperature [K]")
        self.TcritValue = wx.TextCtrl(self)
        self.TcritValue.Enable(False) 
        self.NvalsLabel = wx.StaticText(self,label = "Number of values")
        self.NvalsValue = wx.TextCtrl(self)
        self.TminLabel = wx.StaticText(self,label = "Minimum Temperature [K]")
        self.TminValue = wx.TextCtrl(self)
        self.TmaxLabel = wx.StaticText(self,label = "Maximum Temperature [K]")
        self.TmaxValue = wx.TextCtrl(self)
        
        self.Accept = wx.Button(self, label ="Accept")
        
        sizer = wx.FlexGridSizer(cols = 2)
        sizer.AddMany([self.FluidLabel,self.FluidCombo,
                       self.TtripleLabel,self.TtripleValue,
                       self.TcritLabel, self.TcritValue])
        sizer.AddSpacer(10)
        sizer.AddSpacer(10)
        sizer.AddMany([self.NvalsLabel,self.NvalsValue,
                       self.TminLabel, self.TminValue,
                       self.TmaxLabel, self.TmaxValue])
        sizer.Add(self.Accept)
        
        self.Bind(wx.EVT_COMBOBOX, self.OnSelectFluid)
        self.Bind(wx.EVT_BUTTON, self.OnAccept)
        
        self.SetSizer(sizer)
        sizer.Layout()
        self.Fit()
        
        #Bind a key-press event to all objects to get Esc 
        children = self.GetChildren()
        for child in children:
            child.Bind(wx.EVT_KEY_UP,  self.OnKeyPress)
            
    def OnKeyPress(self,event = None):
        """ cancel if Escape key is pressed """
        event.Skip()
        if event.GetKeyCode() == wx.WXK_ESCAPE:
            self.EndModal(wx.ID_CANCEL)
            
    def get_values(self):
        Fluid = str(self.FluidCombo.GetStringSelection())
        if Fluid:
            N = float(self.NvalsValue.GetValue())
            Tmin = float(self.TminValue.GetValue())
            Tmax = float(self.TmaxValue.GetValue())
            Tvals = np.linspace(Tmin, Tmax, N)
            return Fluid, Tvals
        else:
            return '',[]
    
    def OnCheckTmin(self):
        pass
    
    def OnCheckTmax(self):
        pass
        
    def OnAccept(self, event = None):
        self.EndModal(wx.ID_OK)
    
    def OnSelectFluid(self, event = None):
        Fluid = str(self.FluidCombo.GetStringSelection())
        if Fluid:
            Tcrit = CP.CoolProp.Props(Fluid,'Tcrit')
            Ttriple = CP.CoolProp.Props(Fluid,'Ttriple')
            self.TcritValue.SetValue(str(Tcrit))
            self.TtripleValue.SetValue(str(Ttriple))
            self.NvalsValue.SetValue('100')
            self.TminValue.SetValue(str(Ttriple + 0.01))
            self.TmaxValue.SetValue(str(Tcrit - 0.01))
        
class SaturationTable(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.Fluid, self.Tvals = self.OnSelect()
        if self.Fluid:
            self.tbl = SimpleGrid(self, 
                                  ncol = len(self.Tvals)
                                  )
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.tbl,1,wx.EXPAND)
            self.SetSizer(sizer)
            sizer.Layout()
            self.build()
            self.add_menu()
        else:
            self.Destroy()
        
        
    def OnSelect(self, event = None):
        dlg = SaturationTableDialog(None)
        if dlg.ShowModal() == wx.ID_OK:
            Fluid,Tvals = dlg.get_values()
            cancel = False
        else:
            cancel = True
        dlg.Destroy()
        if not cancel:
            return Fluid,Tvals
        else:
            return None,None
        
    def build(self):
        self.SetTitle('Saturation Table: '+self.Fluid)
        self.tbl.SetColLabelValue(0, "Temperature\n[K]")
        self.tbl.SetColLabelValue(1, "Liquid Pressure\n[kPa]")
        self.tbl.SetColLabelValue(2, "Vapor Pressure\n[kPa]")
        self.tbl.SetColLabelValue(3, "Liquid Density\n[kg/m3]")
        self.tbl.SetColLabelValue(4, "Vapor Density\n[kg/m3]")
        
        for i,T in enumerate(self.Tvals):
            Fluid = self.Fluid
            pL = CP.CoolProp.Props('P','T',T,'Q',0,Fluid)
            pV = CP.CoolProp.Props('P','T',T,'Q',1,Fluid)
            rhoL = CP.CoolProp.Props('D','T',T,'Q',0,Fluid)
            rhoV = CP.CoolProp.Props('D','T',T,'Q',1,Fluid)
            
            self.tbl.SetCellValue(i,0,str(T))
            self.tbl.SetCellValue(i,1,str(pL))
            self.tbl.SetCellValue(i,2,str(pV))
            self.tbl.SetCellValue(i,3,str(rhoL))
            self.tbl.SetCellValue(i,4,str(rhoV))
            
    def add_menu(self):
        # Menu Bar
        self.MenuBar = wx.MenuBar()
        self.File = wx.Menu()
        
        mnuItem0  = wx.MenuItem(self.File, -1, "Select All \tCtrl+A", "", wx.ITEM_NORMAL)
        mnuItem1  = wx.MenuItem(self.File, -1, "Copy selected data \tCtrl+C", "", wx.ITEM_NORMAL)
        mnuItem2  = wx.MenuItem(self.File, -1, "Copy table w/ headers \tCtrl+H", "", wx.ITEM_NORMAL)
        
        
        self.File.AppendItem(mnuItem0)
        self.File.AppendItem(mnuItem1)
        self.File.AppendItem(mnuItem2)
        self.MenuBar.Append(self.File, "Edit")
        self.Bind(wx.EVT_MENU, lambda event: self.tbl.SelectAll(), mnuItem0)
        self.Bind(wx.EVT_MENU, self.OnCopy, mnuItem1)
        self.Bind(wx.EVT_MENU, self.OnCopyHeaders, mnuItem2)
        
        self.SetMenuBar(self.MenuBar)
        
    def OnCopy(self, event = None):
        
        # Number of rows and cols
        rows = self.tbl.GetSelectionBlockBottomRight()[0][0] - self.tbl.GetSelectionBlockTopLeft()[0][0] + 1
        cols = self.tbl.GetSelectionBlockBottomRight()[0][1] - self.tbl.GetSelectionBlockTopLeft()[0][1] + 1
       
        # data variable contain text that must be set in the clipboard
        data = ''
       
        # For each cell in selected range append the cell value in the data variable
        # Tabs '\t' for cols and '\r' for rows
        for r in range(rows):
            for c in range(cols):
                data = data + str(self.tbl.GetCellValue(self.tbl.GetSelectionBlockTopLeft()[0][0] + r, self.tbl.GetSelectionBlockTopLeft()[0][1] + c))
                if c < cols - 1:
                    data = data + '\t'
            data = data + '\n'
        # Create text data object
        clipboard = wx.TextDataObject()
        # Set data object value
        clipboard.SetText(data)
        # Put the data in the clipboard
        if wx.TheClipboard.Open():
            wx.TheClipboard.SetData(clipboard)
            wx.TheClipboard.Close()
        else:
            wx.MessageBox("Can't open the clipboard", "Error")
        event.Skip()
        
    def OnCopyHeaders(self, event = None):        
        self.tbl.SelectAll()
        # Number of rows and cols
        rows = self.tbl.GetSelectionBlockBottomRight()[0][0] - self.tbl.GetSelectionBlockTopLeft()[0][0] + 1
        cols = self.tbl.GetSelectionBlockBottomRight()[0][1] - self.tbl.GetSelectionBlockTopLeft()[0][1] + 1
       
        # data variable contain text that must be set in the clipboard
        data = ''
       
        #Add the headers
        for c in range(cols):
            data += str(self.tbl.GetColLabelValue(c).replace('\n',' ') )
            if c < cols - 1:
                data += '\t'
        data = data + '\n'
        # For each cell in selected range append the cell value in the data variable
        # Tabs '\t' for cols and '\r' for rows
        for r in range(rows):
            for c in range(cols):
                data = data + str(self.tbl.GetCellValue(self.tbl.GetSelectionBlockTopLeft()[0][0] + r, self.tbl.GetSelectionBlockTopLeft()[0][1] + c))
                if c < cols - 1:
                    data = data + '\t'
            data = data + '\n'
        # Create text data object
        clipboard = wx.TextDataObject()
        # Set data object value
        clipboard.SetText(data)
        # Put the data in the clipboard
        if wx.TheClipboard.Open():
            wx.TheClipboard.SetData(clipboard)
            wx.TheClipboard.Close()
        else:
            wx.MessageBox("Can't open the clipboard", "Error")
        event.Skip()
        
class MainFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self,None)
        
        self.build()
    
    def build(self):
        # Menu Bar
        self.MenuBar = wx.MenuBar()
        
        self.plots = wx.Menu()
        self.PHPlot = wx.Menu()
        self.TSPlot = wx.Menu()
        self.tables = wx.Menu()
        self.PsychPlot = wx.MenuItem(self.plots,-1,'Psychrometric Plot')
        self.SatTable = wx.MenuItem(self.tables, -1,' Saturation Table', "", wx.ITEM_NORMAL)
        
        for Fluid in sorted(CP.__fluids__):
            mnuItem  = wx.MenuItem(self.PHPlot, -1, Fluid, "", wx.ITEM_NORMAL)
            self.PHPlot.AppendItem(mnuItem)
            self.Bind(wx.EVT_MENU, lambda event: self.OnPHPlot(event, mnuItem), mnuItem)
            
            mnuItem  = wx.MenuItem(self.TSPlot, -1, Fluid, "", wx.ITEM_NORMAL)
            self.TSPlot.AppendItem(mnuItem)
            self.Bind(wx.EVT_MENU, lambda event: self.OnTSPlot(event, mnuItem), mnuItem)
        
        self.MenuBar.Append(self.plots, "Plots")
        self.plots.AppendItem(self.PsychPlot)
        self.plots.AppendMenu(-1,'p-h plot', self.PHPlot)
        self.plots.AppendMenu(-1,'T-s plot', self.TSPlot)
        self.MenuBar.Append(self.tables, "Tables")
        self.tables.AppendItem(self.SatTable)
        self.Bind(wx.EVT_MENU, self.OnSatTable, self.SatTable)
        self.Bind(wx.EVT_MENU, self.OnPsychPlot, self.PsychPlot)
        
        self.SetMenuBar(self.MenuBar)
    
    def OnPsychPlot(self, event=None):
        
        #Load the options
        dlg = PsychOptions(None)
        if dlg.ShowModal() == wx.ID_OK:
            Tmin = float(dlg.Tmin.GetValue())+273.15
            Tmax = float(dlg.Tmax.GetValue())+273.15
            p = float(dlg.p.GetValue())
            PPF = PsychPlotFrame(Tmin = Tmin, Tmax = Tmax, p = p, size = (1000,700))
            PPF.Show()
        dlg.Destroy()
            
        
    def OnSatTable(self,event):
        TBL = SaturationTable(None)
        TBL.Show()
        
    def OnPHPlot(self, event, mnuItem):
        #Make a p-h plot instance in a new frame
        #Get the label (Fluid name)
        Fluid = self.PHPlot.FindItemById(event.Id).Label
        PH = PHPlotFrame(Fluid)
        PH.Show()
        
    def OnTSPlot(self, event, mnuItem):
        #Make a p-h plot instance in a new frame
        #Get the label (Fluid name)
        Fluid = self.TSPlot.FindItemById(event.Id).Label
        TS = TSPlotFrame(Fluid)
        TS.Show()
        
if __name__=='__main__':
    app = wx.App(False)
    wx.InitAllImageHandlers()
    
    frame = MainFrame() 
    frame.Show(True) 
    app.MainLoop()