#!/usr/bin/env zsh

emulate -L zsh
setopt err_return pipefail nounset no_unset
setopt typeset_silent

# This script takes care of copying and code signing the Reveal Server XCFramework into your iOS application target.

# NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein.
: ${CONFIGURATION:?missing} ${PLATFORM_NAME:?missing} ${CONFIGURATION_BUILD_DIR:?missing} \
  ${FRAMEWORKS_FOLDER_PATH:?missing}

# If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration.
load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:-Debug}

LLDB_INIT_FILE=~/.lldbinit
LLDB_INIT_MAGIC_STRING="### Reveal LLDB commands support"

if [ ! -e "${LLDB_INIT_FILE}" ] || ! grep -q "${LLDB_INIT_MAGIC_STRING}" "${LLDB_INIT_FILE}"; then
    print -r -- "warning: it looks like Reveal Server's debugger commands are not installed. Please try Reveal>Help>Install Debugger Commands… or refer to 'Loading the Reveal Server via an Xcode Breakpoint' section of the Integration Guide for information about loading Reveal Server in Reveal>Help>Integration Guide."
fi

if [[ "${PLATFORM_NAME}" == *"simulator" ]]; then
    print -r -- "Reveal Server not copied into ${TARGET_NAME}: Targeted platform is simulated, and does not require it."
    exit 0
fi

# On-device deployment-only code starting below

if [[ "${CONFIGURATION}" != "${load_trigger}" ]]; then
    print -r -- "Skipping Reveal Server integration for ${TARGET_NAME}: Current build configuration is not '${load_trigger}'."
    exit 0
fi

if [[ "${ENABLE_USER_SCRIPT_SANDBOXING}" == "YES" ]]; then
    print -u2 -r -- "Reveal Server requires that the 'User Script Sandboxing'/ENABLE_USER_SCRIPT_SANDBOXING build option be disabled to work on physical devices. To continue, set this to NO."
    exit 1
fi

if [[ "${GENERATE_INFOPLIST_FILE}" == "YES" && "${INFOPLIST_FILE}" == "" ]]; then
    print -r -- "warning: This project is automatically generating its Info.plist file with build setting GENERATE_INFOPLIST_FILE, and no additional properties have been set to force its generation. Reveal will not be able to inject the necessary values into this file to allow for network advertising via Bonjour. To work around this while still generating the Info.plist file, add the 'NSBonjourServices' key (array) with a single string entry with a value of '_reveal._tcp' in your project's Info settings."
fi

if [[ "${INFOPLIST_FILE}" != "" && "${SCRIPT_INPUT_FILE_0}" != "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" ]]; then
    print -r -- "warning: To ensure that Reveal can set the correct values required for Bonjour network advertising, please add the following as an input file to the Reveal Server build phase script: '\$(CONFIGURATION_BUILD_DIR)/\$(INFOPLIST_PATH)'. This ensures that the Reveal Server script runs after the Info.plist file has been processed."
fi

# Ensure that we have a valid REVEAL_SERVER_FILENAME environment variable, or fall back to the default shipped with the app.
REVEAL_SERVER_FILENAME=${REVEAL_SERVER_FILENAME:-"RevealServer.xcframework"}

# Ensure that we have a valid REVEAL_SERVER_PATH environment variable, or fall back to the default that we copy to the user's application support directory.
REVEAL_SERVER_PATH=${REVEAL_SERVER_PATH:-"${HOME}/Library/Application Support/Reveal/RevealServer/${REVEAL_SERVER_FILENAME}"}

# Populate a variable with the current code signing identity if it's available in the environment.
SIGNING_IDENTITY="${EXPANDED_CODE_SIGN_IDENTITY:-$CODE_SIGN_IDENTITY}"

# Populate a variable with the current code signing flags and options in the environment.
OTHER_CODE_SIGN_FLAGS=${OTHER_CODE_SIGN_FLAGS:-}

