#!/usr/bin/env python

import os

import locale
from collections import defaultdict
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')

header = """
#
#   #####   #######           #     #     #     #     #     #
# ##     #  #     #          # #    #  #  #    # #     #   #
# ##        #     #         #   #   #  #  #   #   #     # #
# ##  ####  #     #        #     #  #  #  #  #     #     #
# ##     #  #     #        #######  #  #  #  #######     #      ###
# ##     #  #     #        #     #  #  #  #  #     #     #      ###
# # #####   #######        #     #   ## ##   #     #     #      ###
#
# Seriously. You shouldn't even be looking at this file unless you're
# debugging generate_mozbuild.py.
#
# DO NOT MODIFY THIS FILE IT IS AUTOGENERATED.
#

skia_opt_flags = []

if CONFIG['MOZ_OPTIMIZE']:
    if CONFIG['_MSC_VER']:
        skia_opt_flags += ['-O2']
    elif CONFIG['GNU_CC']:
        skia_opt_flags += ['-O2']

"""

footer = """

# We allow warnings for third-party code that can be updated from upstream.
ALLOW_COMPILER_WARNINGS = True

FINAL_LIBRARY = 'gkmedias'
LOCAL_INCLUDES += [
    'skia/include/c',
    'skia/include/config',
    'skia/include/core',
    'skia/include/effects',
    'skia/include/gpu',
    'skia/include/images',
    'skia/include/pathops',
    'skia/include/ports',
    'skia/include/private',
    'skia/include/utils',
    'skia/include/utils/mac',
    'skia/include/views',
    'skia/src/core',
    'skia/src/gpu',
    'skia/src/gpu/effects',
    'skia/src/gpu/gl',
    'skia/src/image',
    'skia/src/lazy',
    'skia/src/opts',
    'skia/src/sfnt',
    'skia/src/utils',
    'skia/src/utils/mac',
    'skia/src/utils/win',
]

if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
    'cocoa',
    'gtk2',
    'gtk3',
    'uikit',
  }:
    DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1

if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
    DEFINES['UNICODE'] = True
    DEFINES['_UNICODE'] = True
    UNIFIED_SOURCES += [
        'skia/src/fonts/SkFontMgr_indirect.cpp',
        'skia/src/fonts/SkRemotableFontMgr.cpp',
    ]

if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
    DEFINES['SKIA_DLL'] = 1
    DEFINES['GR_DLL'] = 1

# We should autogenerate these SSE related flags.

if CONFIG['_MSC_VER']:
    # MSVC doesn't need special compiler flags, but Skia needs to be told that these files should
    # be built with the required SSE level or it will simply compile in stubs and cause runtime crashes
    SOURCES['skia/src/opts/SkBitmapFilter_opts_SSE2.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=20']
    SOURCES['skia/src/opts/SkBitmapProcState_opts_SSE2.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=20']
    SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=31']
    SOURCES['skia/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=20']
    SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=31']
    SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=41']
    SOURCES['skia/src/opts/SkOpts_sse42.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=42']
    SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-DSK_CPU_SSE_LEVEL=51']
if CONFIG['INTEL_ARCHITECTURE'] and (CONFIG['GNU_CC'] or CONFIG['CLANG_CL']):
    SOURCES['skia/src/opts/SkBitmapFilter_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
    SOURCES['skia/src/opts/SkBitmapProcState_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
    SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3']
    SOURCES['skia/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
    SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-mssse3']
    SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1']
    SOURCES['skia/src/opts/SkOpts_sse42.cpp'].flags += ['-msse4.2']
    SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx']
elif CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC'] and CONFIG['BUILD_ARM_NEON']:
    DEFINES['SK_ARM_HAS_OPTIONAL_NEON'] = 1

DEFINES['SKIA_IMPLEMENTATION'] = 1

if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
    DEFINES['SK_SUPPORT_GPU'] = 0

if CONFIG['MOZ_TREE_FREETYPE']:
    DEFINES['SK_CAN_USE_DLOPEN'] = 0

# Suppress warnings in third-party code.
if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']:
    CXXFLAGS += [
        '-Wno-deprecated-declarations',
        '-Wno-overloaded-virtual',
        '-Wno-shadow',
        '-Wno-sign-compare',
        '-Wno-unreachable-code',
        '-Wno-unused-function',
    ]
if CONFIG['GNU_CXX'] and not CONFIG['CLANG_CXX'] and not CONFIG['CLANG_CL']:
    CXXFLAGS += [
        '-Wno-logical-op',
        '-Wno-maybe-uninitialized',
    ]
if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
    CXXFLAGS += [
        '-Wno-implicit-fallthrough',
        '-Wno-inconsistent-missing-override',
        '-Wno-macro-redefined',
        '-Wno-unused-private-field',
    ]

if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
    CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
    CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']

if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
    CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']
"""

