# Copyright AllSeen Alliance. All rights reserved.
#
#    Permission to use, copy, modify, and/or distribute this software for any
#    purpose with or without fee is hereby granted, provided that the above
#    copyright notice and this permission notice appear in all copies.
#
#    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
#    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
#    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
#    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
#    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
#    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
#    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# 

import os
import platform

def CheckCXXFlag(context, flag):
   prog = "int main(void) { return 0; }"
   context.Message('Checking c++ compiler support for %s flag... ' % flag)
   prevCXXFLAGS = context.env.get('CXXFLAGS')

   prefix = flag.split('=')[0]
   checkCXXFLAGS = [x for x in prevCXXFLAGS if not x.startswith(prefix)]
   checkCXXFLAGS.append(flag)

   context.env.Replace(CXXFLAGS = checkCXXFLAGS)
   r = context.TryCompile(prog, '.cc')
   if not r:
      context.env.Replace(CXXFLAGS = prevCXXFLAGS)
   context.Result(r)
   return r


if platform.system() == 'Linux':
    default_target_os = 'linux'
    allowed_target_oss = ('linux', 'android', 'openwrt')

    if platform.machine() == 'x86_64':
        default_target_cpu = 'x86_64'
    else:
        default_target_cpu = 'x86'
    allowed_target_cpus = ('x86', 'x86_64', 'arm', 'openwrt')

    default_msvc_version = None

    default_crypto = 'openssl' # Override for OS=android is below
    allowed_crypto = ('openssl', 'builtin')

elif platform.system() == 'Windows':
    default_target_os = 'win7'
    allowed_target_oss = ('win7', 'win10', 'android')

    if platform.machine() == 'x86_64':
        default_target_cpu = 'x86_64'
    else:
        default_target_cpu = 'x86'
    allowed_target_cpus = ('x86', 'x86_64', 'arm')

    default_msvc_version = '12.0'

    default_crypto = 'cng'
    allowed_crypto = ('cng', 'openssl', 'builtin')

elif platform.system() == 'Darwin':
    default_target_os = 'darwin'
    allowed_target_oss = ('darwin', 'android', 'openwrt')

    # Darwin maps both 32-bit and 64-bit x86 CPUs to "x86"
    default_target_cpu = 'x86'
    allowed_target_cpus = ('x86', 'arm', 'armv7', 'armv7s', 'arm64', 'openwrt')

    default_msvc_version = None

    default_crypto = 'openssl'
    allowed_crypto = ('openssl', 'builtin')

vars = Variables()

# Common build variables
vars.Add('V', 'Build verbosity', '0')
vars.Add(EnumVariable('OS', 'Target OS', default_target_os, allowed_values = allowed_target_oss))
vars.Add(EnumVariable('CPU', 'Target CPU', default_target_cpu, allowed_values = allowed_target_cpus))
vars.Add(EnumVariable('VARIANT', 'Build variant', 'debug', allowed_values=('debug', 'release'), ignorecase=2))
vars.Add(EnumVariable('BR', 'Have bundled router built-in for C++ test samples', 'on', allowed_values=('on', 'off')))
vars.Add(EnumVariable('DOCS', '''Output doc type. Setting the doc type to "dev" will produce HTML 
    output that includes all developer files not just the public API.
    ''', 'none', allowed_values=('none', 'pdf', 'html', 'dev', 'chm', 'sandcastle')))
vars.Add(EnumVariable('WS', 'Whitespace Policy Checker', 'off', allowed_values=('check', 'detail', 'fix', 'off')))
vars.Add(PathVariable('GTEST_DIR', 'The path to Google Test (gTest) source code',  os.environ.get('GTEST_DIR'), PathVariable.PathIsDir))
vars.Add(EnumVariable('NDEBUG', 'Override NDEBUG default for release variant', 'defined', allowed_values=('defined', 'undefined')))
vars.Add(PathVariable('SQLITE_DIR', 'The path to sqlite3.c and sqlite3.h, for building the Security Manager sample app',  os.environ.get('SQLITE_DIR'), PathVariable.PathIsDir))
vars.Add('CXX', 'C++ compiler to use')


