import wx
import wx.grid
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as WXCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg 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()
