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

import platform

import Utils
import Options


# the following two variables are used by the target "waf dist"
VERSION=''
APPNAME=''


# these variables are mandatory ('/' are converted automatically)
top = '.'
out = 'build'

# TODO extensive testing with different configuration (should work with Python >= 2.3)

def set_options(opt):
    myplatform = Utils.detect_platform()
    
    if myplatform.startswith('linux'):
        opt.add_option('--no-lsb',
                action = 'store_true',
                help = 'Prevent building LSB (Linux Standard Base) bootloader.',
                default = False,
                dest = 'nolsb')
        opt.add_option('--lsbcc-path',
                action = 'store',
                help = 'Path to lsbcc. By default PATH is searched for lsbcc otherwise is tried file /opt/lsb/bin/lsbcc. [Default: lsbcc]',
                default = 'lsbcc',
                dest = 'lsbcc_path')
        opt.add_option('--lsb-target-version',
                action = 'store',
                help = 'Specify LSB target version [Default: 4.0]',
                default = '4.0',
                dest = 'lsb_version')

    opt.tool_options('compiler_cc')
    opt.tool_options('python')



def configure(conf):

    conf.env.NAME = 'default'

    opt = Options.options
    myplatform = Utils.detect_platform()
    architecture = platform.architecture()[0] # 32bit or 64bit
    Utils.pprint('CYAN', '%s-%s detected' % (platform.system(), architecture))

    if myplatform.startswith('linux') and not opt.nolsb:
        Utils.pprint('CYAN', 'Building LSB bootloader.')

    if myplatform.startswith('darwin'):
        conf.env.MACOSX_DEPLOYMENT_TARGET = '10.4'

    ### C compiler

    if myplatform.startswith('win'):
        try:
            # mingw (gcc for Windows)
            conf.check_tool('gcc')
        except:
            try:
                # Newest detected MSVC version is used by default.
                # msvc 7.1 (Visual Studio 2003)
                # msvc 8.0 (Visual Studio 2005)
                # msvc 9.0 (Visual Studio 2008)
                #conf.env['MSVC_VERSIONS'] = ['msvc 7.1', 'msvc 8.0', 'msvc 9.0']
                if architecture == '32bit':
                    conf.env['MSVC_TARGETS'] = ['x86', 'ia32']
                elif architecture == '64bit':
                    conf.env['MSVC_TARGETS'] = ['x64', 'x86_amd64', 'intel64', 'em64t']
                conf.check_tool('msvc')
                conf.print_all_msvc_detected()
            except:
                Utils.pprint('RED', 'GCC (MinGW) or MSVC compiler not found.')
                exit(1)
        else:
            # When using GCC, deactivate declarations after statements
            # (not supported by Visual Studio)
            conf.env.append_value('CCFLAGS', '-Wdeclaration-after-statement')
            conf.env.append_value('CCFLAGS', '-Werror')

    else:
        conf.check_tool('compiler_cc')


    conf.check_tool('python')
    conf.check_python_headers()

    # Do not link with libpython and libpthread.
    for lib in conf.env.LIB_PYEMBED:
        if lib.startswith('python') or lib.startswith('pthread'):
            conf.env.LIB_PYEMBED.remove(lib)


    ### build LSB (Linux Standard Base) boot loader
    if myplatform.startswith('linux') and not opt.nolsb:
        try:
            # custom lsbcc path
            if opt.lsbcc_path != 'lsbcc':
                conf.env.LSBCC = conf.find_program(opt.lsbcc_path, mandatory=True)
            # default values
            else:
                conf.env.LSBCC = conf.find_program(opt.lsbcc_path)
                if not conf.env.LSBCC:
                    conf.env.LSBCC = conf.find_program('/opt/lsb/bin/lsbcc',
                            mandatory=True)
        except:
            Utils.pprint('RED', 'LSB (Linux Standard Base) tools >= 4.0 are required.')
            Utils.pprint('RED', 'Try --no-lsb option if not interested in building LSB binary.')
            exit(2)
        # lsbcc as CC compiler
        conf.env.append_value('CCFLAGS', '--lsb-cc=%s' % conf.env.CC[0])
        conf.env.append_value('LINKFLAGS', '--lsb-cc=%s' % conf.env.CC[0])
        conf.env.CC = conf.env.LSBCC
        conf.env.LINK_CC = conf.env.LSBCC
        ## check LSBCC flags
        # --lsb-besteffort - binary will work on platforms without LSB stuff
        # --lsb-besteffort - available in LSB build tools >= 4.0
        conf.check_cc(ccflags='--lsb-besteffort',
                msg='Checking for LSB build tools >= 4.0',
                errmsg='LSB >= 4.0 is required', mandatory=True)
        conf.env.append_value('CCFLAGS', '--lsb-besteffort')
        conf.env.append_value('LINKFLAGS', '--lsb-besteffort')
        # binary compatibility with a specific LSB version
        # LSB 4.0 can generate binaries compatible with 3.0, 3.1, 3.2, 4.0
        # however because of using function 'mkdtemp', loader requires
        # using target version 4.0
        lsb_target_flag = '--lsb-target-version=%s' % opt.lsb_version
        conf.env.append_value('CCFLAGS', lsb_target_flag)
        conf.env.append_value('LINKFLAGS', lsb_target_flag)


    ### Defines, Includes

    if myplatform.startswith('lin'):
        # make sure we don't use declarations after statement (break Visual Studio)
        conf.env.append_value('CCFLAGS', '-Wdeclaration-after-statement')
        conf.env.append_value('CCFLAGS', '-Werror')

    if myplatform.startswith('win'):
        conf.env.append_value('CCDEFINES', 'WIN32')
        conf.env.append_value('CPPPATH', '../zlib')

    conf.env.append_value('CPPPATH', '../common')


    ### Libraries

    if myplatform.startswith('win'):
        conf.check_cc(lib='user32', mandatory=True)
        conf.check_cc(lib='comctl32', mandatory=True)
        conf.check_cc(lib='kernel32', mandatory=True)
        conf.check_cc(lib='ws2_32', mandatory=True)

    else:
        conf.check_cc(lib='z', mandatory=True)
        if conf.check_cc(function_name='readlink', header_name='unistd.h'):
            conf.env.append_value('CCFLAGS', '-DHAVE_READLINK')


    ### ccflags

    if myplatform.startswith('win') and conf.env.CC_NAME == 'gcc':
        # Disables console - MinGW option
        conf.check_cc(ccflags='-mwindows', mandatory=True,
                msg='Checking for flags -mwindows')
        # Use Visual C++ compatible alignment
        conf.check_cc(ccflags='-mms-bitfields', mandatory=True,
                msg='Checking for flags -mms-bitfields')
        conf.env.append_value('CCFLAGS', '-mms-bitfields')

    elif myplatform.startswith('win') and conf.env.CC_NAME == 'msvc':
        if architecture == '32bit':
            conf.env.append_value('LINKFLAGS', '/MACHINE:X86')
        elif architecture == '64bit':
            conf.env.append_value('LINKFLAGS', '/MACHINE:X64')
            # enable 64bit porting warnings
            conf.env.append_value('CCFLAGS', '/Wp64')
        # We use SEH exceptions in winmain.c; make sure they are activated.
        conf.env.append_value('CCFLAGS', '/EHa')

    # link only with needed libraries
    # XXX: -Wl,--as-needed detected during configure but fails during mac build
    if conf.check_cc(ccflags='-Wl,--as-needed',
            msg='Checking for flags -Wl,--as-needed') \
            and not myplatform.startswith('darwin'):
        conf.env.append_value('LINKFLAGS', '-Wl,--as-needed')


    ### Other stuff

    if myplatform.startswith('win'):
        # RC file - icon
        conf.check_tool('winres')

    ### DEBUG and RELEASE environments

    rel = conf.env.copy()
    dbg = conf.env.copy()
    rel.set_variant('release') # separate subfolder for building
    dbg.set_variant('debug') # separate subfolder for building
    rel.detach() # detach environment from default
    dbg.detach()

    ## setup DEBUG environment
    dbg.set_variant('debug') # separate subfolder for building
    conf.set_env_name('debug', dbg)
    conf.setenv('debug')
    conf.env.append_value('CCDEFINES', '_DEBUG LAUNCH_DEBUG'.split())
    conf.env.append_value('CCFLAGS', conf.env.CCFLAGS_DEBUG)
    dbgw = conf.env.copy() # WINDOWED DEBUG environment
    dbgw.set_variant('debugw') # separate subfolder for building
    dbgw.detach()

    ## setup windowed DEBUG environment
    conf.set_env_name('debugw', dbgw)
    conf.setenv('debugw')
    if myplatform.startswith('darwin') or myplatform.startswith('win'):
        conf.env.append_value('CCDEFINES', 'WINDOWED')
    # disables console - mingw option
    if myplatform.startswith('win') and conf.env.CC_NAME == 'gcc':
            conf.env.append_value('LINKFLAGS', '-mwindows')
    elif myplatform.startswith('darwin'):
        conf.env.append_value('CCFLAGS', '-I/Developer/Headers/FlatCarbon')
        conf.env.append_value('LINKFLAGS', '-framework')
        conf.env.append_value('LINKFLAGS', 'Carbon')


    ## setup RELEASE environment
    conf.set_env_name('release', rel)
    conf.setenv('release')
    conf.env.append_value('CCDEFINES', 'NDEBUG')
    conf.env.append_value('CCFLAGS', conf.env.CCFLAGS_RELEASE)
    relw = conf.env.copy() # WINDOWED RELEASE environment
    relw.set_variant('releasew') # separate subfolder for building
    relw.detach()

    ## setup windowed RELEASE environment
    conf.set_env_name('releasew', relw)
    conf.setenv('releasew')
    if myplatform.startswith('darwin') or myplatform.startswith('win'):
        conf.env.append_value('CCDEFINES', 'WINDOWED')
    # disables console
    if myplatform.startswith('win') and conf.env.CC_NAME == 'gcc':
            conf.env.append_value('LINKFLAGS', '-mwindows')
    elif myplatform.startswith('darwin'):
        conf.env.append_value('CCFLAGS', '-I/Developer/Headers/FlatCarbon')
        conf.env.append_value('LINKFLAGS', '-framework')
        conf.env.append_value('LINKFLAGS', 'Carbon')