if default_msvc_version:
    vars.Add(EnumVariable('MSVC_VERSION', 'MSVC compiler version - Windows', default_msvc_version, allowed_values=('11.0', '11.0Exp', '12.0', '12.0Exp', '14.0', '14.0Exp')))


# Standard variant directories
build_dir = 'build/${OS}/${CPU}/${VARIANT}'
vars.AddVariables(('OBJDIR', '', '#' + build_dir + '/obj'),
                  ('DISTDIR', '', '#' + build_dir + '/dist'),
                  ('TESTDIR', '', '#' + build_dir + '/test'))

target_os = ARGUMENTS.get('OS', default_target_os)
target_cpu = ARGUMENTS.get('CPU', default_target_cpu)

if target_os == 'android':
    default_crypto = 'builtin'
if target_os == 'darwin' and target_cpu in ['x86', 'x86_64']:
    default_crypto = 'builtin'

vars.Add(EnumVariable('CRYPTO', 'Crypto implementation', default_crypto, allowed_values = allowed_crypto))

if target_os in ['win10', 'win7']:
    # The Win7/Win10 MSI installation package takes a long time to build. Let it be optional.
    if target_os in ['win10', 'win7']:
        vars.Add(EnumVariable('WIN7_MSI', 'Build the .MSI installation package', 'false', allowed_values=('false', 'true')))

    path = []
    if os.environ.has_key('MIKTEX_HOME'):
        path = os.path.normpath(os.environ['MIKTEX_HOME'] + '/bin')

    msvc_version = ARGUMENTS.get('MSVC_VERSION')
    env = Environment(variables = vars, TARGET_ARCH=target_cpu, MSVC_VERSION=msvc_version, tools = ['default', 'jar'], ENV = {'PATH' : path})

else:
    env = Environment(variables = vars, tools = ['gnulink', 'gcc', 'g++', 'ar', 'as', 'javac', 'javah', 'jar', 'pdf', 'pdflatex'])

if env['V'] == '0':
    env.Replace(CCCOMSTR =     '\t[CC]      $SOURCE',
                SHCCCOMSTR =   '\t[CC-SH]   $SOURCE',
                CXXCOMSTR =    '\t[CXX]     $SOURCE',
                SHCXXCOMSTR =  '\t[CXX-SH]  $SOURCE',
                LINKCOMSTR =   '\t[LINK]    $TARGET',
                SHLINKCOMSTR = '\t[LINK-SH] $TARGET',
                JAVACCOMSTR =  '\t[JAVAC]   $SOURCE',
                JARCOMSTR =    '\t[JAR]     $TARGET',
                ARCOMSTR =     '\t[AR]      $TARGET',
                RANLIBCOMSTR = '\t[RANLIB]  $TARGET'
                )

# Some tools aren't in default path
if os.environ.has_key('JAVA_HOME'):
    env.PrependENVPath('PATH', os.path.normpath(os.environ['JAVA_HOME'] + '/bin'))
if os.environ.has_key('DOXYGEN_HOME'):
    env.PrependENVPath('PATH', os.path.normpath(os.environ['DOXYGEN_HOME'] + '/bin'))
if os.environ.has_key('GRAPHVIZ_HOME'):
    env.PrependENVPath('PATH', os.path.normpath(os.environ['GRAPHVIZ_HOME'] + '/bin'))

# Warn user about building stand alone daemon on unsupported platforms
if env['OS'] not in ['android', 'linux', 'openwrt'] and env['BR'] != "on":
    print "Daemon is not supported on OS=%s, building with BR=on anyway" % (env['OS'])
    env['BR'] = "on"


Help(vars.GenerateHelpText(env))


# Don't silently break people still building with BD in place of BR.  We're adding the variable here
# after generating the help text above so that it does not show up in the help.
bd = Variables();
bd.Add(EnumVariable('BD', 'Have bundled router built-in for C++ test samples', 'invalid', allowed_values=('invalid', 'on', 'off')))
bd.Update(env)

if 'invalid' != env['BD']:
    print 'BD has been replaced by BR, setting BR to ' + env['BD']
    env['BR'] = env['BD']

