## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
import sys

from waflib import Options
import wutils

def options(opt):
    opt.add_option('--int64x64-as-double',
                   help=('Whether to use a double floating point'
                         ' type for int64x64 values'
                         ' WARNING: this option only has effect '
                         'with the configure command.'),
                   action="store_true", default=False,
                   dest='int64x64_as_double')
    opt.add_option('--disable-pthread',
                   help=('Whether to enable the use of POSIX threads'),
                   action="store_true", default=False,
                   dest='disable_pthread')



def configure(conf):
    a = conf.check_nonfatal(type_name='uint128_t', define_name='HAVE_UINT128_T')
    b = conf.check_nonfatal(type_name='__uint128_t', define_name='HAVE___UINT128_T')

    if Options.options.int64x64_as_double:
        conf.define('INT64X64_USE_DOUBLE', 1)
        conf.env['INT64X64_USE_DOUBLE'] = 1
        highprec = 'long double'
    elif a or b:
        conf.define('INT64X64_USE_128', 1)
        conf.env['INT64X64_USE_128'] = 1
        highprec = '128-bit integer'
    else:
        conf.define('INT64X64_USE_CAIRO', 1)
        conf.env['INT64X64_USE_CAIRO'] = 1
        highprec = 'cairo 128-bit integer'

    conf.msg('Checking high precision time implementation', highprec)

    conf.check_nonfatal(header_name='stdint.h', define_name='HAVE_STDINT_H')
    conf.check_nonfatal(header_name='inttypes.h', define_name='HAVE_INTTYPES_H')
    conf.check_nonfatal(header_name='sys/inttypes.h', define_name='HAVE_SYS_INT_TYPES_H')
    conf.check_nonfatal(header_name='sys/types.h', define_name='HAVE_SYS_TYPES_H')
    conf.check_nonfatal(header_name='sys/stat.h', define_name='HAVE_SYS_STAT_H')
    conf.check_nonfatal(header_name='dirent.h', define_name='HAVE_DIRENT_H')

    if conf.check_nonfatal(header_name='stdlib.h'):
        conf.define('HAVE_STDLIB_H', 1)
        conf.define('HAVE_GETENV', 1)

    conf.check_nonfatal(header_name='signal.h', define_name='HAVE_SIGNAL_H')

    # Check for POSIX threads
    test_env = conf.env.derive()
    if Options.platform != 'darwin' and Options.platform != 'cygwin':
        test_env.append_value('LINKFLAGS', '-pthread')
        test_env.append_value('CXXFLAGS', '-pthread')
        test_env.append_value('CCFLAGS', '-pthread')
    fragment = r"""
#include <pthread.h>
int main ()
{
   pthread_mutex_t m;
   pthread_mutex_init (&m, NULL);
   return 0;
}
"""
    if Options.options.disable_pthread:
        conf.report_optional_feature("Threading", "Threading Primitives",
                                     False,
                                     "Disabled by user request (--disable-pthread)")
        have_pthread = False
    else:
        have_pthread = conf.check_nonfatal(header_name='pthread.h', define_name='HAVE_PTHREAD_H',
                                           env=test_env, fragment=fragment,
                                           errmsg='Could not find pthread support (build/config.log for details)')
    if have_pthread:
        # darwin accepts -pthread but prints a warning saying it is ignored
        if Options.platform != 'darwin' and Options.platform != 'cygwin':
            conf.env['CXXFLAGS_PTHREAD'] = '-pthread'
            conf.env['CCFLAGS_PTHREAD'] = '-pthread'
            conf.env['LINKFLAGS_PTHREAD'] = '-pthread'

    conf.env['ENABLE_THREADING'] = have_pthread

    conf.report_optional_feature("Threading", "Threading Primitives",
                                 conf.env['ENABLE_THREADING'],
                                 "<pthread.h> include not detected")

    conf.check_nonfatal(header_name='stdint.h', define_name='HAVE_STDINT_H')
    conf.check_nonfatal(header_name='inttypes.h', define_name='HAVE_INTTYPES_H')

    conf.check_nonfatal(header_name='sys/inttypes.h', define_name='HAVE_SYS_INT_TYPES_H')

    if not conf.check_nonfatal(lib='rt', uselib='RT, PTHREAD', define_name='HAVE_RT'):
        conf.report_optional_feature("RealTime", "Real Time Simulator",
                                     False, "librt is not available")
    else:
        conf.report_optional_feature("RealTime", "Real Time Simulator",
                                     conf.env['ENABLE_THREADING'],
                                     "threading not enabled")
        conf.env["ENABLE_REAL_TIME"] = conf.env['ENABLE_THREADING']

    conf.write_config_header('ns3/core-config.h', top=True)

