#!/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
 * Version 0.1
"""

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 matplotlib.numerix as numpy

from wxPython.wx import *
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        = wxNewId()
ID_Exit         = wxNewId()
ID_Isentropic   = wxNewId()
ID_NormalShock  = wxNewId()
ID_ObliqueShock = wxNewId()
ID_FannoFlow    = wxNewId()
ID_RayleighFlow = wxNewId()
ID_Isothermal   = wxNewId()
ID_PrandtlMeyer = wxNewId()

# colours used in status bar
RED = wxColor(200,100,0)

class GasTables(wxFrame):
    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):
        wxFrame.__init__(self, self.parent, self.ID, self.title,
                         wxDefaultPosition,
			 wxSize(800, 300))
        self.CreateStatusBar()
        self.SetStatusMessage("Please select a Module from the Modules menu.")

        self.sizer = wxBoxSizer(wxVERTICAL)
        
        ModulesMenu = wxMenu()

        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 = wxMenu()
        HelpMenu.Append(ID_About, "&About", "More information about this program")

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

        self.SetMenuBar(menuBar)

        EVT_MENU(self, ID_About,        self.About       )
        EVT_MENU(self, ID_Exit,         self.Quit        )
        EVT_MENU(self, ID_Isentropic,   self.Isentropic  )
        EVT_MENU(self, ID_NormalShock , self.NormalShock )
        EVT_MENU(self, ID_ObliqueShock, self.ObliqueShock)
        EVT_MENU(self, ID_FannoFlow   , self.FannoFlow   )
        EVT_MENU(self, ID_RayleighFlow, self.RayleighFlow)
        EVT_MENU(self, ID_Isothermal  , self.Isothermal  )
        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, wxLEFT|wxCENTER|wxGROW, 1)
        self.sizer.Add(self.toolbar, 0, wxCENTER|wxBOTTOM, 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 = wxPanel(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 = wxStaticText(panel, -1, "    ")
        gamma_text = wxStaticText(panel, 1, "Gamma")
        self.gamma_value = wxTextCtrl(panel, -1, str(self.gamma))

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

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

        for field in self.FIELDS[N:]:
            self.LABELS[field] = wxStaticText(panel, -1, field)
            self.ENTRIES[field] = wxTextCtrl(panel, -1, self.VALUES[field], style = wxTE_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, wxLEFT, 2)
        for field in self.FIELDS:
            fieldSizer.Add(self.LABELS[field], 0, wxLEFT, 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(wxTextCtrl(panel, -1, str(round(SECOND_VALUES[field],4))))

        calculateButton = wxButton(panel, -1, "Calculate")
        resetButton = wxButton(panel, -1, "Reset")
        if Plot:
            plotButton = wxButton(panel, -1, "Plot")
	
            self.plotxOptions =  wxChoice(panel)
            self.plotyOptions =  wxChoice(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(EVT_BUTTON, self.readData, calculateButton)
        self.Bind(EVT_BUTTON, Function, resetButton)
        if Plot:
            self.Bind(EVT_BUTTON, self.plotData, plotButton)
	
	buttonSizer = wxFlexGridSizer(1,5,10,5)

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

        border = wxBoxSizer(wxVERTICAL)
        border.Add(fieldSizer ,0, wxALL, 20)
        border.Add(buttonSizer,0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 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)
            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 = wxMessageDialog(self, msg, title, wxOK | wxICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()
        
    def SetStatusMessage(self, message, colour=wxNullColour):
        if colour!= wxNullColour:
            print message
        myBar = wxStatusBar(self)
        myBar.SetBackgroundColour(colour)
        myBar.SetStatusText(message)
        self.SetStatusBar(myBar)


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

app = GasTablesGUI(0)
app.MainLoop()
