#!/usr/bin/python
# encoding: utf-8
# syntax:python

import sys,os,os.path,time
# get yade path (allow YADE_PREFIX to override)
prefix,suffix='/usr' if not os.environ.has_key('YADE_PREFIX') else os.environ['YADE_PREFIX'],''
# duplicate some items from yade.config here, so that we can increase verbosity when the c++ part is booting
features,version='gts,opengl,openmp,qt4,vtk'.split(','),'0.60.3'

## find available builds
nonDebugLibDir=prefix+'/lib/yade'+suffix
debugLibDir=nonDebugLibDir+'/dbg'
hasDebug,hasNonDebug=os.path.exists(debugLibDir+'/py/yade/__init__.py'),os.path.exists(nonDebugLibDir+'/py/yade/__init__.py')
if hasDebug and hasNonDebug: buildsAvailable='both non-debug and debug build'
elif hasDebug and not hasNonDebug: buildsAvailable='debug build only'
elif not hasDebug and hasNonDebug: buildsAvailable='non-debug build only'
else:
	raise RuntimeError('Neither non-debug nor debug build found! ('+nonDebugLibDir+'/py/yade/__init__.py, '+debugLibDir+'/py/yade/__init__.py)')


# handle command-line options first
import optparse
par=optparse.OptionParser(usage='%prog [options] [ simulation.xml[.bz2] | script.py [script options]]',prog=os.path.basename(sys.argv[0]),version='%s (%s; %s)'%(version,','.join(features),buildsAvailable),description="Yade: open-source platform for dynamic compuations. Homepage http://www.yade-dem.org, code hosted at http://www.launchpad.net/yade. This is version %s (with features %s, %s)."%(version,','.join(features),buildsAvailable))
par.add_option('-j','--threads',help='Number of OpenMP threads to run; defaults to number of cores. Equivalent to setting OMP_NUM_THREADS environment variable.',dest='threads',type='int')
par.add_option('--update',help='Update deprecated class names in given script(s) using text search & replace. Changed files will be backed up with ~ suffix. Exit when done without running any simulation.',dest='updateScripts',action='store_true')
par.add_option('--nice',help='Increase nice level (i.e. decrease priority) by given number.',dest='nice',type='int')
par.add_option('-x',help='Exit when the script finishes',dest='exitAfter',action='store_true')
par.add_option('-v',help='Increase logging verbosity; first occurence sets default logging level to info, second to debug, third to trace.'+
	('' if 'log4cxx' in features else " (Since this build doesn't use log4cxx, this option will only have effect if repeated twice (\-vv), equivalent to setting YADE_DEBUG environment variable)"),action='count',dest='verbosity')
par.add_option('-n',help="Run without graphical interface (equivalent to unsetting the DISPLAY environment variable)",dest='nogui',action='store_true')
par.add_option('--generate-manpage',help="Generate man page documenting this program and exit",dest='manpage',metavar='FILE')
par.add_option('--rebuild',help="Re-run build in the source directory, then run the updated yade with the same command line except \-\-rebuild. The build profile for this build (deb) and its stored parameters will be used.",dest='rebuild',action='store_true')
par.add_option('--test',help="Run regression test suite and exit; the exists status is 0 if all tests pass, 1 if a test fails and 2 for an unspecified exception.",dest="test",action='store_true')
par.add_option('--debug',help='Run the debug build, if available.',dest='debug',action='store_true')
par.add_option('--no-gdb',help='Do not show backtrace when yade crashes (only effective with \-\-debug).',dest='noGdb',action='store_true',)
par.disable_interspersed_args()

opts,args=par.parse_args()

# re-build yade so that the binary is up-to-date
if opts.rebuild:
	import subprocess
	# rebuild
	sourceRoot,profile='/build/buildd/yade-0.60.3','deb' # replaced at install-time
	cmd=['scons','-Q','-C',sourceRoot,'profile=%s!'%profile,'debug=%d'%(1 if opts.debug else 0),'execCheck=%s'%(prefix+'/bin/yade'+suffix)]
	print 'Rebuilding yade using',' '.join(cmd)
	if subprocess.call(cmd): raise RuntimeError('Error rebuilding Yade (--rebuild).')
	# run ourselves
	argv=[v for v in sys.argv if v!='--rebuild']
	print 'Running yade using',' '.join(argv)
	sys.exit(subprocess.call(argv))

