#!/usr/bin/python
#######################################################################
#
# $Id: runTLMGine.py,v 1.9 2008-02-26 06:07:17 lorenz Exp $
# 
# Copyright (C) 2004-2008 Petr Lorenz
# All rights reserved.
# 
# ##### BEGIN LICENSE BLOCK #####
#
# Version: MPL 1.1 / GPL 2.0 / LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
# 
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
# License for the specific language governing rights and limitations
# under the License.
# 
# The Original Code is Petr Lorenz emGine Graphical User Interface (GUI).
# 
# The Initial Developer of the Original Code is Petr Lorenz.
# Portions created by the Initial Developer are Copyright (C) 2004-2008.
# All Rights Reserved.
# 
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the  "GPL"), or the
# GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
# case the provisions of the GPL or the LGPL are applicable instead of those
# above.  If you wish to allow use of your version of this file only under the
# terms of either the GPL or the LGPL and not to allow others to use your
# version of this file under the MPL, indicate your decision by deleting the
# provisions above and replace them with the notice and other provisions
# required by the GPL or LGPL.  If you do not delete the provisions above, a
# recipient may use your version of this file under the terms of any one of the
# MPL License, the GPL or the LGPL.
#
# ##### END LICENSE BLOCK #####
#
#######################################################################
# 
# $Log: runTLMGine.py,v $
# Revision 1.9  2008-02-26 06:07:17  lorenz
# Bugfixes for terminating a simulation on Windows.
#
# Revision 1.8  2008-02-14 05:59:28  lorenz
# Simulation thread improvements - lock, better IPC, termination.
#
# Revision 1.7  2008-02-13 15:28:30  lorenz
# Windows simulation termination implemented.
#
# Revision 1.6  2008-02-08 05:05:11  lorenz
# Unix support for terminating a simulation.
#
# Revision 1.5  2008-02-07 05:45:41  lorenz
# * Fixed 'Simulation Boundary View' check problem
#   when closing a project.
#
# * Fixed Bounding Box problems when moving/editing a geometry.
#
# Revision 1.4  2008-02-03 16:36:27  lorenz
# Windows starting of tlmGine from a directory with a space
# in the name.
#
# Revision 1.3  2008-02-01 14:21:02  lorenz
# Bugfix for windows path names with spaces.
#
# Revision 1.2  2008-01-30 15:32:34  lorenz
# The binary name modified to 'tlmGine'.
#
# Revision 1.1.1.1  2008-01-28 06:15:35  lorenz
# Import emGine GUI v. 0.7.1
#
# Revision 1.3  2007-07-02 06:37:24  lorenz
# Clean up of old code.
#
# Revision 1.2  2007-05-14 04:51:34  lorenz
# Syntax of starting the tlmGine modified
#
# Revision 1.1.1.1  2007-05-08 06:22:35  lorenz
# Initial Import
#
#######################################################################

from emGineGUI import consts

import wx
import os, sys, getopt, thread, time
#import Tkinter
import signal 

import subprocess

import math
#try: 
#    import awtGfx as GfxDriver
#except ImportError:
#    try:
#        import wxGfx as GfxDriver
#    except ImportError:
#        try:
#            import gtkGfx as GfxDriver
#        except ImportError:
#            import tkGfx as GfxDriver

#import tkGfx as GfxDriver
#import Graph, Gfx

try:
  import win32process
except:
  pass


GLOB_verbose = False
GLOB_version = '0.2'
GLOB_tlmGine_bin_name = 'tlmGine'

GLOB_EMM_FILE_SUFFIX = 'emm'

# The standard file descriptors
STDIN_FILENO  = 0
STDOUT_FILENO = 1
STDERR_FILENO = 2

GLOB_t1 = 0.0
GLOB_t2 = 0.0

GLOB_RUNNING = False
GLOB_PID = 0

