cmake_minimum_required(VERSION 3.0)

# Since the PS4 CMake sets the PS4 variable 1, we don't want our "PS4" string
# values to evaluate to "1"
cmake_policy(SET CMP0054 NEW)

# for tvapps
set(YI_DEVELOPMENT_TEAM $ENV{DEVELOPMENT_TEAM_ID} CACHE STRING "Development team" FORCE)

if (DEFINED ENV{GIT_COMMIT})
    string(SUBSTRING $ENV{GIT_COMMIT} 0 8 GITHASH)
else()
    execute_process(
        COMMAND git rev-parse --short=8 HEAD
        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
        OUTPUT_VARIABLE GITHASH
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
endif()

execute_process(
    COMMAND node version.js
    WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/scripts
    OUTPUT_VARIABLE PROJECT_VERSION
    OUTPUT_STRIP_TRAILING_WHITESPACE
)

option(BUILD_EXAMPLES "Build examples" ON)

# Give our project access to our CMake modules
message(STATUS "PlayerCore CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")
message(STATUS "PlayerCore CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)

project(player-core VERSION ${PROJECT_VERSION} LANGUAGES C CXX)

include(TargetPlatform)
add_definitions(-DPLAYERCORE_VERSION="${PROJECT_VERSION}-${GITHASH}")
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

# Build configurations
if(CMAKE_CONFIGURATION_TYPES) # multiconfig generator?
    set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo" CACHE STRING "" FORCE)
else()
    if(NOT CMAKE_BUILD_TYPE)
        set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
    endif()
endif()

if (ENABLE_BITCODE)
    add_compile_options(-fembed-bitcode)
endif()

set(CMAKE_CXX_STANDARD 11)

# Detect 32 bits vs 64 bits
if(CMAKE_SYSTEM_NAME STREQUAL "Windows" OR TARGET_PLATFORM STREQUAL "WinStore" OR TARGET_PLATFORM STREQUAL "XboxOne" OR TARGET_PLATFORM STREQUAL "Win64")
    if(${CMAKE_GENERATOR} MATCHES "Win64")  # On UWP, CMAKE_SIZEOF_VOID_P is not defined at all. Check the Generator name instead
        set(ARCHITECTURE_BITS 64)
    else()
        set(ARCHITECTURE_BITS 32)
    endif()
else()
    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
        set(ARCHITECTURE_BITS 64)
    else()
        set(ARCHITECTURE_BITS 32)
    endif()
endif()

message(STATUS "Host platform: ${CMAKE_HOST_SYSTEM_NAME}")
message(STATUS "System name: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Target platform: ${TARGET_PLATFORM}")
message(STATUS "Target architecture is ${ARCHITECTURE_BITS} bits.")

if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
  if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
    string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
  endif()
endif()

if(TARGET_PLATFORM STREQUAL "WinStore" OR TARGET_PLATFORM STREQUAL "XboxOne" OR TARGET_PLATFORM STREQUAL "Win64")
  set(WARNING_FLAGS /W4 /WX /wd4200 /wd4503 /wd4127 /MP)
  add_definitions(-DNOMINMAX)
elseif(TARGET_PLATFORM STREQUAL "Android") # Current NDK's clang doesn't support -Wno-shadow-field-in-constructor, so disable shadow warnings
  set(WARNING_FLAGS -Wall -Wextra -Werror)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
  set(WARNING_FLAGS -Wall -Wextra -Werror)
else()
  # The PS4 vendor samples required for our sample app cause a -Wshadow-field warning in SDK 5.5 (CLANG 5.0.1)
  set(WARNING_FLAGS -Wall -Wextra -Werror -Wshadow-field-in-constructor-modified -Wshadow-ivar)
endif()

if(TARGET_PLATFORM STREQUAL "PS4")
    add_definitions(-D_ORBIS -D_PS4)
    add_compile_options(-frtti)

    math(EXPR NUM_COMPILE_PROCESSES "$ENV{NUMBER_OF_PROCESSORS}-1")
    if(${NUM_COMPILE_PROCESSES} LESS 1)
        set(NUM_COMPILE_PROCESSES 1)
    endif()

    message("Building with Multiple Processes ${NUM_COMPILE_PROCESSES}")
    add_compile_options(/MP${NUM_COMPILE_PROCESSES})
endif()

if(NOT TARGET_PLATFORM STREQUAL "Android" AND NOT TARGET_PLATFORM STREQUAL "iPhone" AND NOT TARGET_PLATFORM STREQUAL "AppleTV" AND NOT TARGET_PLATFORM STREQUAL "OSX")
  set(PLATFORM_LIB "platforms")
endif()

# Universal Windows Apps
if (TARGET_PLATFORM STREQUAL "WinStore" OR TARGET_PLATFORM STREQUAL "XboxOne")
    add_definitions(-DUWP)
    add_compile_options(/ZW /EHsc) # Windows Runtime Compilation
    set(WARNING_FLAGS ${WARNING_FLAGS} /wd4447)
endif()

# Windows 64-bit native
if (TARGET_PLATFORM STREQUAL "Win64")
    set(WARNING_FLAGS ${WARNING_FLAGS} /wd4447)
endif()

# Output directories
if(NOT TARGET_PLATFORM STREQUAL "PS4")
    if(ARCHITECTURE_BITS EQUAL 32)
        set(OUTPUT_SUFFIX "x86")
    elseif(ARCHITECTURE_BITS EQUAL 64)
        set(OUTPUT_SUFFIX "x64")
    else()
        message(FATAL_ERROR "Unsupported bits architecture ${ARCHITECTURE_BITS}")
    endif()
endif()

# Save log files to this directory
set(LOG_DIR ${CMAKE_BINARY_DIR}/log)
if(NOT EXISTS ${LOG_DIR})
  message(STATUS "Creating log directory: ${LOG_DIR}")
  file(MAKE_DIRECTORY ${LOG_DIR})
endif()

if(MSVC OR TARGET_PLATFORM STREQUAL "PS4")
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/${OUTPUT_SUFFIX})
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/${OUTPUT_SUFFIX})
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${OUTPUT_SUFFIX})
endif()

