#!/usr/bin/python

""" GUI for Gas Tables
"""
"""
 * Copyright (C) 2008 Varun Hiremath
 * Copyright (C) 2008 A Venkattraman
 * Copyright (C) 2008 C Shyam Sundar
 *
 * This program 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 2
 * of the License, or (at your option) any later version.
 * This program 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.

 * Authors: Varun Hiremath, Venkattraman A, Shyam Sundar C

"""

import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg 
from matplotlib.backends.backend_wxagg import Toolbar
from matplotlib.figure import Figure
from matplotlib import rc
rc('text', usetex=True)

import wx
import numpy

import gastablesgui.Isentropic as Isentropic
import gastablesgui.NormalShock as NormalShock
import gastablesgui.ObliqueShock as ObliqueShock
import gastablesgui.PrandtlMeyer as PrandtlMeyer
import gastablesgui.FannoFlow as FannoFlow
import gastablesgui.Isothermal as Isothermal
import gastablesgui.RayleighFlow as RayleighFlow

TITLE       = "Gas Tables"
ABOUT_TITLE = "About Gas Tables"
ABOUT_BODY  = "A Graphical Interface for the python gas tables module. \n\nAuthors : \
Varun Hiremath, Venkattraman A, Shyam Sundar C" 
              

# Generate IDs for different Menu actions.
ID_About        = wx.NewId()
ID_Exit         = wx.NewId()
ID_Isentropic   = wx.NewId()
ID_NormalShock  = wx.NewId()
ID_ObliqueShock = wx.NewId()
ID_FannoFlow    = wx.NewId()
ID_RayleighFlow = wx.NewId()
ID_Isothermal   = wx.NewId()
ID_PrandtlMeyer = wx.NewId()

# colours used in status bar
RED = wx.Color(200,100,0)