def execute_simulation(cmd, args, log_device, platform):
  pltf_name = platform["name"]
  log_device.progress = 0.0

  log_device.status1_lock.acquire()
  log_device.status1 = consts.EMGINE_SIMULATION_RUNNING
  log_device.status1_lock.release()

  if pltf_name == "LINUX":
    # Using the pipe function (lower level than popen)
    (r_end, w_end) = os.pipe()

    # Create child process; Child pid = has zero 
    pid = os.fork()

    if pid:
      ##########
      ### Parent process:
      ##########

      # Work on file descriptors...
      os.close(w_end)

      while 1:
	if log_device.status1 == consts.EMGINE_SIMULATION_TERMINATE:
	  # Terminating the running simulation
	  wx.CallAfter(log_device.write, "INFO: Terminating simulation!")
	  os.kill(pid, signal.SIGKILL)
	  break
	else:

	  data = os.read(r_end, 1024)	# XXX: Blocking read? Probably yes
	  if data == '':	# eof
	    break
	  if data.find('PROGRESS') != -1:
	    # FIXME: This code is Q & D
	    progress = data.split()[1]
	    #print progress

	    # FIXME! This is dangerous !!
	    try:
	      log_device.progress = float(progress)
	    except:
	      pass
	  else:
	    wx.CallAfter(log_device.write, "%s" %(data))

      (w_pid, w_retcode) = os.waitpid(pid, 0)
      print "w_pid: ", w_pid
      print "w_retcode: ", w_retcode
    else:
      ##########
      ### Child process:
      ##########
      # Close STDOUT and make a copy of w_end to STDOUT through pipe
      os.close(r_end)
      os.close(STDERR_FILENO)
      os.close(STDOUT_FILENO)
      os.dup(w_end)
      os.close(w_end)

      # Start the actual simulation
      #os.execv(cmd, args)
      os.execlp(cmd, cmd, args[0], args[1])
      print >> sys.stderr, "** Fatal error! Simulation could not be started!"
      sys.exit(-1)

    #os.system(cmdline)

  elif pltf_name == "WIN32":
    import subprocess
    import win32api, win32process

    # Windows running
    #print "Starting simulation on Windows!"

    # run_cmd = "\"%s\" %s %s" %(cmd, args[0], args[1])
    # r_end = os.popen(run_cmd, "r")

    # TODO: How to start a process on windows, with no shell window,
    # TODO: but with no shell = True ? This did not work with py2exe.
    # TODO: If shell = False, then a "black" shell window is shown,
    # TODO: when the simulation is started. 12.02.2008
    #subproc = subprocess.Popen( (cmd, args[0], args[1]), stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)
    subproc = subprocess.Popen( (cmd, args[0], args[1]), stdout = subprocess.PIPE, shell = False)
    r_end = subproc.stdout
    #rerr_end = subproc.stderr

    while 1:
      # wx.CallAfter(log_device.write, "INFO: Simulation running (PID: %d)!" %(subproc.pid))
      if log_device.status1 == consts.EMGINE_SIMULATION_TERMINATE:
	# Terminating the running simulation
	wx.CallAfter(log_device.write, "INFO: Terminating simulation (PID: %d)!" %(subproc.pid))
	win32process.TerminateProcess(subproc._handle, 0)
	win32api.Sleep(100)	# wait for resources to be released
	break
      else:
	#data = r_end.read(1024)	# XXX: Blocking read? Probably yes
	data = r_end.readline()	# XXX: Blocking read? Probably yes
	if data == '':	# eof
	  break
	if data.find('PROGRESS') != -1:
	  # FIXME: This code is Q & D
	  progress = data.split()[1]
	  #print progress

	  # FIXME! This is dangerous !!
	  try:
	    log_device.progress = float(progress)
	  except:
	    pass
	else:
	  wx.CallAfter(log_device.write, "%s" %(data))

    #(w_pid, w_retcode) = os.waitpid(pid, 0)
    #log_device.progress = 100
    #wx.CallAfter(log_device.write, ">>>> Simulation finished <<<<")

    r_end.close()
    # rerr_end.close()
    subproc.wait()

  # send a message to the main thread that the simulation is finished
  log_device.progress = 100
  #log_device.progress = consts.EMGINE_SIMULATION_FINISHED_OK	

  log_device.status1_lock.acquire()
  log_device.status1 = consts.EMGINE_SIMULATION_FINISHED	
  log_device.status1_lock.release()

  # while log_device.status1 != consts.EMGINE_SIMULATION_EXIT_THREAD:
  #   wx.MilliSleep(100)
  # print "Exiting thread..."

############################################
### class runTLMGine
############################################
class runTLMGine:
  def __init__(self, platform, log_device = None):
    global GLOB_tlmGine_bin_name

    self.app_path = os.getcwd()
    #print "App path: ", self.app_path
    self.platform = platform
    self.log_device = log_device
    self.bin_tlmGine = os.path.join(self.app_path, "bin", platform["name"], GLOB_tlmGine_bin_name + platform["execsuffix"])
    self.tmp_path = os.path.join(self.app_path, "tmp")



  def run(self, EMMfname_full, output_file_suffix = ''):
    """ Run the simulation and return a dict with info """
    pth = os.path.split(EMMfname_full)
    EMMfname = pth[1]
    pth = pth[0]

    # create the name of the time-domain output file
    EMMfname_base = EMMfname
    if EMMfname_base.endswith('.' + GLOB_EMM_FILE_SUFFIX):
      EMMfname_base = EMMfname_base[:-4]
    TDSfname = EMMfname_base + output_file_suffix + ".tds"

    #TDSignal_fname = os.path.join(self.tmp_path, TDSfname)

    ### XXX: not needed sine always in the working directory of the EMM model
    ### TDSignal_fname = os.path.join(pth, TDSfname)
    TDSignal_fname = TDSfname

    #runcmd = self.bin_tlmGine + " " + EMMfname_full + " 2>/dev/null" + " | tee " + TDSignal_fname
    runcmd = self.bin_tlmGine + " " + EMMfname_full + " " + TDSignal_fname + " 2>/dev/null"
    print runcmd
    sim_thid = thread.start_new_thread(execute_simulation, (self.bin_tlmGine, [EMMfname_full, TDSignal_fname], self.log_device, self.platform))
    # os.system(runcmd)

    # return a dict with information
    ret = {}
    ret["tds_file"] = [TDSignal_fname]	# returning just one signal
    return ret

  def get_id_name(self):
    return "tlmGine"