if opts.debug:
	if not hasDebug:
		raise RuntimeError('Debug build not available (run without --debug, or try --debug --rebuild)')
	libDir=debugLibDir
else:
	if not hasNonDebug:
		print 'WARNING: non-debug build not available, running with --debug instead (try --rebuild to get the non-debug build).'
	libDir=nonDebugLibDir

## remove later
## python2.5 relative module imports workaround
v=sys.version_info
if v[0]==2 and v[1]<=5:
	for submodule in ('yade','gts','yade/tests'):
		sys.path.append(os.path.join(libDir,'py',submodule))

sys.path.append(os.path.join(libDir,'py'))

# run regression test suite and exit
if opts.test:
	import yade.tests
	try:
		result=yade.tests.testAll()
	except:
		print 20*'*'+' UNEXPECTED EXCEPTION WHILE RUNNING TESTS '+20*'*'
		print 20*'*'+' '+str(sys.exc_info()[0])
		print 20*'*'+" Please report bug at http://bugs.launchpad.net/yade providing the following traceback:"
		import traceback; traceback.print_exc()
		print 20*'*'+' Thank you '+20*'*'
		sys.exit(2)
	if result.wasSuccessful():
		print "*** ALL TESTS PASSED ***"
		sys.exit(0)
	else:
		print 20*'*'+' SOME TESTS FAILED '+20*'*'
		sys.exit(1)


# c++ boot code checks for YADE_DEBUG at some places; debug verbosity is equivalent
# do this early, to have debug messages in the boot code (plugin registration etc)
if opts.verbosity>1: os.environ['YADE_DEBUG']='1'

# this must be done before loading yade libs ("import yade" below)
# has no effeect after libgomp initializes
if opts.threads: os.environ['OMP_NUM_THREADS']=str(opts.threads)
elif int('-1')>0:
	os.environ['OMP_NUM_THREADS']='-1'

sys.stderr.write('Welcome to Yade '+version+'%s\n'%(' (debug build)' if opts.debug else ''))

# initialization and c++ plugins import
import yade
# other parts we will need soon
import yade.config
import yade.wrapper
import yade.log
import yade.system
import yade.runtime

# continue option processing

if opts.updateScripts:
	yade.system.updateScripts(args)
	sys.exit(0)
if opts.manpage:
	import yade.manpage
	yade.manpage.generate_manpage(par,yade.config.metadata,opts.manpage,section=1,seealso='yade%s-batch (1)'%suffix)
	print 'Manual page %s generated.'%opts.manpage
	sys.exit(0)
if opts.nice:
	os.nice(opts.nice)
if yade.config.debug and opts.noGdb:
	yade.wrapper.Omega().disableGdb()
if 'log4cxx' in yade.config.features and opts.verbosity:
	yade.log.setLevel('',[yade.log.INFO,yade.log.DEBUG,yade.log.TRACE][min(opts.verbosity,2)])

# modify sys.argv in-place so that it can be handled by userSession
sys.argv=yade.runtime.argv=args
yade.runtime.opts=opts

from yade import *
from math import *

