config("default") {
  if (is_android) {
    defines = [ "OS_ANDROID" ]
  } else if (is_ios) {
    defines = [ "OS_IOS" ]
  } else if (is_linux) {
    defines = [ "OS_LINUX" ]
  } else if (is_mac) {
    defines = [ "OS_MAC" ]
  } else if (is_win) {
    defines = [ "OS_WIN" ]
  }
  if (is_ios || is_mac) {
    defines += [ "OS_APPLE" ]
  }

  cflags_cc = []
  if (is_win) {
    cflags_cc += [ "/std:c++20" ]
  } else {
    cflags_cc += [ "-std=c++20" ]
  }

  cflags = []
  if (is_win) {
    cflags += [
      "/permissive-",  # Don't enable nonconforming code to compile.
      "/EHsc",  # Use C++ exception handling.
      "/Gd",  # __cdecl calling convention.
      "/utf-8",  # Set Source and Executable character set.
    ]
    defines += [
      # Windows headers from MS and third party expects these to be set.
      "_WINDOWS",
      "_UNICODE",
      "UNICODE",

      # Don't want MS specific extensions for C stdlib functions.
      "_CRT_SECURE_NO_WARNINGS",

      # Clean up the Windows headers.
      "WIN32_LEAN_AND_MEAN",
      "NOMINMAX",
    ]
  } else {
    # cflags += [
    #   "-fstrict-aliasing",  # Optimizations allowed for type aliasing.
    #   "-fvisibility=hidden",  # Only let symbols be visible inside it's own
    #                           # library.
    # ]
    # cflags_cc += [
    #   "-fvisibility-inlines-hidden",  # Inline methods may not generate symbols.
    #   # "-stdlib=libc++",
    # ]

    if (is_linux) {
      cflags += [
        "-fPIC",  # Emit position-independent code suitable for dynamic linking.
      ]
      libs = [
        "pthread",  # Use the POSIX thread library.
      ]
    } else if (is_mac) {
      asmflags = [
        "-target",
        "arm64-apple-macos11",
      ]
      cflags += [
        # "-fPIC",  # Emit position-independent code suitable for dynamic linking.
        "-target",
        "arm64-apple-macos11",
      ]
      ldflags = [
        "-target",
        "arm64-apple-macos11",
      ]
    } else if (is_android) {
      cflags += [
        "--sysroot=$ndk/toolchains/llvm/prebuilt/$ndk_host/sysroot",
        "-fPIC",  # Emit position-independent code suitable for dynamic linking.
      ]
      ldflags = [ "-static-libstdc++" ]
    }
  }

  # Avoid relative paths everywhere.
  include_dirs = [
    "//src",
  ]
}

config("debug") {
  defines = [ "_DEBUG" ]

  if (is_win) {
    cflags = [
      "/GS",  # Buffer security checks (buffer overruns).
      "/sdl",  # Enable additional security features and warnings.
      "/RTC1",  # Enable fast run-time checks.
      "/Od",  # Disable optimizations.
      "/fp:precise",  # Floating points are predictable.
      "/MDd",  # Link with multithread debug DLL run-time libraries.
      "/Z7",  # Produce obj files that contain full symbolic debugging
              # information.
    ]
    ldflags = [ "/DEBUG:FASTLINK" ]  # Reference the debug symbols in the obj
                                     # files instead of a PDB file.
  } else {
    cflags = [
      "-g",  # Debug symbols.
      # TODO: This makes it hard to step through code on Linux.
      # "-Og",  # Optimizations that works well with debugger.
    ]

    if (is_unix) {
      defines += [
        "_GLIBCXX_DEBUG=1",  # Enable asserts in stl headers.
      ]
    }
  }
}

config("release") {
  defines = [ "NDEBUG" ]

  if (is_win) {
    cflags = [
      "/GL",  # Link-time code generation / Whole program optimization.
      "/Gy",  # Allows converting inline functions to separate functions.
      "/O2",  # Optimize for maximum speed.
      "/Oi",  # Enable intrinsics.
      "/fp:fast",  # Floating points are fast.
      "/MD",  # Link with multithread release DLL run-time libraries.

      # "/arch:AVX2",   # Minimum CPU architecture.
    ]
    ldflags = [
      "/LTCG",  # Link time code generation.
      "/OPT:ICF",  # COMDAT (common data) folding, e.g. vtables and big inline
                   # functions that can't be inlined, but also if identical code
                   # happens to be generated though common math operations or
                   # similar.
      "/OPT:REF",  # Eliminate unreferenced code and data.
    ]
  } else {
    cflags = [
      "-Ofast",  # Full optimization and disregard strict standard compliance.
      "-fno-math-errno",  # Do not check or set errno for invalid input to sqrt
                          # and other math functions.
    ]

    if (is_apple) {
      ldflags = [ "-dead-strip" ]
    } else {
      # Place both data and functions in it's their own sections.
      # Linker optimization that allows for smaller binaries.
      cflags += [
        "-fdata-sections",
        "-ffunction-sections",
      ]
      ldflags = [ "-Wl,--gc-sections" ]
    }
  }
}