include(AddConditionalOption)
include(TargetFolderProperty)

option(BUILD_DEPENDENCY_SUBMODULES "Build dependencies as submodules" OFF)

add_conditional_option(BUILD_TESTS
  CONDITION "NOT CMAKE_SYSTEM_NAME STREQUAL Windows"
  DESCRIPTION "Build the unit and integration tests")

if(BUILD_TESTS)
  enable_testing()
endif()

add_conditional_option(BUILD_UNIFIED_LIB
  CONDITION "NOT TARGET_PLATFORM STREQUAL iPhone AND NOT TARGET_PLATFORM STREQUAL OSX AND NOT CMAKE_GENERATOR STREQUAL Xcode"
  DESCRIPTION "Build a single unified library")

option(ENABLE_ADDRESS_SANITIZER "Enable address sanitizer" OFF)
if(ENABLE_ADDRESS_SANITIZER)
  include(AddressSanitizer)
endif()

add_conditional_option(ENABLE_CODE_COVERAGE
  CONDITION "CMAKE_BUILD_TYPE STREQUAL Debug AND BUILD_TESTS AND NOT TARGET_PLATFORM STREQUAL Web"
  DESCRIPTION "Enable code coverage reporting")

if(ENABLE_CODE_COVERAGE)
  include(CodeCoverage)
endif()

find_package(CPPCheck)
add_conditional_option(ENABLE_CPPCHECK
  CONDITION "CPPCheck_FOUND"
  DESCRIPTION "Enable cppcheck analysis")
if(ENABLE_CPPCHECK)
  enable_cppcheck(platforms src)
endif()

add_conditional_option(ENABLE_EXCEPTIONS
  CONDITION "NOT TARGET_PLATFORM STREQUAL Android"
  DESCRIPTION "Enable exceptions")
if(ENABLE_EXCEPTIONS)
  add_definitions(-DPLAYERCORE_ENABLE_EXCEPTIONS)

  # TODO: ENABLE_EXCEPTIONS=ON currently only adds the compiler flags for PS4.  We
  #     should eventually handle all other platforms
  if(TARGET_PLATFORM STREQUAL "PS4")
    add_compile_options(-fexceptions)
  endif()
