#
# Waf script for the ION BPv7 RTEMS port.
# Supported BSPs (verified under QEMU):
#   * arm/xilinx-zynq-a9     (32-bit ARM)
#   * aarch64/a53_lp64_qemu  (64-bit ARM, ARMv8-A virt machine)
#   * sparc/leon3            (Gaisler LEON3, SPARC V8)
#
import os
import sys

rtems_version = os.environ.get("RTEMS_VERSION", "6")

try:
    from rtems_waf import rtems
except ImportError:
    print("error: no rtems_waf git submodule")
    sys.exit(1)


def init(ctx):
    rtems.init(ctx, version=rtems_version, long_commands=True)


def bsp_configure(conf, arch_bsp):
    # Add BSP specific configuration checks
    pass


def options(opt):
    rtems.options(opt)


def configure(conf):
    rtems.configure(conf, bsp_configure=bsp_configure)
    conf.define('CSI_SUITE_NAME', 'NULL Suites')
    conf.write_config_header('config.h', top=True)


def build(bld):
    # note: 64 bit riscv is also only named "riscv"
    arch64 = {'aarch64', 'x86_64', 'riscv64', 'powerpc64', 'riscv'}
    _arch = bld.env.RTEMS_ARCH
    if isinstance(_arch, list):
        _arch = _arch[0] if _arch else ''
    space_order = 3 if _arch in arch64 else 2
    is_64bit = _arch in arch64
    long_long_okay = 1

    # Excluding non-core parts for minimal RTEMS configuration
    # Set to 1 to enable these convergence layers
    # ! Note: libbsd takes ~1.6MB / ION minimal ~1.0MB / Kernel etc take 0.3MB ROM
    # note about ROM usage addded by each added module:
    # dgr ~ no added ROM usage
    # tcpcl ~ 40KiB
    # ams ~ core library + amsd daemon (RAMS/test/utils excluded)

    ENABLE_DGR = 1
    ENABLE_TCPCL = 1
    ENABLE_AMS = 1

    # AMS requires DGR: the test MIB uses DGR as primary transport service.
    if ENABLE_AMS == 1 and ENABLE_DGR == 0:
        bld.fatal('ENABLE_AMS requires ENABLE_DGR (test MIB primary pts=dgr)')

    iondefines = [
        'HAVE_CONFIG_H',
        'RTEMS',
        'BP_EXTENDED',
        'CRYPTO',
        'ENABLE_IMC',
        'GDSSYMTAB',
        'GDSLOGGER',
        'USING_SDR_POINTERS',
        'NO_SDR_TRACE',
        'NO_PSM_TRACE',
        'NO_PROXY',
        'NO_DIRLIST',
        'UDPTS',
        'TCPTS',
        'DGRTS',
        'SPACE_ORDER=%d' % space_order,
        'LONG_LONG_OKAY=%d' % long_long_okay,
        'PRIVATE_SYMTAB',
        'USING_RTEMS_LIBBSD',
        # AMS over DGR
        # AMS with the DGR adds a lot of threads to the build
        # without AMS this is not necessary and MAX_SPAWNS can be 32
        'MAX_SPAWNS=64',
        'MAX_POSIX_TASKS=100',
    ]

    # Add conditional defines for DGR
    if ENABLE_DGR == 1:
        iondefines += ['ENABLE_DGR']

    if ENABLE_TCPCL == 1:
        iondefines += ['ENABLE_TCPCL']

    if ENABLE_AMS == 1:
        iondefines += ['ENABLE_AMS']

    # Not including the following
    # -DION_NO_DNS
    # Not building with nm
    # -DAGENT_ROLE

    # -Wno-error=shift-count-overflow: libbp.c parseEidString shifts an
    # `unsigned long` by 32; harmless on LP64 (aarch64) but on 32-bit
    # SPARC the shift width equals the type width, which GCC flags.
    # -Wno-error=overflow: cbr.h compile-time constants assume 64-bit
    # uvast and overflow when uvast is downgraded to `long` on 32-bit.
    # Demoting these to warnings keeps -Werror useful elsewhere.
    ionflags = (
        '-g -Wall -Werror -Wno-unused-variable'
        ' -Wno-error=shift-count-overflow'
        ' -Wno-error=overflow'
    )

    icisources = [
        'rfxclock.c',
        'ionadmin.c',
        'sdrmend.c',
        'ionunlock.c',
        'ionxnowner.c',
        'ionlog.c',
        'ionsecadmin.c',
        'crypto.c',
        'csi.c',
        'ionwarn.c',
        'ionrestart.c',
        'ionexit.c',
        'bpadmin.c',
        'bpsecadmin.c',
        'rtems_stubs.c',  # RTEMS pthread_kill and pause stubs
    ]

    iciinclude = [
        bld.bldnode.parent,
        '../ici/include',
        '../ici/library',
        '../ici/crypto',
        '../bpv7/library',
        '../ltp/library',
        '../ici/test',
        '../ici/sdr',
        '../ici/libbloom-master',
        '../ici/libbloom-master/murmur2',
    ]

    icilibsources = [
        'bulk.c',
        'cbor.c',
        'crc.c',
        'ion.c',
        'ion_admin.c',
        'ion_atomic.c',
        'ion_network.c',
        'ionsec.c',
        'libicinm.c',
        'llcv.c',
        'lyst.c',
        'memmgr.c',
        'platform.c',
        'platform_sm.c',
        'psm.c',
        'radix.c',
        'rfx.c',
        'smlist.c',
        'smrbt.c',
        'sptrace.c',
        'zco.c',
        'sdrcatlg.c',
        'sdrhash.c',
        'sdrxn.c',
        'sdrmgt.c',
        'sdrstring.c',
        'sdrlist.c',
        'sdrtable.c',
    ]

    ltpsource = [
        'ltpclock.c',
        'ltpdeliv.c',
        'ltpmeter.c',
        'udplsi.c',
        'udplso.c',
        'ltpadmin.c',
        'ltpsecadmin.c',
    ]

    ltpinclude = ['../ltp/include', '../ltp/library', '../ltp/library/ext']

    ltplibsources = [
        'libltp.c',
        'libltpnm.c',
        'libltpP.c',
        'ltpei.c',
        'ltpsec.c',
        'libsda.c',
        'libudplsa.c',
    ]

    # not included in port
    ltpudplsasources = []

    bpsources = [
        'bpclock.c',
        'bptransit.c',
        'bpclm.c',
        'bpstats.c',
        'bptrace.c',
        'bplist.c',
        'cpsd.c',
        'lgagent.c',
        'ipnadmin.c',
        'ipnfw.c',
        'cbdedup.c',
        'ipnadminep.c',
        'ltpcli.c',
        'ltpclo.c',
        'bibeadmin.c',
        'bibeclo.c',
        'imcadminep.c',
        'imcfw.c',
    ]

    # Conditionally add DGR convergence layer sources
    if ENABLE_DGR == 1:
        bpsources += ['dgrcli.c', 'dgrclo.c']

    # Conditionally add TCPCL convergence layer sources
    if ENABLE_TCPCL == 1:
        bpsources += ['tcpcli.c', 'tcpclo.c']

    bplibsources = [
        'bpsec_instr.c',
        'bpsec_policy.c',
        'bpsec_policy_eventset.c',
        'bpsec_policy_event.c',
        'bpsec_policy_rule.c',
        'ion_test_sc.c',
        'bib_hmac_sha2_sc.c',
        'bcb_aes_gcm_sc.c',
        'rfc9173_utils.c',
        'sc_util.c',
        'sci.c',
        'sci_valmap.c',
        'sc_value.c',
        'bpsec_util.c',
        'bpsec_asb.c',
        'bibe.c',
        'libbp.c',
        'libbpP.c',
        'libbpnm.c',
        'libimcfw.c',
        'imc.c',
        'eureka.c',
        'bei.c',
        'bae.c',
        'bpq.c',
        'bcb.c',
        'bib.c',
        'hcb.c',
        'meb.c',
        'pnb.c',
        'snw.c',
        'cbr.c',
        'cteb.c',
        'creb.c',
        'libipnfw.c',
        'saga.c',
        'bloom.c',
        'MurmurHash2.c',
        'libcgr.c',
    ]

    bpinclude = [
        './',
        '../bpv7/include',
        '../bpv7/bpsec/sci',
        '../bpv7/bpsec/utils',
        '../bpv7/bpsec/instr',
        '../bpv7/bpsec/policy',
        '../bpv7/library',
        '../bpv7/library/ext',
        '../bpv7/library/ext/meb',
        '../bpv7/library/ext/bae',
        '../bpv7/ipn',
        '../bpv7/ipnd',
        '../bpv7/library/crypto',
        '../bpv7/tcp',
        '../bpv7/udp',
        '../bpv7/library/ext/snw',
        '../bpv7/bibe',
        '../bpv7/imc',
        '../bpv7/saga',
        '../bpv7/dtn2',
        '../bpv7/library/ext/bpsec',
        '../bpv7/library/ext/hcb',
        '../bpv7/library/ext/pnb',
        '../bpv7/library/ext/bpq',
        '../bpv7/library/ext/imc',
        '../bpv7/utils',
    ]

    # nm and adm are not used, list not vetted
    nmsource = [
        'adm.c',
        'adm_agent.c',
        'adm_bp.c',
        'adm_sbsp.c',
        'adm_ltp.c',
        'expr.c',
        'blob.c',
        'report.c',
        'rules.c',
        'ctrl.c',
        'table.c',
        'ion_if.c',
        'utils.c',
        'db.c',
        'nm_types.c',
        'ingest.c',
        'instr.c',
        'lcc.c',
        'ldc.c',
        'rda.c',
        'msg.c',
        'agent_db.c',
        'adm_ion_priv.c',
        'adm_ltp_priv.c',
        'adm_agent_impl.c',
        'adm_bp_impl.c',
        'adm_sbsp_impl.c',
        'nmagent.c',
    ]

    bsssources = ['bsscounter.c', 'bssdriver.c']

    bsslibsources = ['libbss.c', 'libbssP.c']

    bssinclude = ['./', '../bss/include']

    cfdpsources = ['bputa.c', 'cfdpclock.c', 'cfdpadmin.c']

    cfdplibsources = ['libcfdp.c', 'libcfdpP.c', 'libcfdpops.c']

    cfdpinclude = ['./', '../cfdp/include', '../cfdp/library', '../cfdp/utils']

    amssources = [
        'amsd.c',
        'librams.c',
        'ramscommon.c',
        'ramsgate.c',
        'amsshell.c',
        'amslog.c',
        'amslogprt.c',
        'amsmib.c',
        'amsstop.c',
    ]
    # marshal.c is part of libams.c

    amslibsources = [
        'amscommon.c',
        'crypt.c',
        'secrypt.c',
        'dgrts.c',
        'libams.c',
        'loadmib.c',
        'tcpts.c',
        'udpts.c',
    ]

    # AMS require cryptographic stubs from the NULL_SUITES crypto engine
    amsinclude = [
        './',
        '../ams/include',
        '../ams/rams',
        '../ici/crypto/NULL_SUITES',
    ]

    dgrlibsources = ['libdgr.c']

    dgrinclude = ['./', '../dgr/include']

    testsources = ['bpsource.c', 'bpsink.c', 'bpcrash.c']

    # Minimal BP/LTP port - excluding AMS, BSS
    # DGR conditionally included based on the ENABLE_DGR flag
    csrcs = (
        ['ionrtems.c'] + icisources + ltpsource + bpsources + testsources + cfdpsources
    )

    # amsd is a daemon main (spawned via pseudoshell); the rest of AMS
    # lives in the 'ams' static library built below.
    if ENABLE_AMS == 1:
        csrcs += ['amsd.c']

    # Minimal BP/LTP port - excluding BSS
    # DGR conditionally included based on the ENABLE_DGR flag
    includes = ['./'] + iciinclude + ltpinclude + bpinclude + cfdpinclude

    # Conditionally add DGR include paths
    if ENABLE_DGR == 1:
        includes += dgrinclude

    if ENABLE_AMS == 1:
        includes += amsinclude

    rtems.build(bld)
    bld.stlib(
        target='ici',
        cflags=ionflags,
        source=icilibsources,
        includes=iciinclude,
        defines=iondefines,
        use=['m'],
    )

    bld.stlib(
        target='ltp',
        cflags=ionflags,
        source=ltplibsources,
        includes=ltpinclude + iciinclude,
        defines=iondefines,
        use=['ici', 'm'],
    )

    # Not building libudplsa.la, not used for this port

    # Conditionally build DGR library
    if ENABLE_DGR == 1:
        bld.stlib(
            target='dgr',
            cflags=ionflags,
            source=dgrlibsources,
            includes=dgrinclude + iciinclude,
            defines=iondefines,
            use=['ici', 'm'],
        )

    # Build BP library with conditional DGR dependency
    bp_includes = bpinclude + ltpinclude + iciinclude
    bp_use = ['ici', 'ltp', 'm']

    if ENABLE_DGR == 1:
        bp_includes += dgrinclude
        bp_use.insert(-1, 'dgr')  # Insert before 'm'

    bld.stlib(
        target='bp',
        cflags=ionflags,
        source=bplibsources,
        includes=bp_includes,
        defines=iondefines,
        use=bp_use,
    )

    # BSS library excluded for minimal port.
    # Core AMS library (libams + amscommon + loadmib + dgr/udp/tcp transport
    # services); RAMS, AMS test programs and AMS utils remain excluded.
    if ENABLE_AMS == 1:
        bld.stlib(
            target='ams',
            cflags=ionflags,
            source=amslibsources,
            includes=amsinclude + iciinclude + dgrinclude,
            defines=iondefines,
            use=['ici', 'dgr', 'm'],
        )

    bld.stlib(
        target='cfdp',
        cflags=ionflags,
        source=cfdplibsources,
        includes=iciinclude + bpinclude + cfdpinclude,
        defines=iondefines,
        use=['ici', 'bp', 'm'],
    )

    # Minimal BP/LTP port - only required libraries
    libuse = ['ici', 'bp', 'ltp', 'cfdp', 'm']

    # Conditionally add DGR to linked libraries
    if ENABLE_DGR == 1:
        libuse.insert(-1, 'dgr')  # Insert before 'm'

    if ENABLE_AMS == 1:
        libuse.insert(-1, 'ams')  # Insert before 'm'

    # Note: RTEMS pthread functions are in rtemscpu library

    bld(
        features='c cprogram',
        target='ion.exe',
        cflags=ionflags,
        source=csrcs,
        includes=includes,
        defines=iondefines,
        use=libuse,
        lib=[
            'rtemscpu',
            'rtemsbsp',
            'bsd',
            'm',
        ],  # RTEMS core, BSD networking, and Math libraries (order matters)
    )