import json

platforms = ['linux', 'mac', 'win']

def generate_opt_sources():
  opt_sources = {'opts': {''}}
  for root, dirs, files in os.walk('skia/src/opts'):
    for name in files:
      if name.endswith('.cpp'):
        opt_sources['opts'].add(os.path.join(root, name))

  return opt_sources

def generate_platform_sources():
  sources = {}

  for plat in platforms:
    if os.system("cd skia && GYP_GENERATORS=dump_mozbuild ./gyp_skia -D OS=%s -D host_os=linux gyp/skia_lib.gyp" % plat) != 0:
      print('Failed to generate sources for ' + plat)
      continue


    f = open('skia/sources.json');
    sources[plat] = set(v.replace('../', 'skia/') for v in json.load(f));
    f.close()

  return dict(list(sources.items()) + list(generate_opt_sources().items()))


def generate_separated_sources(platform_sources):
  blacklist = [
    'experimental',
    'SkXML',
    'GrGLCreateNativeInterface',
    'GrGLCreateNullInterface',
    'GrGLAssembleInterface',
    'GrGLTestInterface',
    'fontconfig',
    'FontConfig',
    'SkThreadUtils_pthread_',
    'SkFontMgr_android',
    'SkFontMgr_custom',
    'SkFontHost_FreeType.cpp',
    'Movie',
    'ImageEncoder',
    'skia/src/c/',
    'skia/src/effects/Gr',
    'skia/src/effects/Sk',
    'skia/src/fonts/',
    'skia/src/images/',
    'skia/src/ports/SkImageGenerator',
    'skia/src/sksl/',
    'skia/src/gpu/vk/',
    'SkBitmapRegion',
    'SkLight',
    'SkRadialShadow',
    'SkShadow',
    'SkNormal',
    'SkLite',
    'codec',
    'SkWGL',
    'SkMemory_malloc',
    'SkOpts_',
    'opts_check_x86',
    'third_party',
    # unused in skia/src/utils
    'SkBoundaryPatch',
    'SkCamera',
    'SkCanvasStack',
    'SkCanvasStateUtils',
    'SkCurveMeasure',
    'SkDeferredCanvas',
    'SkDumpCanvas',
    'SkFrontBufferedStream',
    'SkInterpolator',
    'SkLayer',
    'SkMeshUtils',
    'SkMD5',
    'SkMultiPictureDocument',
    'SkNinePatch',
    'SkNullCanvas',
    'SkNWayCanvas',
    'SkPaintFilterCanvas',
    'SkParseColor',
    'SkPatchGrid',
    'SkRTConf',
    'SkTextBox',
    'SkWhitelistTypefaces',
  ]

  def isblacklisted(value):
    for item in blacklist:
      if value.find(item) >= 0:
        return True

    return False

  separated = defaultdict(set, {
    'common': {
      'skia/src/core/SkBlurImageFilter.cpp',
      'skia/src/core/SkGpuBlurUtils.cpp',
      'skia/src/effects/SkDashPathEffect.cpp',
      'skia/src/effects/SkImageSource.cpp',
      'skia/src/effects/SkLayerRasterizer.cpp',
      'skia/src/gpu/gl/GrGLCreateNativeInterface_none.cpp',
      'skia/src/ports/SkDiscardableMemory_none.cpp',
      'skia/src/ports/SkMemory_mozalloc.cpp',
      'skia/src/ports/SkImageEncoder_none.cpp',
      'skia/src/ports/SkImageGenerator_none.cpp',
    },
    'linux': {
      'skia/src/ports/SkFontHost_cairo.cpp',
    },
    'intel': {
      # There is currently no x86-specific opt for SkTextureCompression
      'skia/src/opts/opts_check_x86.cpp',
      'skia/src/opts/SkOpts_ssse3.cpp',
      'skia/src/opts/SkOpts_sse41.cpp',
      'skia/src/opts/SkOpts_sse42.cpp',
      'skia/src/opts/SkOpts_avx.cpp',
      'skia/src/opts/SkOpts_hsw.cpp',
    },
    'arm': {
      'skia/src/core/SkUtilsArm.cpp',
    },
    'neon': {
      'skia/src/opts/SkOpts_neon.cpp',
    },
    'none': set(),
    'pdf': set(),
    'gpu': set()
  })

  for plat in list(platform_sources.keys()):
    for value in platform_sources[plat]:
      if isblacklisted(value):
        continue

      if value in separated['common']:
        continue

      key = plat

      if '_SSE' in value or '_SSSE' in value:
        key = 'intel'
      elif '_neon' in value:
        key = 'neon'
      elif '_arm' in value:
        key = 'arm'
      elif '_none' in value:
        key = 'none'
      elif 'gpu' in value or 'Gpu' in value:
        key = 'gpu'
      elif all(value in platform_sources.get(p, {})
               for p in platforms if p != plat):
        key = 'common'

      separated[key].add(value)

  if os.system("cd skia && GYP_GENERATORS=dump_mozbuild ./gyp_skia -D OS=linux -D host_os=linux -R pdf gyp/pdf.gyp") != 0:
    print('Failed to generate sources for Skia PDF')
  else:
    f = open('skia/sources.json');
    separated['pdf'].add('skia/src/core/SkMD5.cpp');
    separated['pdf'].update([x for x in set(v.replace('../', 'skia/') for v in json.load(f)) if 'pdf' in x]);
    f.close()

  return separated