class GasTables(wx.Frame):
    def __init__(self, parent, ID, title):
        self.parent = parent
        self.ID = ID
        self.title = title
        self.ModuleName = None
        self.Plotted = False
        self.gamma = 1.4
        self.initialise()

    def initialise(self):
        wx.Frame.__init__(self, self.parent, self.ID, self.title,
                         wx.DefaultPosition,
			 wx.Size(800, 300))
        self.CreateStatusBar()
        self.SetStatusMessage("Please select a Module from the Modules menu.")

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        
        ModulesMenu = wx.Menu()

        ModulesMenu.Append(ID_Isentropic, "&Isentropic", "Isentropic relations")
        ModulesMenu.Append(ID_NormalShock, "&NormalShock", "Normal Shock relations")
        ModulesMenu.Append(ID_ObliqueShock, "&ObliqueShock", "Oblique Shock relations")
        ModulesMenu.Append(ID_Isothermal, "&Isothermal", "Isothermal relations")
        ModulesMenu.Append(ID_FannoFlow, "&FannoFlow", "Fanno Flow relations")
        ModulesMenu.Append(ID_RayleighFlow, "&RayleighFlow", "Rayleigh Flow relations")
        ModulesMenu.Append(ID_PrandtlMeyer, "&PrandtlMeyer", "Prandtl Meyer relations")
        ModulesMenu.AppendSeparator()
        ModulesMenu.Append(ID_Exit, "E&xit", "Terminate the program")

        HelpMenu = wx.Menu()
        HelpMenu.Append(ID_About, "&About", "More information about this program")

        menuBar = wx.MenuBar()
        menuBar.Append(ModulesMenu, "&Modules")
        menuBar.Append(HelpMenu, "&Help")

        self.SetMenuBar(menuBar)

        wx.EVT_MENU(self, ID_About,        self.About       )
        wx.EVT_MENU(self, ID_Exit,         self.Quit        )
        wx.EVT_MENU(self, ID_Isentropic,   self.Isentropic  )
        wx.EVT_MENU(self, ID_NormalShock , self.NormalShock )
        wx.EVT_MENU(self, ID_ObliqueShock, self.ObliqueShock)
        wx.EVT_MENU(self, ID_FannoFlow   , self.FannoFlow   )
        wx.EVT_MENU(self, ID_RayleighFlow, self.RayleighFlow)
        wx.EVT_MENU(self, ID_Isothermal  , self.Isothermal  )
        wx.EVT_MENU(self, ID_PrandtlMeyer, self.PrandtlMeyer)

    def initFigure(self):
        self.fig = Figure((10,4), dpi=80)
        self.canvas = FigureCanvas(self, 0, self.fig)
        self.toolbar = Toolbar(self.canvas)
        self.toolbar.Realize()
        self.sizer.Add(self.canvas, 0, wx.BOTTOM, 1)
        self.sizer.Add(self.toolbar, 0, wx.CENTER|wx.BOTTOM, 1)

    def Isentropic(self, event, VALUES = None, SECOND_VALUES=None):
        self.ModuleName = "Isentropic"
        self.FIELDS = ["M", "M*", "T/To", "P/Po", "A/A*", "r/ro", "F/F*", "AP/A*Po" ]

        self.createDialog(self.ModuleName, self.FIELDS, self.Isentropic, VALUES, SECOND_VALUES)
        
    def NormalShock(self, event, VALUES = None, SECOND_VALUES=None):
        self.ModuleName = "Normal Shock"
        self.FIELDS = ["Mx", "My", "Py/Px", "Poy/Px", "Poy/Pox", "ry/rx", "Ty/Tx"]

        self.createDialog(self.ModuleName, self.FIELDS, self.NormalShock, VALUES, SECOND_VALUES)

    def ObliqueShock(self, event,VALUES = None, SECOND_VALUES = None):
        self.ModuleName = "Oblique Shock"
        self.FIELDS = ["Mx","Turn Angle","Wave Angle","My", "Py/Px", "Poy/Px", "Poy/Pox","ry/rx", "Ty/Tx"]

        self.createDialog(self.ModuleName, self.FIELDS, self.ObliqueShock, VALUES, SECOND_VALUES, 3, False)
 
    def Isothermal(self, event, VALUES = None, SECOND_VALUES=None):
        self.ModuleName = "Isothermal"
        self.FIELDS = ["M","P/P*(ref)","r*(ref)/r","To/To*(ref)","Po/Po*(ref)","4fLmax/D"]

        self.createDialog(self.ModuleName, self.FIELDS, self.Isothermal, VALUES, SECOND_VALUES)

    def FannoFlow(self, event, VALUES=None, SECOND_VALUES=None):
	self.ModuleName = "Fanno Flow"
        self.FIELDS = ["M", "P/Pstar", "T/Tstar", "rho/rhostar", "Po/Postar", "F/Fstar", "4fLmax/D" ]

        self.createDialog(self.ModuleName, self.FIELDS, self.FannoFlow, VALUES, SECOND_VALUES)

    def RayleighFlow(self, event, VALUES = None, SECOND_VALUES=None):
        self.ModuleName = "Rayleigh Flow"
        self.FIELDS = ["M","P/P*","Po/Po*","T/T*","To/To*","r/r*","Qmax/CpT"]

        self.createDialog(self.ModuleName, self.FIELDS, self.RayleighFlow, VALUES, SECOND_VALUES)

    def PrandtlMeyer(self, event, VALUES=None, SECOND_VALUES=None):
        self.ModuleName = "Prandtl Meyer"
        self.FIELDS = ["M","Prandtl function","Mach angle"]

        self.createDialog(self.ModuleName, self.FIELDS, self.PrandtlMeyer, VALUES, SECOND_VALUES, None, False)
        
    def createDialog(self, ModuleName, FIELDS, Function, VALUES=None, SECOND_VALUES=None, N=None, Plot=True):
        self.ModuleName = ModuleName
        self.CreateStatusBar()
        self.SetStatusMessage(self.ModuleName + " Running")

        self.sizer.Clear(True)
        panel = wx.Panel(self,1)

        self.FIELDS = FIELDS
        self.LABELS = {}
        self.ENTRIES = {}
        self.VALUES = {}

        if VALUES == None:
            for field in self.FIELDS:
                self.VALUES[field] = ""
        else:
            for field in self.FIELDS:
                self.VALUES[field] = str(round(VALUES[field],4))

        if N == None:
            N = len(self.FIELDS)

        empty = wx.StaticText(panel, -1, "    ")
        gamma_text = wx.StaticText(panel, 1, "Gamma")
        self.gamma_value = wx.TextCtrl(panel, -1, str(self.gamma))

        fieldSizer = wx.FlexGridSizer(2, len(self.FIELDS)+1, 10, 10)

        for field in self.FIELDS[:N]:
            self.LABELS[field] = wx.StaticText(panel, -1, field)
            self.ENTRIES[field] = wx.TextCtrl(panel, -1, self.VALUES[field])
            self.ENTRIES[field].SetToolTipString("Enter the value of %s" % field)

        for field in self.FIELDS[N:]:
            self.LABELS[field] = wx.StaticText(panel, -1, field)
            self.ENTRIES[field] = wx.TextCtrl(panel, -1, self.VALUES[field], style = wx.TE_READONLY)
            if self.VALUES[field] == "":
                self.ENTRIES[field].Enable(False)
            self.ENTRIES[field].SetToolTipString("Enter the value of %s" % field)

        fieldSizer.Add(gamma_text, 0, wx.LEFT, 2)
        for field in self.FIELDS:
            fieldSizer.Add(self.LABELS[field], 0, wx.LEFT, 2)

        fieldSizer.Add(self.gamma_value)
        for field in self.FIELDS:
            fieldSizer.Add(self.ENTRIES[field])

	if SECOND_VALUES != None:
            fieldSizer.Add(empty)
            for field in self.FIELDS:
                fieldSizer.Add(wx.TextCtrl(panel, -1, str(round(SECOND_VALUES[field],4))))

        calculateButton = wx.Button(panel, -1, "Calculate")
        resetButton = wx.Button(panel, -1, "Reset")
        if Plot:
            plotButton = wx.Button(panel, -1, "Plot")
	
            self.plotxOptions =  wx.Choice(panel)
            self.plotyOptions =  wx.Choice(panel)
            self.plotxOptions.AppendItems(strings=self.FIELDS)
            self.plotyOptions.AppendItems(strings=self.FIELDS)
            self.plotxOptions.Select(n=0)
            self.plotyOptions.Select(n=1)
	
        self.Bind(wx.EVT_BUTTON, self.readData, calculateButton)
        self.Bind(wx.EVT_BUTTON, Function, resetButton)
        if Plot:
            self.Bind(wx.EVT_BUTTON, self.plotData, plotButton)
	
	buttonSizer = wx.FlexGridSizer(1,5,10,5)

        if Plot:
            buttonSizer.Add(plotButton)
            buttonSizer.Add(self.plotxOptions)
            buttonSizer.Add(self.plotyOptions)
        buttonSizer.Add(calculateButton)
        buttonSizer.Add(resetButton)

        border = wx.BoxSizer(wx.VERTICAL)
        border.Add(fieldSizer ,0, wx.ALL, 20)
        border.Add(buttonSizer,0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, 20)

	panel.SetSizer(border)

        self.sizer.Add(panel)
        self.SetSizer(self.sizer)
        self.Fit()

    def readData(self, event):
        VALUES = {}
        NEW_VALUES = None
        SECOND_VALUES = None

        # Read Values
        for field in self.FIELDS:
            VALUES[field] = self.ENTRIES[field].GetValue()

        # Gamma
        if self.gamma_value.GetValue() != "":
            self.gamma = float(self.gamma_value.GetValue())

        # Do some checks and call the appropriate function
        if self.ModuleName == "Isentropic":
            try:
                if VALUES["M"] != "":
                    NEW_VALUES = Isentropic.get_allValues_from_M(float(VALUES["M"]), self.gamma)
                elif VALUES["T/To"] != "":   
                    NEW_VALUES = Isentropic.get_allValues_from_T_by_To(float(VALUES["T/To"]), self.gamma)
                elif VALUES["P/Po"] != "":   
                    NEW_VALUES = Isentropic.get_allValues_from_P_by_Po(float(VALUES["P/Po"]), self.gamma)
                elif VALUES["r/ro"] != "":   
                    NEW_VALUES = Isentropic.get_allValues_from_rho_by_rhoo(float(VALUES["r/ro"]), self.gamma)
                elif VALUES["A/A*"] != "":   
                    (NEW_VALUES, SECOND_VALUES) = Isentropic.get_allValues_from_A_by_Astar(float(VALUES["A/A*"]), self.gamma)
                elif VALUES["F/F*"] != "":
                    NEW_VALUES = Isentropic.get_allValues_from_F_by_Fstar(float(VALUES["A/A*"]),self.gamma)
                elif VALUES["AP/A*Po"] != "":
                    NEW_VALUES = Isentropic.get_allValues_from_A_by_Astar(float(VALUES["AP/A*Po"]),self.gamma)
                if NEW_VALUES == None:
                    raise Exception("Please enter some value!")
                else:
                    self.Isentropic(event, NEW_VALUES, SECOND_VALUES)
            except Exception, e:
                self.SetStatusMessage(str(e[0]), RED)

        if self.ModuleName == "Normal Shock" :
            try:
                if VALUES["Mx"] != "":
                    NEW_VALUES = NormalShock.get_allValues_from_Mx(float(VALUES["Mx"]), self.gamma)
                elif VALUES["My"] != "":   
                    NEW_VALUES = NormalShock.get_allValues_from_My(float(VALUES["My"]), self.gamma)
                elif VALUES["Py/Px"] != "":   
                    NEW_VALUES = NormalShock.get_allValues_from_Py_by_Px(float(VALUES["Py/Px"]), self.gamma)
                elif VALUES["Poy/Px"] != "":   
                    NEW_VALUES = NormalShock.get_allValues_from_Poy_by_Px(float(VALUES["Poy/Px"]), self.gamma)
                elif VALUES["Poy/Pox"] != "":   
                    NEW_VALUES = NormalShock.get_allValues_from_Poy_by_Pox(float(VALUES["Poy/Pox"]), self.gamma)
                elif VALUES["Ty/Tx"] != "":
                    NEW_VALUES = NormalShock.get_allValues_from_Ty_by_Tx(float(VALUES["Ty/Tx"]),self.gamma)
                elif VALUES["ry/rx"] != "":
                    NEW_VALUES = NormalShock.get_allValues_from_rhoy_by_rhox(float(VALUES["ry/rx"]),self.gamma)
                if NEW_VALUES == None:
                    raise Exception("Please enter some value!")
                else:
                    self.NormalShock(event, NEW_VALUES)
            except Exception, e:
                self.SetStatusMessage(str(e[0]), RED)
                
        if self.ModuleName == "Oblique Shock" :
            try:
                if VALUES["Mx"] != "":
                    if VALUES["Turn Angle"] == "" and VALUES["Wave Angle"] == "":
                        raise Exception("Please enter the value of Mx and one of Wave or Turn Angles.")
                    if VALUES["Wave Angle"] != "" :
                        NEW_VALUES, SECOND_VALUES = ObliqueShock.get_allValues_from_Mx_and_Wave_Angle(float(VALUES["Mx"]),float(VALUES["Wave Angle"]),self.gamma)
                    if VALUES["Turn Angle"] != "" :
                        NEW_VALUES, SECOND_VALUES = ObliqueShock.get_allValues_from_Mx_and_Turn_Angle(float(VALUES["Mx"]),float(VALUES["Turn Angle"]),self.gamma)
                else:
                    raise Exception("Please enter the value of Mx!")
                self.ObliqueShock(event,NEW_VALUES, SECOND_VALUES)
            except Exception, e:
                self.SetStatusMessage(str(e[0]), RED)
        
        if self.ModuleName == "Prandtl Meyer" :
            try:
                if VALUES["M"] != "" :
                    NEW_VALUES = PrandtlMeyer.get_allValues_from_M(float(VALUES["M"]),self.gamma)
                if VALUES["Prandtl function"] != "" :
                    NEW_VALUES = PrandtlMeyer.get_allValues_from_Prandtl_Func(float(VALUES["Prandtl function"]),self.gamma)
                if VALUES["Mach angle"] != "" :
                    NEW_VALUES = PrandtlMeyer.get_allValues_from_Mach_Angle(float(VALUES["Mach angle"]),self.gamma)

                if NEW_VALUES == None:
                    raise Exception("Please enter some value!")
                else:
                    self.PrandtlMeyer(event,NEW_VALUES, SECOND_VALUES)

            except Exception, e:
                self.SetStatusMessage(str(e[0]), RED)
	    
	if self.ModuleName == "Fanno Flow":
            try:
                if VALUES["M"] != "":
                    NEW_VALUES = FannoFlow.get_allValues_from_M(float(VALUES["M"]), self.gamma)
		elif VALUES["P/Pstar"] != "":   
                    NEW_VALUES = FannoFlow.get_allValues_from_P_by_Pstar(float(VALUES["P/Pstar"]), self.gamma)
                elif VALUES["T/Tstar"] != "":   
                    NEW_VALUES = FannoFlow.get_allValues_from_T_by_Tstar(float(VALUES["T/Tstar"]), self.gamma)
                elif VALUES["rho/rhostar"] != "":   
                    NEW_VALUES = FannoFlow.get_allValues_from_rho_by_rhostar(float(VALUES["rho/rhostar"]), self.gamma)
		elif VALUES["Po/Postar"] != "":   
                    NEW_VALUES , SECOND_VALUES = FannoFlow.get_allValues_from_Po_by_Postar(float(VALUES["Po/Postar"]), self.gamma)
		elif VALUES["F/Fstar"] != "":   
		    NEW_VALUES,SECOND_VALUES = FannoFlow.get_allValues_from_F_by_Fstar(float(VALUES["F/Fstar"]), self.gamma)
		elif VALUES["4fLmax/D"] != "":   
                    NEW_VALUES ,SECOND_VALUES= FannoFlow.get_allValues_from_4fLmax_by_D(float(VALUES["4fLmax/D"]), self.gamma)

                if NEW_VALUES == None:
                    raise Exception("Please enter some value!")
                else:
                    self.FannoFlow(event, NEW_VALUES,SECOND_VALUES)
		
            except Exception, e:
                self.SetStatusMessage(str(e[0]), RED)
                
        if self.ModuleName == "Isothermal" :
            try :
                if VALUES["M"] != "" :
                    NEW_VALUES = Isothermal.get_allValues_from_M(float(VALUES["M"]),self.gamma)
                elif VALUES["P/P*(ref)"] != "" :
                    NEW_VALUES = Isothermal.get_allValues_from_P_by_Pstarref(float(VALUES["P/P*(ref)"]),self.gamma)
                elif VALUES["r*(ref)/r"] != "" :
                    NEW_VALUES = Isothermal.get_allValues_from_rhostarref_by_rho(float(VALUES["r*(ref)/r"]),self.gamma)
                elif VALUES["To/To*(ref)"] != "" :
                    NEW_VALUES = Isothermal.get_allValues_from_To_by_Tostarref(float(VALUES["To/To*(ref)"]),self.gamma)
                elif VALUES["Po/Po*(ref)"] != "" :
                    NEW_VALUES, SECOND_VALUES = Isothermal.get_allValues_from_Po_by_Postarref(float(VALUES["Po/Po*(ref)"]),self.gamma)
                elif VALUES["4fLmax/D"] != "" :
                    NEW_VALUES ,SECOND_VALUES= Isothermal.get_allValues_from_4fLmax_by_D(float(VALUES["4fLmax/D"]),self.gamma)

                if NEW_VALUES == None:
                    raise Exception("Please enter some value!")
                else:
                    self.Isothermal(event,NEW_VALUES,SECOND_VALUES)
            except Exception, e:
                self.SetStatusMessage(str(e[0]), RED)
                
        if self.ModuleName == "Rayleigh Flow" :
            try :
                if VALUES["M"] != "" :
                    NEW_VALUES = RayleighFlow.get_allValues_from_M(float(VALUES["M"]),self.gamma)
                elif VALUES["P/P*"] != "" :
                    NEW_VALUES = RayleighFlow.get_allValues_from_P_by_Pstar(float(VALUES["P/P*"]),self.gamma)
                elif VALUES["Po/Po*"] != "":
                    NEW_VALUES, SECOND_VALUES = RayleighFlow.get_allValues_from_Po_by_Postar(float(VALUES["Po/Po*"]),self.gamma)
                elif VALUES["T/T*"] :
                    NEW_VALUES, SECOND_VALUES = RayleighFlow.get_allValues_from_T_by_Tstar(float(VALUES["T/T*"]),self.gamma)
                elif VALUES["To/To*"] != "" :
                    NEW_VALUES, SECOND_VALUES = RayleighFlow.get_allValues_from_To_by_Tostar(float(VALUES["To/To*"]),self.gamma)
                elif VALUES["r/r*"] != "" :
                    NEW_VALUES = RayleighFlow.get_allValues_from_rho_by_rhostar(float(VALUES["r/r*"]),self.gamma)
                elif VALUES["Qmax/CpT"] != "" :
                     NEW_VALUES = RayleighFlow.get_allValues_from_Qmax_by_CpT(float(VALUES["Qmax/CpT"]),self.gamma)
                if NEW_VALUES == None:
                    raise Exception("Please enter some value!")
                else:
                    self.RayleighFlow(event, NEW_VALUES, SECOND_VALUES)
            except Exception, e:
                self.SetStatusMessage(str(e[0]), RED)


    def plotData(self, event):
	if self.ModuleName == "Isentropic":
            values = Isentropic.get_plotData_from_M(self.gamma)
	elif self.ModuleName ==	"Normal Shock":
            values = NormalShock.get_plotData_from_Mx(self.gamma)
	elif self.ModuleName ==	"Oblique Shock":
            values = ObliqueShock.get_plotData_from_M(20.0, self.gamma)
	elif self.ModuleName ==	"Fanno Flow":
            values = FannoFlow.get_plotData_from_M(self.gamma)
        elif self.ModuleName == "Rayleigh Flow" :
            values = RayleighFlow.get_plotData_from_M(self.gamma)
	elif self.ModuleName == "Isothermal":
            values = Isothermal.get_plotData_from_M(self.gamma)

        if(self.Plotted):
            try:
                self.sizer.Remove(self.canvas)
                self.sizer.Remove(self.toolbar)
                self.Fit()
            except:
                pass
        
        self.initFigure()
        plotArea = self.fig.add_subplot(111)
        plotArea.set_xlabel(self.FIELDS[self.plotxOptions.GetSelection()])
        plotArea.set_ylabel(self.FIELDS[self.plotyOptions.GetSelection()])
        plotArea.plot(values[self.FIELDS[self.plotxOptions.GetSelection()]],values[self.FIELDS[self.plotyOptions.GetSelection()]])

        self.toolbar.update()
        self.Fit()
        self.Plotted = True

    def About(self, event):
        self.Alert(ABOUT_TITLE,ABOUT_BODY)

    def Quit(self, event):
        self.Close(true)

    def Alert(self,title,msg):
        dlg = wx.MessageDialog(self, msg, title, wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()
        
    def SetStatusMessage(self, message, colour=wx.NullColour):
        if colour!= wx.NullColour:
            print message
        myBar = self.GetStatusBar()
        myBar.SetBackgroundColour(colour)
        myBar.SetStatusText(message)
        self.SetStatusBar(myBar)

class GasTablesGUI(wx.App):
    def OnInit(self):
        frame = GasTables(None, -1, TITLE)
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

app = GasTablesGUI(0)
app.MainLoop()