def userSession(qt4=False):
	# prepare nice namespace for users
	import yade
	import sys
	if len(sys.argv)>0:
		arg0=sys.argv[0]
		if qt4: yade.qt.Controller();
		if sum(bool(arg0.endswith(ext)) for ext in ('.xml','.xml.bz2','.xml.gz','.yade','.yade.gz','.yade.bz2','.bin','.bin.gz','.bin.bz2'))>0:
			if len(sys.argv)>1: raise RuntimeError('Extra arguments to saved simulation to run: '+' '.join(sys.argv[1:]))
			sys.stderr.write("Running simulation "+arg0+'\n')
			O=yade.wrapper.Omega(); O.load(arg0); O.run()
		if arg0.endswith('.py'):
			def runScript(script):
				sys.stderr.write("Running script "+arg0+'\n')
				try:
					execfile(script,globals())
				except SystemExit: raise
				except: # all other exceptions
					import traceback
					traceback.print_exc()
					if yade.runtime.opts.exitAfter: sys.exit(1)
				if yade.runtime.opts.exitAfter: sys.exit(0)
			#if qt4:
			#	class RunScriptThread(QThread):
			#		def __init__(self,script): QThread.__init__(self); self.script=script
			#		def run(self): runScript(self.script)
			#	worker=RunScriptThread(arg0)
			#	worker.start()
			#	print 'worker started'
			#else:
			runScript(arg0)
	if yade.runtime.opts.exitAfter: sys.exit(0)
	# show python console
	if 1:
		from IPython.Shell import IPShellEmbed
		ipshell=IPShellEmbed(
			#exit_msg='Bye.',
			banner='[[ ^L clears screen, ^U kills line. '+
				', '.join((['F12 controller','F11 3d view','F10 both','F9 generator'] if (qt4) else [])+['F8 plot'])+'. ]]',
			rc_override=dict( # ipython options, see e.g. http://www.cv.nrao.edu/~rreid/casa/tips/ipy_user_conf.py
				prompt_in1='Yade [\#]: ',
				prompt_in2='     .\D.: ',
				prompt_out=" ->  [\#]: ",
				separate_in='0',
				separate_out='0',
				separate_out2='0',
				#execfile=[prefix+'/lib/yade'+suffix+'/py/yade/ipython.py'],
				readline_parse_and_bind=[
					'tab: complete',
					# only with the gui
					# the escape codes might not work on non-linux terminals.
					]
					+(['"\e[24~": "\C-Uyade.qt.Controller();\C-M"','"\e[23~": "\C-Uyade.qt.View();\C-M"','"\e[21~": "\C-Uyade.qt.Controller(), yade.qt.View();\C-M"','"\e[20~": "\C-Uyade.qt.Generator();\C-M"'] if (qt4) else []) #F12,F11,F10,F9
					+['"\e[19~": "\C-Uimport yade.plot; yade.plot.plot();\C-M"', #F8
					'"\e[A": history-search-backward', '"\e[B": history-search-forward', # incremental history forward/backward
					]
			)
		)
		ipshell()
		# save history -- a workaround for atexit handlers not being run (why?)
		# http://lists.ipython.scipy.org/pipermail/ipython-user/2008-September/005839.html
		import IPython.ipapi
		IPython.ipapi.get().IP.atexit_operations()

## run userSession in a way corresponding to the features we use:
gui=None
yade.runtime.hasDisplay=False # this is the default initialized in the module, anyway
if 'qt4' in features: gui='qt4'
if opts.nogui: gui=None
if gui:
	import Xlib.display
	# PyQt4's QApplication does exit(1) if it is unable to connect to the display
	# we however want to handle this gracefully, therefore
	# we test the connection with bare xlib first, which merely raises DisplayError
	try:
		# contrary to display.Display, _BaseDisplay does not check for extensions and that avoids spurious message "Xlib.protocol.request.QueryExtension" (bug?)
		Xlib.display._BaseDisplay();
		yade.runtime.hasDisplay=True
	except: 
		# usually Xlib.error.DisplayError, but there can be Xlib.error.XauthError etc as well
		# let's just pretend any exception means the display would not work
		gui=None

# run remote access things, before actually starting the user session
from yade import remote
yade.remote.useQThread=(gui=='qt4')
yade.remote.runServers()

if gui==None:
	userSession()
elif gui=='qt4':
	## we already tested that DISPLAY is available and can be opened
	## otherwise Qt4 might crash at this point
	import PyQt4
	from PyQt4 import QtGui
	from PyQt4.QtCore import *
	import yade.qt # this yade.qt is different from the one that comes with qt3
	qapp=QtGui.QApplication(sys.argv)
	userSession(qt4=True)
O.exitNoBacktrace()

