#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
***********************************************************************************
                            tutorial17.py
                DAE Tools: pyDAE module, www.daetools.com
                Copyright (C) Dragan Nikolic
***********************************************************************************
DAE Tools is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License version 3 as published by the Free Software
Foundation. DAE Tools 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.
You should have received a copy of the GNU General Public License along with the
DAE Tools software; if not, see <http://www.gnu.org/licenses/>.
************************************************************************************
"""
__doc__ = """
This tutorial introduces the following concepts:

- TCPIP Log and TCPIPLogServer

In this example we use the same heat transfer problem as in the tutorial 7.

The screenshot of the TCP/IP log server:

.. image:: _static/tutorial17-screenshot.png
   :width: 500px

The temperature plot:

.. image:: _static/tutorial17-results.png
   :width: 500px
"""

import os, sys, threading
from PyQt5 import QtCore, QtGui, QtWidgets
from time import localtime, strftime, sleep
from os.path import join, realpath, dirname
from subprocess import Popen, call
from daetools.pyDAE import *
try:
    from .tutorial17_ui import Ui_tcpipLogServerMainWindow
except Exception as e:
    from tutorial17_ui import Ui_tcpipLogServerMainWindow

# Standard variable types are defined in variable_types.py
from pyUnits import m, kg, s, K, Pa, mol, J, W, kW

class modTutorial(daeModel):
    def __init__(self, Name, Parent = None, Description = ""):
        daeModel.__init__(self, Name, Parent, Description)

        self.m     = daeParameter("m",       kg,           self, "Mass of the copper plate")
        self.cp    = daeParameter("c_p",     J/(kg*K),     self, "Specific heat capacity of the plate")
        self.alpha = daeParameter("&alpha;", W/((m**2)*K), self, "Heat transfer coefficient")
        self.A     = daeParameter("A",       m**2,         self, "Area of the plate")
        self.Tsurr = daeParameter("T_surr",  K,            self, "Temperature of the surroundings")

        self.Qin  = daeVariable("Q_in",  power_t,       self, "Power of the heater")
        self.T    = daeVariable("T",     temperature_t, self, "Temperature of the plate")

    def DeclareEquations(self):
        daeModel.DeclareEquations(self)

        eq = self.CreateEquation("HeatBalance", "Integral heat balance equation")
        eq.Residual = self.m() * self.cp() * dt(self.T()) - self.Qin() + self.alpha() * self.A() * (self.T() - self.Tsurr())

class simTutorial(daeSimulation):
    def __init__(self):
        daeSimulation.__init__(self)
        self.m = modTutorial("tutorial17")
        self.m.Description = __doc__

    def SetUpParametersAndDomains(self):
        self.m.cp.SetValue(385 * J/(kg*K))
        self.m.m.SetValue(1 * kg)
        self.m.alpha.SetValue(200 * W/((m**2)*K))
        self.m.A.SetValue(0.1 * m**2)
        self.m.Tsurr.SetValue(283 * K)

    def SetUpVariables(self):
        self.m.Qin.AssignValue(1500 * W)
        self.m.T.SetInitialCondition(283 * K)

    def Run(self):
        # The default Run() function is re-implemented here (just the basic version)
        # to allow simulation to wait for certain period between time intervals
        while self.CurrentTime < self.TimeHorizon:
            self.Log.Message('Integrating from %f to %f ...' % (self.CurrentTime, self.CurrentTime+self.ReportingInterval), 0)
            self.IntegrateForTimeInterval(self.ReportingInterval, eDoNotStopAtDiscontinuity)
            self.ReportData(self.CurrentTime)
            self.Log.SetProgress(int(100.0 * self.CurrentTime/self.TimeHorizon))
            sleep(0.2)

        self.Log.Message('The simulation has finished succesfully!', 0)

class tcpipLogServer(daeTCPIPLogServer):
    def __init__(self, port, app, textEdit):
        if port <= 0:
            cfg  = daeGetConfig()
            port = cfg.GetInteger("daetools.logging.tcpipLogPort", 51000)

        daeTCPIPLogServer.__init__(self, port)

        self.app      = app
        self.textEdit = textEdit

    def MessageReceived(self, message):
        self.textEdit.append(message)
        if self.textEdit.isVisible() == True:
            self.textEdit.update()
        self.textEdit.verticalScrollBar().setSliderPosition(self.textEdit.verticalScrollBar().maximum())
        self.app.processEvents()

class tcpipLogServerMainWindow(QtWidgets.QMainWindow):
    def __init__(self, app):
        QtWidgets.QMainWindow.__init__(self)

        self.ui = Ui_tcpipLogServerMainWindow()
        self.ui.setupUi(self)
        #self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setWindowIcon(QtGui.QIcon(join(dirname(__file__), 'daetools-48x48.png')))
        self.setGeometry(0, 100, 600, 200) # Position window
        self.resize(600, 300)  # Resize window

        # Create TCPIP log server
        self.logServer = tcpipLogServer(0, app, self.ui.messagesEdit)
        # Start TCPIP log server
        self.logServer.Start()

class simulationThread(threading.Thread):
    def __init__(self, saveReports):
        threading.Thread.__init__(self)

        self.saveReports = saveReports

    def run(self):
        # Create a delegate log and add two logs: stdout and tcpip logs
        log, log1, log2  = setupLog()

        # Create Solver, DataReporter and Simulation object
        daesolver    = daeIDAS()
        datareporter = daeTCPIPDataReporter()
        simulation   = simTutorial()

        # Enable reporting of all variables
        simulation.m.SetReportingOn(True)

        # Set the time horizon and the reporting interval
        simulation.ReportingInterval = 10
        simulation.TimeHorizon = 500

        # Connect data reporter
        simName = simulation.m.Name + strftime(" [%d.%m.%Y %H:%M:%S]", localtime())
        if(datareporter.Connect("", simName) == False):
            sys.exit()

        # Initialize the simulation
        simulation.Initialize(daesolver, datareporter, log)
        
        # Save the model report and the runtime model report
        if self.saveReports:
            simulation.m.SaveModelReport(simulation.m.Name + ".xml")
            simulation.m.SaveRuntimeModelReport(simulation.m.Name + "-rt.xml")

        # Solve at time=0 (initialization)
        simulation.SolveInitial()

        simulation.Run()
        simulation.Finalize()

def setupLog():
    log  = daeDelegateLog()
    log1 = daePythonStdOutLog()
    log2 = daeTCPIPLog()

    # Connect TCPIP log
    if(log2.Connect("", 0) == False):
        sys.exit()

    log.AddLog(log1)
    log.AddLog(log2)

    # Return all of them for we have to keep references to them
    # and prevent them going out of scope
    return log, log1, log2

# Use daeSimulator class
def runGUI(app):
    # Start TCP/IP log server
    log_server = tcpipLogServerMainWindow(app)
    log_server.show()
    # Give it some time to start the TCP/IP server
    sleep(1)

    # Create a delegate log and add two logs: stdout and tcpip logs
    log, log1, log2  = setupLog()

    sim = simTutorial()
    sim.m.SetReportingOn(True)
    sim.ReportingInterval = 10
    sim.TimeHorizon       = 500
    simulator  = daeSimulator(app, simulation=sim, log = log)
    simulator.exec_()
    app.exec_()

# Setup everything manually and run in a console
def runConsole(app):
    # Start TCP/IP log server
    log_server = tcpipLogServerMainWindow(app)
    log_server.show()
    # Give it some time to start the TCP/IP server
    sleep(1)

    # Create and start a thread with the simulation
    st = simulationThread(True)
    st.start()

    app.exec_()
    st.join()

def run(**kwargs):
    qtApp  = kwargs.get('qtApp',  None)
    guiRun = kwargs.get('guiRun', False)
    simulation = simTutorial()
    if guiRun:
        runGUI(qtApp)
    else:
        runConsole(qtApp)

if __name__ == "__main__":
    qtApp  = daeCreateQtApplication(sys.argv)
    guiRun = False if (len(sys.argv) > 1 and sys.argv[1] == 'console') else True
    run(guiRun = guiRun, qtApp = qtApp)