def uniq(seq):
  seen = set()
  seen_add = seen.add
  return [ x for x in seq if x not in seen and not seen_add(x)]

def write_cflags(f, values, subsearch, cflag, indent):
  def write_indent(indent):
    for _ in range(indent):
        f.write(' ')

  if isinstance(subsearch, str):
    subsearch = [ subsearch ]

  def iswhitelisted(value):
    for item in subsearch:
      if value.find(item) >= 0:
        return True

    return False

  val_list = uniq(sorted(values, key=lambda x: x.lower()))

  if len(val_list) == 0:
    return

  for val in val_list:
    if iswhitelisted(val):
      write_indent(indent)
      f.write("SOURCES[\'" + val + "\'].flags += " + cflag + "\n")

opt_whitelist = [
  'skia/src/opts/Sk',
  'SkOpts',
  'SkBitmapProcState',
  'SkBlitMask',
  'SkBlitRow',
  'SkBlitter',
  'SkSpriteBlitter',
  'SkMatrix.cpp',
]

# Unfortunately for now the gpu and pathops directories are
# non-unifiable. Keep track of this and fix it.
unified_blacklist = [
  'FontHost',
  'SkAdvancedTypefaceMetrics',
  'SkBitmapProcState_matrixProcs.cpp',
  'SkBlitter_A8.cpp',
  'SkBlitter_ARGB32.cpp',
  'SkBlitter_RGB16.cpp',
  'SkBlitter_Sprite.cpp',
  'SkScan_Antihair.cpp',
  'SkParse.cpp',
  'SkPDFFont.cpp',
  'SkPictureData.cpp',
  'GrDrawContext',
  'GrResourceCache',
  'GrAA',
  'GrGL',
  'GrBatchAtlas.cpp',
  'GrMSAAPathRenderer.cpp',
  'GrNonAAFillRect',
  'SkColorSpace',
  'SkImage_Gpu.cpp',
  'SkPathOpsDebug.cpp',
  'SkParsePath.cpp',
  'SkRecorder.cpp',
  'SkMiniRecorder.cpp',
  'SkXfermode',
  'SkMatrix44.cpp',
] + opt_whitelist