else()
  add_compile_options(-fno-exceptions)
endif()

option(ENABLE_ADDRESS_SANITIZER "Enable address sanitizer" OFF)
if(ENABLE_ADDRESS_SANITIZER)
  include(AddressSanitizer)
endif()

if(TARGET_PLATFORM STREQUAL "PS4")
    option(ENABLE_PLATFORM_CONCURRENCY_PRIMITIVES "Use mutex and condition_variable preferred by the OS (not STL)" ON)
endif()

add_conditional_option(ENABLE_ENCRYPTION
    CONDITION "TARGET_PLATFORM STREQUAL Posix"
    DESCRIPTION "Enable AES encryption library")
if (ENABLE_ENCRYPTION)
    include(external/TinyAES)
endif()

set(PLAYERCORE_OPTIONS
  BUILD_EXAMPLES
  BUILD_TESTS
  BUILD_UNIFIED_LIB
  ENABLE_ADDRESS_SANITIZER
  ENABLE_CODE_COVERAGE
  ENABLE_CPPCHECK
  ENABLE_ENCRYPTION
  ENABLE_EXCEPTIONS
  ENABLE_PLATFORM_CONCURRENCY_PRIMITIVES
)

foreach(option ${PLAYERCORE_OPTIONS})
  message(STATUS "Player Core option: ${option} - ${${option}}")
endforeach()

message("Warning flags used: " ${WARNING_FLAGS})

if (ENABLE_PLATFORM_CONCURRENCY_PRIMITIVES)
    add_definitions(-DENABLE_PLATFORM_CONCURRENCY_PRIMITIVES)
endif()

# Add external dependencies
include(external/JSON11)
include(external/LibCaption)

# NOTE: We only need GCDWebServer for iOS/OSX Platforms
if(TARGET_PLATFORM STREQUAL "iPhone" OR TARGET_PLATFORM STREQUAL "AppleTV" OR TARGET_PLATFORM STREQUAL "OSX")
    include(external/gcdwebserver)
endif()

if(TARGET_PLATFORM STREQUAL "Posix")
  find_package(CURL REQUIRED)
  include_directories(SYSTEM ${CURL_INCLUDE_DIRS})
endif()

# all packages search for headers here
include_directories(${CMAKE_CURRENT_LIST_DIR})
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
set(PLAYER_LIB_INC ${CMAKE_CURRENT_LIST_DIR}/include/playercore)

include(CopyResources)
include(UnifiedLibrary)

#Packages
# Resources are shared between modules, so add this first
if(TARGET_PLATFORM STREQUAL "WinStore" OR TARGET_PLATFORM STREQUAL "XboxOne")
    add_subdirectory(platforms/uwp/resources)
endif()

if(BUILD_EXAMPLES)
    add_subdirectory(examples)

    if(TARGET_PLATFORM STREQUAL "PS4")
        set_property(DIRECTORY ${CMAKE_CURRENT_LIST_DIR} PROPERTY VS_STARTUP_PROJECT SamplePlayer)
    endif()

endif()
add_subdirectory(platforms)
add_subdirectory(vendor)
add_subdirectory(src)


if(BUILD_TESTS)
  # BUILD_EXAMPLES is required for PS4 because it requires linking to the nativeplayer
  if(TARGET_PLATFORM STREQUAL "PS4" AND NOT BUILD_EXAMPLES)
    message(SEND_ERROR "BUILD_EXAMPLES is disabled but is required to link the nativeplayer")
  endif()

  add_subdirectory(test)
endif()

# Force dependencies to be built before submodules
foreach(pc_library ${PLAYERCORE_LIBRARIES})
  add_dependencies(${pc_library} ${EXTERNAL_DEPENDENCIES})
endforeach()

# Unified Library Support
# NOTE: Disable for Android for now since it will call this function itself
if(BUILD_UNIFIED_LIB AND NOT TARGET_PLATFORM STREQUAL "Android")
  create_unified_library(playercore)
  target_include_directories(playercore
    PUBLIC
      $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
      $<INSTALL_INTERFACE:include>
  )
endif()

