#!/usr/bin/env python
#  -*- coding: utf-8 -*-
# Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
#
# Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
#

import os,sys,optparse
import socket,shutil
import re

usage="""
        Connect to a SALOME session (local or remote) and execute a Python script with data files in argument (optional)

        Usage: %prog [ options ] script

          script           : The Python script to execute in the SALOME session

        SALOME SESSION
        --------------
        If PORT and MACHINE are not given, try to connect to the last active session on the local machine
        If PORT and MACHINE are given, try to connect to the remote session associated with PORT on MACHINE
        If MACHINE is not given, try to connect to the session associated to PORT on the local machine
        If PORT is not given, try to connect to the remote session associated to port 2810 on MACHINE

        If MACHINE isn't localhost or hostname, USER is needed

        If Salome isn't installed on the local machine, use APPLI_DIST to execute a distant session


        INPUTS AND OUPUTS
        -----------------
        script is needed in all case
        INPUT and OUTPUT are used only when Salome isn't on local machine. It defines a list of script (full path, blank separated)
        Actions done with these two lists are :
        - adjustment of path in a temporary script file
        - copy of the input files and script file on the distant machine

"""

appli_local=os.path.realpath(os.path.dirname(__file__))

def get_hostname():
  return socket.gethostname().split('.')[0]

def vararg_callback(option, opt_str, value, parser):
  """optparse callback for variable length arguments"""
  assert value is None

  done = 0
  value = []
  rargs = parser.rargs
  while rargs:
    arg = rargs[0]

    # Stop if we hit an arg like "--foo", "-a", "-fx", "--file=f",
    # etc.  Note that this also stops on "-3" or "-3.0", so if
    # your option takes numeric values, you will need to handle this.
    if ((arg[:2] == "--" and len(arg) > 2) or
        (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")):
      break
    else:
      value.append(arg)
      del rargs[0]

  setattr(parser.values, option.dest, value)

def parse_args():
  """parse arguments, check validity and set defaults"""
  parser = optparse.OptionParser(usage=usage)
  parser.add_option('-p','--port', dest="port", default='', help="The port to connect to")
  parser.add_option('-m','--machine', dest="machine", default='', help="The computer to connect to")
  parser.add_option('-d','--directory', dest="directory", help="[Distant Mode] The APPLI_HOME path on the distant machine (must be used if Salome isn't on the local machine)")
  parser.add_option('-u','--user', dest="user", default='', help="[Distant Mode] The user on the computer to connect to")
  parser.add_option('-i','--infiles', dest="infiles", default=[], action="callback", callback=vararg_callback,
                    help="[Distant Mode] The list of input files (blank separated) used in the Python script")
  parser.add_option('-o','--outfiles', dest="outfiles", default=[], action="callback", callback=vararg_callback,
                    help="[Distant Mode] The list of output files (blank separated) generated by the Python script")

  args=sys.argv[1:]

  script=""
  if args:
    script=args.pop()

  options, args = parser.parse_args(args)

  #check the arguments
  if not os.path.exists(script):
    print "ERROR: the script file is mandatory"
    sys.exit(1)

  machine=options.machine
  port=options.port
  user=options.user
  infiles = options.infiles
  outfiles = options.outfiles
  directory=options.directory

  mode="local"

  if machine:
    here=get_hostname()
    if machine != here and machine != "localhost":
      #SALOME server is on a remote computer
      mode="remote"

      if not user:
        print "ERROR: the remote execution needs -u user argument"
        sys.exit(1)

      if not os.path.exists(os.path.join(appli_local,"runSession")):
        if not directory:
          print "ERROR: the remote execution without SALOME installation needs -d directory argument"
          sys.exit(1)

  return mode,user,machine,port,directory,infiles,outfiles,script

def copy_files(user,machine,script,infiles,outfiles,directory):
  """modify script, copy files to remote computer and return lists of copied files"""

  namescript=os.path.basename(script)
  logname=os.getenv("LOGNAME",user)
  tmp_script="/tmp/%s_%s_%s" % (logname,os.getpid(),namescript)
  fscript=open(script)
  script_text=fscript.read()
  fscript.close()

  list_infiles=[]
  list_outfiles=[]

  n=0
  for infile in infiles:
    # generate a temporary file name
    namefile=os.path.basename(infile)
    tmp_file="/tmp/%s_%s_i%s_%s" % (logname,os.getpid(),n,namefile)

    #modify the salome script
    script_text = re.sub(infile,tmp_file,script_text)

    # copy the infile to the remote server (into /tmp)
    cmd="scp %s %s@%s:%s" % (infile,user,machine,tmp_file)
    print "[  SCP  ]",cmd
    os.system(cmd)

    list_infiles.append(tmp_file)
    n=n+1

  n=0
  for outfile in outfiles:
    # generate a temporary file name
    namefile=os.path.basename(outfile)
    tmp_file="/tmp/%s_%s_o%s_%s" % (logname,os.getpid(),n,namefile)

    #modify the salome script
    script_text = re.sub(outfile,tmp_file,script_text)

    list_outfiles.append(tmp_file)
    n=n+1

  fscript=open(tmp_script,'w')
  fscript.write(script_text)
  fscript.close()

  if directory:
    #copy the salome script on the remote server
    cmd="scp %s %s@%s:%s" % (tmp_script,user,machine,tmp_script)
    print "[  SCP  ]",cmd
    os.system(cmd)

  return list_infiles, list_outfiles, tmp_script

def main():

  mode,user,machine,port,directory,infiles,outfiles,script = parse_args()

  tmp_script=script

  print "mode:",mode

  if mode == "remote":
    list_infiles, list_outfiles, tmp_script = copy_files(user,machine,script,infiles,outfiles,directory)

  #################################################
  #          Execution                            #
  #################################################
  if directory:
    print "[ REMOTE ]"

    # execute runSession from the remote SALOME application
    cmd="ssh %s@%s %s/runSession " % (user,machine,directory)
    if port:
      cmd=cmd+"-p %s "%port
    cmd = cmd +"python " + tmp_script
    print '[  SSH   ] ' + cmd
    os.system(cmd)

  else:
    print "[ LOCAL ]"

    # execute runSession from the local SALOME application
    cmd="%s/runSession " % appli_local
    if machine:
      cmd=cmd+"-m %s "%machine
    if port:
      cmd=cmd+"-p %s "%port
    cmd = cmd +"python " + tmp_script
    print '[  SH   ] ' + cmd
    os.system(cmd)

  #################################################
  #          Get remote files and clean           #
  #################################################
  if mode == "remote":
    temp_files=list_infiles+list_outfiles

    #get the outfiles
    for outfile in outfiles:
      remote_outfile=list_outfiles.pop(0)
      cmd="scp %s@%s:%s %s" %(user,machine,remote_outfile,outfile)
      print "[  SCP  ] "+cmd
      os.system(cmd)

    #clean temporary files
    cmd="ssh %s@%s \\rm -f %s" % (user,machine," ".join(temp_files))
    print '[  SSH   ] ' + cmd
    os.system(cmd)
    os.remove(tmp_script)

if __name__ == '__main__':
  main()