def write_sources(f, values, indent):
  def isblacklisted(value):
    for item in unified_blacklist:
      if value.find(item) >= 0:
        return True

    return False

  sources = {}
  sources['nonunified'] = set()
  sources['unified'] = set()

  for item in values:
    if isblacklisted(item):
      sources['nonunified'].add(item)
    else:
      sources['unified'].add(item)

  write_list(f, "UNIFIED_SOURCES", sources['unified'], indent)
  write_list(f, "SOURCES", sources['nonunified'], indent)
  
def write_list(f, name, values, indent):
  def write_indent(indent):
    for _ in range(indent):
        f.write(' ')

  val_list = uniq(sorted(values, key=lambda x: x.lower()))

  if len(val_list) == 0:
    return

  write_indent(indent)
  f.write(name + ' += [\n')
  for val in val_list:
    write_indent(indent + 4)
    f.write('\'' + val + '\',\n')

  write_indent(indent)
  f.write(']\n')

def write_mozbuild(sources):
  filename = 'moz.build'
  f = open(filename, 'w')

  f.write(header)

  write_sources(f, sources['common'], 0)
  write_cflags(f, sources['common'], opt_whitelist, 'skia_opt_flags', 0)

  f.write("if CONFIG['MOZ_ENABLE_SKIA_PDF']:\n")
  write_sources(f, sources['pdf'], 4)

  f.write("if CONFIG['MOZ_ENABLE_SKIA_GPU']:\n")
  write_sources(f, sources['gpu'], 4)

  f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}:\n")
  write_sources(f, sources['mac'], 4)

  f.write("if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:\n")
  write_sources(f, sources['linux'], 4)

  f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':\n")
  # Windows-specific files don't get unification because of nasty headers.
  # Luckily there are not many files in this.
  write_list(f, "SOURCES", sources['win'], 4)

  f.write("if CONFIG['INTEL_ARCHITECTURE']:\n")
  write_sources(f, sources['intel'], 4)
  write_cflags(f, sources['intel'], opt_whitelist, 'skia_opt_flags', 4)

  f.write("elif CONFIG['CPU_ARCH'] in ('arm', 'aarch64') and CONFIG['GNU_CC']:\n")
  write_sources(f, sources['arm'], 4)
  write_cflags(f, sources['arm'], opt_whitelist, 'skia_opt_flags', 4)

  f.write("    if CONFIG['CPU_ARCH'] == 'aarch64':\n")
  write_sources(f, sources['neon'], 8)
  f.write("    elif CONFIG['BUILD_ARM_NEON']:\n")
  write_list(f, 'SOURCES', sources['neon'], 8)
  write_cflags(f, sources['neon'], 'neon', "CONFIG['NEON_FLAGS']", 8)

  f.write("    if CONFIG['CPU_ARCH'] == 'aarch64' or CONFIG['BUILD_ARM_NEON']:\n")
  write_cflags(f, sources['neon'], opt_whitelist, 'skia_opt_flags', 8)
 
  f.write("else:\n")
  write_sources(f, sources['none'], 4)

  f.write(footer)

  f.close()

  print('Wrote ' + filename)

def main():
  platform_sources = generate_platform_sources()
  separated_sources = generate_separated_sources(platform_sources)
  write_mozbuild(separated_sources)


if __name__ == '__main__':
  main()