def build(bld):
    # Force to run with 1 job (no parallel building).
    # There are reported build failures on multicore CPUs
    # with some msvc versions.
    Options.options.jobs = 1

    myplatform = Utils.detect_platform()
    install_path = '../../support/loader/' + platform.system() + "-" + platform.architecture()[0]
    targets = dict(release='run', debug='run_d', releasew='runw', debugw='runw_d')

    if myplatform.startswith('win'):

        # static lib zlib

        for key in targets.keys():
            bld(
                features = 'cc cstaticlib',
                source = bld.path.ant_glob('zlib/*.c'),
                target = 'staticlib_zlib',
                env = bld.env_of_name(key),
            )

        # console

        for key in ('release', 'debug'):
            bld(
                features = 'cc cprogram pyembed',
                source = bld.path.ant_glob('windows/winmain.c windows/run.rc common/*.c'),
                target = targets[key],
                install_path = install_path,
                uselib_local = 'staticlib_zlib',
                uselib = 'USER32 COMCTL32 KERNEL32 WS2_32',
                env = bld.env_of_name(key).copy(),
            )

        # windowed

        for key in ('releasew', 'debugw'):
            bld(
                features = 'cc cprogram pyembed',
                source = bld.path.ant_glob('windows/winmain.c windows/runw.rc common/*.c'), # uses different RC file (icon)
                target = targets[key],
                install_path = install_path,
                uselib_local = 'staticlib_zlib',
                uselib = 'USER32 COMCTL32 KERNEL32 WS2_32',
                env = bld.env_of_name(key).copy(),
            )

        ## inprocsrvr console

        if bld.env.CC_NAME == 'msvc':
            linkflags_c = bld.env.CPPFLAGS_CONSOLE
            linkflags_w = bld.env.CPPFLAGS_WINDOWS
        else:
            linkflags_c = ''
            linkflags_w = ''

        for key, value in dict(release='inprocsrvr', debug='inprocsrvr_d').items():
            bld(
                features = 'cc cshlib',
                source = bld.path.ant_glob('common/launch.c windows/dllmain.c'),
                target = value,
                install_path = install_path,
                uselib_local = 'staticlib_zlib',
                uselib = 'USER32 COMCTL32 KERNEL32 WS2_32',
                env = bld.env_of_name(key).copy(),
                linkflags = linkflags_c
            )

        ## inprocsrvr windowed

        for key, value in dict(releasew='inprocsrvrw', debugw='inprocsrvrw_d').items():
            bld(
                features = 'cc cshlib',
                source = bld.path.ant_glob('common/launch.c windows/dllmain.c'),
                target = value,
                install_path = install_path,
                uselib_local = 'staticlib_zlib',
                uselib = 'USER32 COMCTL32 KERNEL32 WS2_32',
                env = bld.env_of_name(key).copy(),
                linkflags = linkflags_w
            )

    else: # linux, darwin (MacOSX)

        for key, value in targets.items():
            bld(
                features = 'cc cprogram pyembed',
                source = bld.path.ant_glob('linux/*.c common/*.c'),
                target = value,
                install_path = install_path,
                uselib = 'Z', # zlib
                env = bld.env_of_name(key).copy(),
            )