config("warnings") {
  if (is_win) {
    cflags = [
      "/WX",  # Treat warnings as errors.
      "/Wall",  # Enable all warnings.

      # Disable the following warnings:
      "/wd4061",  # enumerator in switch with a default case not explicitly
                  # handled by a case label.
      "/wd4100",  # nonstandard extension used: nameless struct/union.
      "/wd4201",  # enumerator in switch with a default case not explicitly
                  # handled by a case label.
      "/wd4324",  # structure was padded due to alignment specifier
      "/wd4371",  # layout of class may have changed from a previous version of
                  # the compiler due to better packing of member
      "/wd4514",  # unreferenced inline function has been removed.
      "/wd4710",  # function not inlined
      "/wd4711",  # function selected for automatic inline expansion
      "/wd4820",  # padding added after data member.
      "/wd5045",  # compiler will insert Spectre mitigation for memory load if
                  # switch specified.
      "/wd4355",  # 'this': used in base member initializer list

      # TODO: Not sure how I feel about these conversion warnings.
      "/Wv:18",
      "/wd4191",  # 'type cast': unsafe conversion
      "/wd4244",  # conversion, possible loss of data. 'int' to 'float'
      "/wd4245",  # conversion from 'int' to 'const unsigned int'
      "/wd4305",  # truncation from 'double' to 'float'.
      "/wd4365",  # conversion, signed/unsigned mismatch.
      "/wd4722",  # destructor never returns, potential memory leak
      "/wd4702",  # unreachable code
      "/wd4625",  # copy constructor was implicitly defined as deleted
      "/wd4626",  # assignment operator was implicitly defined as deleted

      # Possible compiler bug? Needs investigation.
      "/wd4668",  # '__STDC_WANT_SECURE_LIB__' is not defined as a preprocessor
                  # macro, replacing with '0' for '#if/#elif'
    ]

    defines = [
      "_CRT_NONSTDC_NO_DEPRECATE",
      "_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING",
      "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
    ]
  } else if (is_mac) {
    cflags = [
      "-Wno-deprecated-declarations",
      "-Wno-inconsistent-missing-override",
      "-Wno-pointer-sign",
    ]
  } else {
    cflags = [
      # Enable as much warnings as possible.
      "-Werror",  # Make all warnings into errors.
      "-Wall",  # Enable (almost) all warnings.
      "-Wextra",  # Enable even more warnings.

      # forbidden extensions.
      "-Wvla",  # Warn if variable-length array is used in code.

      # Disable the following warnings:
      "-Wno-unused-parameter",
      "-Wno-sign-compare",
    ]
  }
}

config("executable") {
  if (is_android) {
    ldflags = [
      "-pie",
      "-rdynamic",
    ]
  } else if (is_linux) {
    ldflags = [
      "-Wl,-rpath,\$ORIGIN",  # Add directory to runtime library search path.
    ]
  } else if (is_mac) {
    ldflags = [ "-Wl,-rpath,@loader_path/." ]
  }
}

config("shared_library") {
  if (is_android) {
    ldflags = [
      "-Wl,--build-id=sha1",
      "-Wl,--no-rosegment",
      "-Wl,--fatal-warnings",
      "-Wl,--no-undefined",
      "-Qunused-arguments",
      "-u ANativeActivity_onCreate",
    ]
  } else if (is_linux) {
    ldflags = [
      "-Wl,-rpath,\$ORIGIN",  # Add directory to runtime library search path.
    ]
  } else if (is_mac) {
    ldflags = [ "-Wl,-rpath,@loader_path/." ]
  }
}

# TODO: This needs some more investigation.
# Is it possible to avoid setting it and rely on defaults?
# Some tools will be console apps while games will be gui apps.
# GLFW seems to be using console in debug mode and gui in release mode.
if (is_win) {
  config("win_gui") {
    ldflags = [ "/SUBSYSTEM:WINDOWS" ]
  }

  config("win_console") {
    ldflags = [ "/SUBSYSTEM:CONSOLE" ]
  }
}