# Select the correct XCFramework variant directory for the current Xcode build.
# Usage: selected_dir="$(select_xcframework_variant "/path/to/Thing.xcframework")"
select_xcframework_variant() {
  emulate -L zsh
  setopt localoptions null_glob extended_glob

  local xcframework_root="${1:?Pass path to .xcframework root}"

  # Derive base platform and variant suffix from Xcode env
  local base="" want_suffix=""
  case "${PLATFORM_NAME:-}" in
    macosx)
      if [[ "${IS_MACCATALYST:-NO}" == "YES" ]]; then
        base="ios";  want_suffix="-maccatalyst"
      else
        base="macos"; want_suffix=""
      fi
      ;;
    iphoneos)         base="ios";  want_suffix="" ;;
    iphonesimulator)  base="ios";  want_suffix="-simulator" ;;
    appletvos)        base="tvos"; want_suffix="" ;;
    appletvsimulator) base="tvos"; want_suffix="-simulator" ;;
    xros)             base="xros"; want_suffix="" ;;
    xrsimulator)      base="xros"; want_suffix="-simulator" ;;
    *) print -u2 -- "select_xcframework_variant: unsupported PLATFORM_NAME='${PLATFORM_NAME:-<unset>}'"; return 2 ;;
  esac

  # Build a list of all variant directories under the xcframework for the base platform.
  # We intentionally avoid zsh extended-glob qualifiers so the logic works even if executed under bash.
  # Collect broadly with a simple glob, then filter in code.
  local -a all_variants
  all_variants=( "${xcframework_root:A}/${base}-"*/ )

  # Filter variants according to the desired suffix (device vs simulator/catalyst).
  local -a candidates
  candidates=()
  local variant_path variant_name
  for variant_path in "${all_variants[@]}"; do
    # Ensure it's a directory; the trailing slash from the glob usually guarantees this, but double-check.
    [[ -d "${variant_path}" ]] || continue

    # Variant directory name (basename), e.g., ios-arm64, ios-arm64_x86_64-simulator, ios-arm64-maccatalyst
    variant_name="${variant_path:t}"

    if [[ -n "${want_suffix}" ]]; then
      # Simulator/Catalyst build: require exact suffix match.
      # Example: want_suffix="-simulator" will select names ending with "-simulator".
      [[ "${variant_name}" == *"${want_suffix}" ]] || continue
    else
      # Device build: exclude simulator and catalyst variants explicitly.
      [[ "${variant_name}" == *"-simulator" ]] && continue
      [[ "${variant_name}" == *"-maccatalyst" ]] && continue
    fi

    candidates+=( "${variant_path}" )
  done

  if (( ${#candidates} == 0 )); then
    print -u2 -- "select_xcframework_variant: no variant for ${base}${want_suffix} under ${xcframework_root}"
    return 3
  fi

  # Prefer a candidate whose architecture token matches ARCHS (spaces collapsed to underscores), if present.
  local selected="${candidates[1]}"
  local architectures="${ARCHS:-}"
  local arch_token="${architectures// /_}"
  if (( ${#candidates} > 1 )) && [[ -n "${arch_token}" ]]; then
    local d
    for d in "${candidates[@]}"; do
      [[ "${d:t}" == *"-${arch_token}${want_suffix}" ]] && { selected="$d"; break; }
    done
  fi

  # Echo absolute path
  builtin print -r -- "${selected:A}"
}

if [ -e "${REVEAL_SERVER_PATH}" ]; then
    APP_FRAMEWORKS_DIRECTORY="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"

    # Ensure that the application bundle's "Frameworks" directory exists.
    mkdir -p "${APP_FRAMEWORKS_DIRECTORY}"

    # Copy the XCFramework into place.
    xcframework_variant="$(select_xcframework_variant "${REVEAL_SERVER_PATH}")"
    ditto --noqtn --noacl "${xcframework_variant}/RevealServer.framework" "${APP_FRAMEWORKS_DIRECTORY}/RevealServer.framework"

    # Re-sign the XCFramework using the application's details, or the kernel will refuse to load any of the bundled framework binaries.
    if [ -n "${SIGNING_IDENTITY}" ]; then
        # Sign the inner framework.
        xcrun codesign ${OTHER_CODE_SIGN_FLAGS} \
            --force \
            --sign "${SIGNING_IDENTITY}" \
            "${APP_FRAMEWORKS_DIRECTORY}/RevealServer.framework";
    fi

    print -r -- "RevealServer.framework is included in this build, and has been copied to $APP_FRAMEWORKS_DIRECTORY"

    # It is not possible to inject these values when the Info.plist file is fully generated
    if [[ "${GENERATE_INFOPLIST_FILE}" == "NO" || "${INFOPLIST_FILE}" != "" ]]; then
        # Now we need to attempt to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing.
        plutil -insert NSBonjourServices -xml '<array/>' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true

        # Now insert Reveal's Bonjour details into the NSBonjourServices array.
        plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"

        PLIST_BONJOUR_CONFIRMATION="$(plutil -extract "NSBonjourServices.0" raw "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}")"

        if [[ "${PLIST_BONJOUR_CONFIRMATION}" != "_reveal._tcp" ]]; then
            print -u2 -r -- "Failed to write 'NSBonjourServices' key to ${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"
            exit 1
        else
            print -r -- "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"
        fi

        # Now we need to setup the NSLocalNetworkUsageDescription in the app's Info.plist. It's OK if this fails due to the key already existing.
        plutil -insert NSLocalNetworkUsageDescription -string 'Reveal Server needs access to the local network to allow communication with the Reveal desktop application.' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true

        PLIST_PERMISSION_CONFIRMATION="$(plutil -extract "NSLocalNetworkUsageDescription" raw "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}")"

        if [[ "${PLIST_PERMISSION_CONFIRMATION}" == "" ]]; then
            ecprint -u2 -r --ho "Failed to write 'NSLocalNetworkUsageDescription' key to ${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"
            exit 1
        else
            print -r -- "An NSLocalNetworkUsageDescription entry for Reveal has been added to ${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"
        fi
    else
        print -r -- "warning: Skipping NSBonjourServices entry as the Info.plist file is auto-generated."
    fi

    print -r -- "Reveal Server has been successfully integrated into ${TARGET_NAME}."
else
  print -r -- "Reveal Server not integrated into ${TARGET_NAME}: ${REVEAL_SERVER_FILENAME} could not be found."
fi