def build(bld):
    bld.install_files('${INCLUDEDIR}/%s%s/ns3' % (wutils.APPNAME, wutils.VERSION), '../../ns3/core-config.h')

    core = bld.create_ns3_module('core')
    core.source = [
        'model/time.cc',
        'model/event-id.cc',
        'model/scheduler.cc',
        'model/list-scheduler.cc',
        'model/map-scheduler.cc',
        'model/heap-scheduler.cc',
        'model/calendar-scheduler.cc',
        'model/event-impl.cc',
        'model/simulator.cc',
        'model/simulator-impl.cc',
        'model/default-simulator-impl.cc',
        'model/timer.cc',
        'model/watchdog.cc',
        'model/synchronizer.cc',
        'model/make-event.cc',
        'model/log.cc',
        'model/breakpoint.cc',
        'model/type-id.cc',
        'model/attribute-construction-list.cc',
        'model/object-base.cc',
        'model/ref-count-base.cc',
        'model/object.cc',
        'model/test.cc',
        'model/random-variable.cc',
        'model/random-variable-stream.cc',
        'model/rng-seed-manager.cc',
        'model/rng-stream.cc',
        'model/command-line.cc',
        'model/type-name.cc',
        'model/attribute.cc',
        'model/boolean.cc',
        'model/integer.cc',
        'model/uinteger.cc',
        'model/enum.cc',
        'model/double.cc',
        'model/int64x64.cc',
        'model/string.cc',
        'model/pointer.cc',
        'model/object-ptr-container.cc',
        'model/object-factory.cc',
        'model/global-value.cc',
        'model/trace-source-accessor.cc',
        'model/config.cc',
        'model/callback.cc',
        'model/names.cc',
        'model/vector.cc',
        'model/fatal-impl.cc',
        'model/system-path.cc',
        'helper/random-variable-stream-helper.cc',
        'helper/event-garbage-collector.cc',
        'model/hash-function.cc',
        'model/hash-murmur3.cc',
        'model/hash-fnv.cc',
        'model/hash.cc',
        ]

    core_test = bld.create_ns3_module_test_library('core')
    core_test.source = [
        'test/attribute-test-suite.cc',
        'test/callback-test-suite.cc',
        'test/command-line-test-suite.cc',
        'test/config-test-suite.cc',
        'test/global-value-test-suite.cc',
        'test/int64x64-test-suite.cc',
        'test/names-test-suite.cc',
        'test/object-test-suite.cc',
        'test/ptr-test-suite.cc',
        'test/random-variable-test-suite.cc',
        'test/event-garbage-collector-test-suite.cc',
        'test/many-uniform-random-variables-one-get-value-call-test-suite.cc',
        'test/one-uniform-random-variable-many-get-value-calls-test-suite.cc',
        'test/sample-test-suite.cc',
        'test/simulator-test-suite.cc',
        'test/time-test-suite.cc',
        'test/timer-test-suite.cc',
        'test/traced-callback-test-suite.cc',
        'test/type-traits-test-suite.cc',
        'test/watchdog-test-suite.cc',
        'test/hash-test-suite.cc',
        'test/type-id-test-suite.cc',
        ]

    headers = bld(features='ns3header')
    headers.module = 'core'
    headers.source = [
        'model/nstime.h',
        'model/event-id.h',
        'model/event-impl.h',
        'model/simulator.h',
        'model/simulator-impl.h',
        'model/default-simulator-impl.h',
        'model/scheduler.h',
        'model/list-scheduler.h',
        'model/map-scheduler.h',
        'model/heap-scheduler.h',
        'model/calendar-scheduler.h',
        'model/simulation-singleton.h',
        'model/singleton.h',
        'model/timer.h',
        'model/timer-impl.h',
        'model/watchdog.h',
        'model/synchronizer.h',
        'model/make-event.h',
        'model/system-wall-clock-ms.h',
        'model/empty.h',
        'model/callback.h',
        'model/object-base.h',
        'model/ref-count-base.h',
        'model/simple-ref-count.h',
        'model/type-id.h',
        'model/attribute-construction-list.h',
        'model/ptr.h',
        'model/object.h',
        'model/log.h',
        'model/assert.h',
        'model/breakpoint.h',
        'model/fatal-error.h',
        'model/test.h',
        'model/random-variable.h',
        'model/random-variable-stream.h',
        'model/rng-seed-manager.h',
        'model/rng-stream.h',
        'model/command-line.h',
        'model/type-name.h',
        'model/type-traits.h',
        'model/int-to-type.h',
        'model/attribute.h',
        'model/attribute-accessor-helper.h',
        'model/boolean.h',
        'model/int64x64.h',
        'model/int64x64-double.h',
        'model/integer.h',
        'model/uinteger.h',
        'model/double.h',
        'model/enum.h',
        'model/string.h',
        'model/pointer.h',
        'model/object-factory.h',
        'model/attribute-helper.h',
        'model/global-value.h',
        'model/traced-callback.h',
        'model/traced-value.h',
        'model/trace-source-accessor.h',
        'model/config.h',
        'model/object-ptr-container.h',
        'model/object-vector.h',
        'model/object-map.h',
        'model/deprecated.h',
        'model/abort.h',
        'model/names.h',
        'model/vector.h',
        'model/default-deleter.h',
        'model/fatal-impl.h',
        'model/system-path.h',
        'model/unused.h',
        'model/math.h',
        'helper/event-garbage-collector.h',
        'helper/random-variable-stream-helper.h',
        'model/hash-function.h',
        'model/hash-murmur3.h',
        'model/hash-fnv.h',
        'model/hash.h',
        ]

    if sys.platform == 'win32':
        core.source.extend([
            'model/win32-system-wall-clock-ms.cc',
            ])
    else:
        core.source.extend([
            'model/unix-system-wall-clock-ms.cc',
            ])


    env = bld.env
    if env['INT64X64_USE_DOUBLE']:
        headers.source.extend(['model/int64x64-double.h'])
    elif env['INT64X64_USE_128']:
        headers.source.extend(['model/int64x64-128.h'])
        core.source.extend(['model/int64x64-128.cc'])
    elif env['INT64X64_USE_CAIRO']:
        core.source.extend([
            'model/int64x64-cairo.cc',
            ])
        headers.source.extend([
            'model/int64x64-cairo.h',
            'model/cairo-wideint-private.h',
            ])

    if env['ENABLE_REAL_TIME']:
        headers.source.extend([
                'model/realtime-simulator-impl.h',
                'model/wall-clock-synchronizer.h',
                ])
        core.source.extend([
                'model/realtime-simulator-impl.cc',
                'model/wall-clock-synchronizer.cc',
                ])
        core.use.append('RT')
        core_test.use.append('RT')

    if env['ENABLE_THREADING']:
        core.source.extend([
            'model/system-thread.cc',
            'model/unix-fd-reader.cc',
            'model/unix-system-mutex.cc',
            'model/unix-system-condition.cc',
            ])
        core.use.append('PTHREAD')
        core_test.use.append('PTHREAD')
        core_test.source.extend(['test/threaded-test-suite.cc'])
        headers.source.extend([
                'model/unix-fd-reader.h',
                'model/system-mutex.h',
                'model/system-thread.h',
                'model/system-condition.h',
                ])

    if env['ENABLE_GSL']:
        core.use.extend(['GSL', 'GSLCBLAS', 'M'])
        core_test.use.extend(['GSL', 'GSLCBLAS', 'M'])
        core_test.source.extend(['test/rng-test-suite.cc'])

    if (bld.env['ENABLE_EXAMPLES']):
        bld.recurse('examples')

    pymod = bld.ns3_python_bindings()
    if pymod is not None:
        pymod.source += ['bindings/module_helpers.cc']