# Validate build vars
if env['OS'] == 'linux':
    env['OS_GROUP'] = 'posix'
    env['OS_CONF'] = 'linux'

elif env['OS'] in ['win10', 'win7']:
    env['OS_GROUP'] = 'windows'
    env['OS_CONF'] = 'windows'

elif env['OS'] == 'android':
    env['OS_GROUP'] = 'posix'
    env['OS_CONF'] = 'android'

elif env['OS'] == 'darwin':
    env['OS_GROUP'] = 'posix'
    env['OS_CONF'] = 'darwin'

elif env['OS'] == 'openwrt':
    env['OS_GROUP'] = 'posix'
    env['OS_CONF'] = 'openwrt'

else:
    print 'Unsupported OS/CPU combination'
    if not GetOption('help'):
        Exit(1)

if env['VARIANT'] == 'release' and env['NDEBUG'] == 'defined':
    env.Append(CPPDEFINES = 'NDEBUG')

if env['BR'] == 'on':
    env.Append(CPPDEFINES = 'ROUTER')

env.Append(CPPDEFINES = ['QCC_OS_GROUP_%s' % env['OS_GROUP'].upper()])

# Setup additional builders
if os.path.exists('tools/scons/doxygen.py'):
    env.Tool('doxygen', toolpath=['tools/scons'])
else:
    def dummy_emitter(target, source, env):
        return [], []
    def dummy_action(target, source, env):
        pass
    dummyBuilder = Builder(action = dummy_action,
                           emitter = dummy_emitter);
    env.Append(BUILDERS = {'Doxygen' : dummyBuilder})
env.Tool('genversion', toolpath=['tools/scons'])
env.Tool('javadoc', toolpath=['tools/scons'])
env.Tool('Csharp', toolpath=['tools/scons'])
env.Tool('jsdoc3', toolpath=['tools/scons'])

# Create the builder that generates Status.h from Status.xml
import sys
import SCons.Util
sys.path.append('tools/bin')
import make_status
def status_emitter(target, source, env):
    base = SCons.Util.splitext(str(target[0]))[0]
    target.append(base + '.h')
    return target, source

def status_action(target, source, env):
    base = os.path.dirname(os.path.dirname(SCons.Util.splitext(str(target[1]))[0]))
    cmdList = []
    cmdList.append('--code=%s' % str(target[0]))
    cmdList.append('--header=%s' % str(target[1]))
    if env.has_key('STATUS_FLAGS'):
        cmdList.extend(env['STATUS_FLAGS'])
    cmdList.append(str(source[0]))
    return make_status.main(cmdList)

statusBuilder = Builder(action = status_action,
                        emitter = status_emitter,
                        suffix = '.cc',
                        src_suffix = '.xml')
env.Append(BUILDERS = {'Status' : statusBuilder})

if env['OS'] in ['linux', 'openwrt']:
    env['LIBTYPE'] = 'both'
else:
    env['LIBTYPE'] = 'static'

# Read OS and CPU specific SConscript file
Export('env', 'CheckCXXFlag')
env.SConscript('conf/${OS_CONF}/SConscript')

# Whitespace policy
if env['WS'] != 'off' and not env.GetOption('clean'):
    import sys
    sys.path.append('tools/bin')
    import whitespace

    def wsbuild(target, source, env):
        print "Evaluating whitespace compliance..."
        curdir = os.path.abspath(os.path.dirname(wsbuild.func_code.co_filename))
        version = whitespace.get_uncrustify_version()
        if (version == "0.57"):
            config = os.path.join(curdir, 'tools', 'conf', 'ajuncrustify.0.57.cfg')
        else: #use latest known version
            config = os.path.join(curdir, 'tools', 'conf', 'ajuncrustify.0.61.cfg')
        print "Config:", config
        print "Note: enter 'scons -h' to see whitespace (WS) options"
        return whitespace.main([env['WS'], config])

    ws = env.Command('#/ws', Dir('$DISTDIR'), wsbuild)
if env['WS'] != 'off':
    env.Clean(env.File('SConscript'), env.File('#/whitespace.db'))

Return('env')
