#!/usr/bin/env python
#
# ecell3-dmc  - a program to compile and/or docify dynamic module in 
#               E-Cell Simulation Environment Version 3
#
# authors: Tomoya Kitayama <tomo@e-cell.org>
#          Satya Arjunan <satya@sfc.keio.ac.jp>


import sys
import os
import getopt
import operator
from glob import glob
from distutils.util import split_quoted

# Inspired by BTL::platform::get_module_filename
def get_running_executable():
    if getattr( sys, 'frozen', '' ) == 'windows_exe':
        import win32api
        return win32api.GetModuleFileName( 0 )
    else:
        return os.path.abspath( sys.argv[0] )

per_app_dir_name = "ecell-3.2"

prefix = '''/usr'''
if len( prefix ) == 0 or not os.path.isdir( prefix ):
    prefix = os.path.dirname(
        os.path.dirname( get_running_executable() )
        )

if os.name == 'nt':
    try:
        from win32com.shell import shell, shellcon
        home_dir = shell.SHGetPathFromIDList(
            shell.SHGetSpecialFolderLocation( 0, shellcon.CSIDL_PROFILE )
            )
        user_prefs_dir = os.path.join(
            shell.SHGetPathFromIDList(
                shell.SHGetSpecialFolderLocation( 0, shellcon.CSIDL_APPDATA )
                ),
            per_app_dir_name
            )
    except:
        home_dir = os.environ['USERPROFILE']
        user_prefs_dir = os.environ['APPDATA']
else:
    home_dir = os.path.expanduser( '~' )
    user_prefs_dir = os.path.join( home_dir, '.' + per_app_dir_name )

def expand_shell_variables( val, vars ):
    import re
    return re.compile( r'\$\{([^}]+)\}' ).sub(
        lambda m: vars[ m.group( 1 ) ], val )

exec_prefix = '''/usr'''
exec_prefix = expand_shell_variables( exec_prefix, locals() )
if len( exec_prefix ) == 0 or not os.path.isdir( exec_prefix ):
    exec_prefix = prefix

bin_dir = '''/usr/bin'''
bin_dir = expand_shell_variables( bin_dir, locals() )
if len( bin_dir ) == 0 or not os.path.isdir( bin_dir ):
    bin_dir = os.path.join( prefix, 'bin' )

_include_dir = '''/usr/include'''
_include_dir = expand_shell_variables( _include_dir, locals() )
if len( _include_dir ) == 0 or not os.path.isdir( _include_dir ):
    _include_dir = os.path.join( prefix, 'include' )
include_dir = os.path.join( _include_dir, per_app_dir_name  )

_lib_dir = '''/usr/lib'''
_lib_dir = expand_shell_variables( _lib_dir, locals() )
if len( _lib_dir ) == 0 or not os.path.isdir( _lib_dir ):
    _lib_dir = os.path.join( prefix, 'lib' )
lib_dir = os.path.join( _lib_dir, per_app_dir_name  )

    
DMCOMPILE = 'dmcompile'
CLASS_NAME_MACRO_NAME = '_ECELL3_DM_CLASSNAME'

progname = os.path.basename( sys.argv[ 0 ] )

def usage():
    print '''
%(progname)s -- Compile dynamic modules for E-Cell Simulation Environment Version 3.

Usage:
    %(progname)s [options] [sourcefile(s)]
    %(progname)s [-h] or [--help]
    ''' % dict( progname = progname )

def help():
    usage()
    print '''
%(progname)s options:
    --no-stdinclude          Don't set standard include file path.
    --no-stdlibdir           Don't set standard library directory path.
    --ldflags=[ldflags]      Specify options to the linker.
    --cxxflags=[cxxflags]    Add compiler options.
    --no-define-cxxflags=[cxxflags]
                             Override the default compiler options.
    --dmcompile=[path]       Specify dmcompile path.
    -v or --verbose          Be verbose.
    -h or --help             Print this message.


This program is part of E-Cell Simulation Environment Version 3.
Written by:
    Tomoya Kitayama <tomo@e-cell.org>
    Satya Arjunan <satya@ttck.keio.ac.jp>
    ''' % dict( progname = progname )

def msg( outstr ):
    global progname
    print "%s: %s" % ( progname, outstr )

spawnvp = os.spawnv

if hasattr(os, 'spawnvp'):
    spawnvp = os.spawnvp
