# 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 string
import subprocess
import re


# JellyBean is API level 16
min_api_level = 16
# Minimal required NDK version
min_ndk_version = "r9b"
# NDK should be equal to or lower as min_api_level
ndk_level = min_api_level

Import('env')

env['ANDROID_STL'] = 'gnustl_static'

# android specific vars
vars = Variables()
vars.Add(PathVariable('ANDROID_SRC', 'Base directory of Android source tree', os.environ.get('ANDROID_SRC')))
vars.Add(PathVariable('ANDROID_NDK', 'Base directory of Android NDK', os.environ.get('ANDROID_NDK')))
vars.Add(PathVariable('ANDROID_SDK', 'Base directory of Android SDK', os.environ.get('ANDROID_SDK')))
vars.Add('ANDROID_TARGET', 'Target to be used for the build', os.environ.get('ANDROID_TARGET'))

# java 6 related
vars.Add(PathVariable('JAVA6_BOOT', 'Base directory for Java 6 bootclass', os.environ.get('JAVA6_BOOT')))

vars.Update(env)
Help(vars.GenerateHelpText(env))


# Verify dependencies
if env.get('ANDROID_NDK', '') == '':
   print 'ANDROID_NDK variable is required'
   if not GetOption('help'):
      Exit(1)

if env.get('ANDROID_SRC', '') == '' and env['CRYPTO'] == 'openssl':
   print 'ANDROID_SRC variable is required'
   if not GetOption('help'):
      Exit(1)

if env.get('ANDROID_TARGET', '') == '':
   env['ANDROID_TARGET'] = 'generic'

if env.get('ANDROID_SDK', '') != '':
   # Get the list of supported SDK platforms.
   # Use the latest one as compileSdkVersion (to define which android.jar to use).
   # It should be pretty safe always to compile with the latest one.
   # This should not be done for minSdkVersion or targetSdkVersion!
   api_levels = [ level
                  for level in set([ int(d.split('-')[-1]) for d in os.listdir(env['ANDROID_SDK'] + '/platforms') ])
                  if level >= min_api_level ]

   api_levels.sort()

   vars2 = Variables()
   vars2.Add('ANDROID_API_LEVEL', 'Android API level', os.environ.get('ANDROID_API_LEVEL', str(api_levels[-1])))
   vars2.Update(env)
   Help(vars2.GenerateHelpText(env))

#take ndk_level same as SDK level
ndk_platform_path = env['ANDROID_NDK'] + '/platforms/android-' + str(ndk_level)
if not os.path.exists(ndk_platform_path) and not GetOption('help'):
    print "Android NDK level not found"
    Exit(1)

# Read NDK RELEASE.TXT and check if the version is ok
ndk_release = ''
with open(env['ANDROID_NDK'] + '/RELEASE.TXT', 'r') as f:
    ndk_release = f.read()
f.closed
ndk_version_required = re.match("^r(\d+)([a-z])", min_ndk_version)
ndk_version_found = re.match("^r(\d+)([a-z])", ndk_release)
if ndk_version_found == None or ndk_version_required == None:
    print "WARNING: can't parse NDK release version"
else:
   # check if version is higher or equal to the required version.
   if (int(ndk_version_found.group(1)) < int(ndk_version_required.group(1))
      or (int(ndk_version_found.group(1)) == int(ndk_version_required.group(1)) and ord(ndk_version_found.group(2)) < ord(ndk_version_required.group(2)))):
      if not GetOption('help'):
         print "ERROR: This Android NDK is too old. Minimal required: \"" + min_ndk_version + "\", found \"" + ndk_version_found.group(0) + "\""
         Exit(1)

# Override MSVC build settings when building on windows.
if 'win32' == env.subst('$HOST_OS'):
   env['OBJPREFIX']      = ''
   env['OBJSUFFIX']      = '.o'
   env['SHOBJPREFIX']    = '$OBJPREFIX'
   env['SHOBJSUFFIX']    = '.os'         # SCons uses ".os" for shared object files on Linux
   env['PROGPREFIX']     = ''
   env['PROGSUFFIX']     = ''
   env['LIBPREFIX']      = 'lib'
   env['LIBSUFFIX']      = '.a'
   env['SHLIBPREFIX']    = '$LIBPREFIX'
   env['SHLIBSUFFIX']    = '.so'
   env['LIBPREFIXES']    = [ '$LIBPREFIX' ]
   env['LIBSUFFIXES']    = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]