# Setup CPack for packaging our library
# These variables need to be set BEFORE including the CPack module
if(TARGET_PLATFORM STREQUAL Windows)
  set(CPACK_GENERATOR ZIP)
else()
  set(CPACK_GENERATOR TGZ)
endif()
set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME})

set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
include(CPack)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/PlayerCoreConfigVersion.cmake
  VERSION ${CPACK_PACKAGE_VERSION}
  COMPATIBILITY AnyNewerVersion
)

configure_file(
  ${PROJECT_SOURCE_DIR}/cmake/PlayerCoreConfig.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/PlayerCoreConfig.cmake
  COPYONLY
)

# Since only PS4 cares about the concurrency header, always do it
# The ENABLE_PLATFORM_CONCURRENCY_PRIMITIVES will enable/disable the feature
# This prevents caching issue with configure_file not re-running when switching platforms
set(CONCURRENCY_HEADER "platforms/ps4/Concurrency.hpp")

# Always configure this header, even if we are not using it, so we don't end up with weird code visible to the engineer
configure_file(
    ${PROJECT_SOURCE_DIR}/src/util/Concurrency.hpp.in
    ${PROJECT_SOURCE_DIR}/src/util/Concurrency.hpp
)

install(
  FILES
    ${CMAKE_CURRENT_BINARY_DIR}/PlayerCoreConfig.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/PlayerCoreConfigVersion.cmake
  DESTINATION
    lib/cmake/playercore
)

install(
  DIRECTORY ${PROJECT_SOURCE_DIR}/include
  DESTINATION include
  COMPONENT playercore
)

# NOTE: Disable for Android for now since it will call this function itself
if(BUILD_UNIFIED_LIB AND NOT TARGET_PLATFORM STREQUAL "Android")
  export(TARGETS playercore FILE "${PROJECT_BINARY_DIR}/PlayerCoreTargets.cmake")
  install(TARGETS playercore EXPORT PlayerCoreTargets
    INCLUDES DESTINATION include
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
    RUNTIME DESTINATION bin
  )
  export(EXPORT PlayerCoreTargets FILE ${CMAKE_CURRENT_BINARY_DIR}/PlayerCoreTargets.cmake)

  install(
    EXPORT PlayerCoreTargets
    FILE PlayerCoreTargets.cmake
    DESTINATION lib/cmake/playercore
  )
endif()

# add a target to generate API documentation with Doxygen
if(TARGET_PLATFORM STREQUAL "iPhone" OR TARGET_PLATFORM STREQUAL "AppleTV" OR TARGET_PLATFORM STREQUAL "OSX")
    set(DOXYFILE_PROJECT_BRIEF "Twitch iOS/tvOS/OSX Video Technology")
    set(DOXYFILE_INPUT "${CMAKE_CURRENT_LIST_DIR}/platforms/ios")
    set(DOXYFILE_INPUT_PATTERN "TTV*.h TTV*.hpp")
else()
    set(DOXYFILE_PROJECT_BRIEF "Twitch Core Video Technology")
    set(DOXYFILE_INPUT "${CMAKE_CURRENT_LIST_DIR}/include")
    set(DOXYFILE_INPUT_PATTERN "*.h *.hpp")
endif()

find_package(Doxygen)
if(DOXYGEN_FOUND)
  configure_file(${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
  add_custom_target(doxygen
    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    COMMENT "Generating API documentation with Doxygen" VERBATIM
)
endif(DOXYGEN_FOUND)

# Set minimum VS target version
if(TARGET_PLATFORM STREQUAL "WinStore" OR TARGET_PLATFORM STREQUAL "XboxOne")
  if (BUILD_EXAMPLES)
    set_target_properties(nativeplayer playercore util ${PLAYERCORE_LIBRARIES} ${player-core_OBJECT_LIBRARIES} PROPERTIES VS_GLOBAL_WindowsTargetPlatformMinVersion "10.0.14393.0")
  endif(BUILD_EXAMPLES)
  if (BUILD_TESTS)
    set_target_properties(integrationTestRunner integrationtest testenv PROPERTIES VS_GLOBAL_WindowsTargetPlatformMinVersion "10.0.14393.0")
  endif(BUILD_TESTS)
endif()