elif os.name == 'nt':
    def lookup_executable_path_nt(path, file):
        for p in path:
            for ext in ('', '.exe', '.cmd', '.bat'):
                _file = os.path.join(p, file) + ext
                try:
                    stat = os.stat(_file)
                    if stat.st_mode & 0500 == 0500:
                        return _file
                except:
                    pass
        return None

    def spawnvp(mode, file, args):
        if os.path.dirname(file) == '':
            path = os.environ.get('PATH', None)
            if path is not None:
                path = path.split(os.pathsep)
            else:
                path = os.defpath
            _file = lookup_executable_path_nt(path, file)
            if _file is not None:
                file = _file
        return os.spawnv(mode, file, args)


def main():
    # -------------------------------------
    # initialize file names
    # -------------------------------------
    STDLIBDIRS = [ '-L' + _lib_dir, '-L' + lib_dir ]
    STDINCLUDE = [ '-I' + _include_dir,
                   '-I' + include_dir,
                   '-I' + os.path.join(include_dir, 'libecs') ]
    SRC = None


    # -------------------------------------
    # get options and arguments
    # -------------------------------------
    try:
        opts , args = getopt.getopt( sys.argv[1:], "hvo:",
                ["help", "verbose", "no-stdinclude","no-stdlibdir", "ldflags=",
                 "cxxflags=", "no-define-cxxflags=", "dmcompile=", "output="])
    except:
        help()
        sys.exit( 1 ) 
        
    VERBOSE = False
    NO_STDINCLUDE = False
    NO_STDLIBDIR = False
    NO_DEFINE_CXXFLAGS = False
    LDFLAGS = []
    CXXFLAGS = []
    CXXCOMPILEFLAGS = []
    OUTPUT = None
    cmdList = []
    dmcompile = DMCOMPILE

    # -------------------------------------
    # check argument
    # -------------------------------------
    for anOption, anArg in opts:

        # print help message
        if anOption in ( "-h", '--help' ):
            help()
            return 255
      
        # be verbose
        if anOption in ( "-v", '--verbose'):
            VERBOSE = True
            
        # without stdinclude            
        if anOption == "--no-stdinclude":
            NO_STDINCLUDE = True

        # without stdlibdir            
        if anOption == "--no-stdlibdir":
            NO_STDLIBDIR = True

        # set ldflags
        if anOption == "--ldflags":
            LDFLAGS.extend( split_quoted( anArg ) )

        # set no-define-cxxflags
        if anOption == "--no-define-cxxflags":
            NO_DEFINE_CXXFLAGS = True
            CXXFLAGS.extend( split_quoted( anArg ) )

        # set cxxflags
        if anOption == "--cxxflags":
            CXXFLAGS.extend( split_quoted( anArg ) )

        # set cxxflags
        if anOption == "--dmcompile":
            if os.path.isdir( anArg ):
                dmcompile = os.path.join( anArg, DMCOMPILE )
            else:
                dmcompile = anArg

        # set output file
        if anOption in ( "-o", "--output" ):
            OUTPUT = anArg

    # check if source file is given
    if len( args ) < 1:
        help()
        msg( "no source file is given." )
        return 1

    cmdList.append( dmcompile )

    if VERBOSE:
        cmdList.append( '-v' )

    if OUTPUT:
        cmdList += [ '-o', OUTPUT ]

    # on MS-Windows, command line doesn't expand *.cpp automatically
    srcs = reduce( operator.add, map( glob, args ) )

    # check if source file is valid
    if len( srcs ) < 1:
        msg( "source file "+ args[0] + " was not found." )
        return 1

    if NO_STDINCLUDE:
        STDINCLUDE = []

    if not NO_STDLIBDIR:
        LDFLAGS.extend( STDLIBDIRS )
  
    LDFLAGS.append( '-lecs' )
    LDFLAGS.append( '-lgsl' )

    cmdList.extend( STDINCLUDE )
    cmdList.extend( CXXFLAGS )
    cmdList.extend( LDFLAGS )

    for src in srcs:
        ext = os.path.basename( src )
        classname, ext = os.path.splitext( ext )
        argList = cmdList + [ '-D%s=%s' % ( CLASS_NAME_MACRO_NAME, classname ), src ]

        if VERBOSE:
            print ' '.join( argList[0:] )
            
        # Need to us os.spawnv because of problems in MS-Windows with os.sytem
        # when the dmcompile path contains whitespaces even if we force quotes
        ret = spawnvp( os.P_WAIT, cmdList[0], argList )
        if ret != 0:
            msg( '%s returned error status: %d' % ( cmdList[0], ret ) )
            return 1
    return 0

if __name__ == '__main__':
    sys.exit( main() )