if not GetOption('help') and env.has_key('ANDROID_NDK'):
   abi_map = { 'arm': 'armeabi',
               'mips': 'mips',
               'x86': 'x86' }
   curdir = env.Dir('.').srcnode()


   # Do a "test build" using ndk-build on our special Android.mk file to get
   # the command line options used by Android when building code.
   tb = subprocess.Popen([ env.subst('$ANDROID_NDK/ndk-build'),
                           'APP_BUILD_SCRIPT=%s/Android.mk' % curdir,
                           'NDK_PROJECT_PATH=%s' % curdir,
                           'APP_ABI=%s' % abi_map[env['CPU']],
                           'APP_STL=%s' % env['ANDROID_STL'],
                           'APP_PLATFORM=android-%s' % str(ndk_level),
                           '-n' ],
                         stdout = subprocess.PIPE)
   stdoutdata, stderrdata = tb.communicate()

   # Message the output from ndk-build into a dictionary.
   settings = dict( [ map(string.strip, line[4:].split('=', 1))
                      for line in stdoutdata.split('\n')
                      if line.startswith('AJ> ') ] )

   # Some versions of the Android NDK leave out
   # .../gnu-libstdc++/<gcc-version>/include/backward include path so we need
   # to add it if it is missing.  Also need to figure out where the STL
   # library lives.
   stl_lib_path = ''
   inc_paths = map(string.strip, settings['INCLUDES'].split())
   for ip in inc_paths:
      bpath = ip + '/backward'
      if bpath not in inc_paths and os.path.exists(bpath):
         # Get the "backward" include path
         inc_paths.append(bpath)

      if '/libs/' in ip:
         # Get the STL library path
         stl_lib_path = os.path.split(ip)[0]

   path = settings['TOOLCHAIN_PREBUILT_ROOT'] + '/bin'
   prefix = settings['TOOLCHAIN_PREFIX'][len(path) + 1:]

   env['AR'] = prefix + 'ar'
   env['CC'] = prefix + 'gcc'
   env['CXX'] = prefix + 'g++'
   env['LINK'] = prefix + 'gcc'
   env['RANLIB'] = prefix + 'ranlib'

   env.PrependENVPath('PATH', path)
   env.Append(CPPPATH = inc_paths)
   env.AppendUnique(CFLAGS = settings['CFLAGS'].split())
   env.AppendUnique(CXXFLAGS = settings['CXXFLAGS'].split())
   env.AppendUnique(LINKFLAGS = settings['LDFLAGS'].split())
   vflags = [f for f in settings[env['VARIANT'] + '_FLAGS'].split() if (f != '-g' and
                                                                        f != '-DNDEBUG' and
                                                                        f != '-UNDEBUG' and
                                                                        not f.startswith('-O'))]
   env.MergeFlags(vflags)
   env.Append(LIBPATH = [ settings['SYSROOT'] + '/usr/lib',
                          stl_lib_path ])
   env.Append(LINKFLAGS = [ '--sysroot=' + settings['SYSROOT'],
                            '-Wl,-rpath-link=' + settings['SYSROOT'] + '/usr/lib' ])

   if env['CRYPTO'] == 'openssl':
      # Get OpenSSL from Android source.
      env.Append(CPPPATH = '$ANDROID_SRC/external/openssl/include')
      env.Append(LIBPATH = '$ANDROID_SRC/out/target/product/$ANDROID_TARGET/system/lib')

   env.Append(CPPDEFINES = 'QCC_OS_ANDROID')
   env.Append(CFLAGS = ['-Wall',
                       '-pipe',
                       '-Wno-long-long',
                       '-Wno-deprecated',
                       '-Wno-unknown-pragmas',
                       '-pie',
                       '-fPIE'])
   env.Append(CXXFLAGS = ['-Wall',
                       '-pipe',
                       '-std=gnu++0x',
                       '-Wno-long-long',
                       '-Wno-deprecated',
                       '-Wno-unknown-pragmas',
                       '-pie',
                       '-fPIE'])


   env.Append(LINKFLAGS = ['-Wl,--gc-sections', '-Wl,-z,nocopyreloc', '-pie', '-fPIE'])

   # Java 6 compile options
   java6Flags = '-source 1.6 -target 1.6 '

   # bootclasspath for java 6
   java6BootClassOption = '-bootclasspath '

   java6BootPath = env.get('JAVA6_BOOT', '')

   # Use default java6 bootclass path if JAVA6_BOOT not set
   if java6BootPath == '':
    java6BootPath = '/usr/lib/jvm/java-6-sun/jre/lib/'
   elif not java6BootPath.endswith('/'):
    java6BootPath = java6BootPath + '/'

   java6BootClassJar = 'rt.jar'

   # Add bootclasspath compile option if java6 rt.jar exist
   if FindFile(java6BootClassJar, java6BootPath):
    java6Boot = java6BootClassOption + java6BootPath + java6BootClassJar
    java6Flags = java6Flags + java6Boot
   
   # Debug/Release variants
   if env['VARIANT'] == 'debug':
      env.Append(CFLAGS = ['-O0', '-g'])
      env.Append(CXXFLAGS = ['-O0', '-g'])
      env.Append(JAVACFLAGS='-g -Xlint -Xlint:-serial ' + java6Flags)
   else:
      env.Append(CFLAGS = '-Os')
      env.Append(CXXFLAGS = '-Os')
      env.Append(LINKFLAGS='-s')
      env.Append(JAVACFLAGS='-Xlint -Xlint:-serial ' + java6Flags)

   env.SConscript('${CPU}/SConscript')
