#!/usr/bin/env bash

# Freeciv - Copyright (C) 2022-2023 - Freeciv team
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2, or (at your option)
#  any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

# Variant of homebrew-appbundle.sh: GTK4 client only, libraries bundled
# inside the package. Output: dist/Freeciv.app and dist/Freeciv-<version>.dmg
#
# Required:
#   brew install dylibbundler meson ninja pkg-config gettext icu4c \
#     readline sqlite curl xz zstd gtk4 librsvg adwaita-icon-theme lua@5.4 \
#     sdl2 sdl2_mixer sdl3 gdk-pixbuf
#
# Note: Homebrew's sdl2 is provided by sdl2-compat, which loads libSDL3
# at runtime via dlopen(). dylibbundler cannot see such dependencies,
# so libSDL3 is copied into the bundle manually (see below).

# Work from the top-level source directory, regardless of where the
# script was started from
cd "$(dirname "$0")/../.." || exit 1

BREW="$(brew --prefix)"

export CXXFLAGS=-std=c++17
export CPPFLAGS="-I$(brew --prefix readline)/include -I$(brew --prefix gettext)/include -I${BREW}/include"
export LDFLAGS="-L$(brew --prefix icu4c)/lib -L$(brew --prefix readline)/lib -L$(brew --prefix gettext)/lib"
export PKG_CONFIG_PATH="$(brew --prefix icu4c)/lib/pkgconfig:$(brew --prefix lua@5.4)/lib/pkgconfig"
export PATH="$(brew --prefix gettext)/bin:$PATH"

if ! command -v dylibbundler >/dev/null ; then
  echo "dylibbundler not found. Install it with: brew install dylibbundler" >&2
  exit 1
fi

DISTDIR="${PWD}/dist"
APPDIR="${DISTDIR}/Freeciv.app"
CONTENTSDIR="${APPDIR}/Contents"

# Clean start (changing the configuration requires a fresh build directory)
rm -rf "${DISTDIR}" build

if ! mkdir -p "${CONTENTSDIR}/Resources" "${CONTENTSDIR}/MacOS" ; then
  echo "Failed to create \"${CONTENTSDIR}\"" >&2
  exit 1
fi

if ! cp data/freeciv-client.icns "${CONTENTSDIR}/Resources" ; then
  echo "Failed to copy file \"freeciv-client.icns\"" >&2
  exit 1
fi

VSTRING=$(./fc_version)

# Info.plist - the executable is a launcher script that sets up the environment
cat > "${CONTENTSDIR}/Info.plist" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleName</key>
	<string>freeciv</string>
	<key>CFBundleIdentifier</key>
	<string>freeciv</string>
	<key>CFBundleVersion</key>
	<string>${VSTRING}</string>
	<key>CFBundleShortVersionString</key>
	<string>${VSTRING}</string>
	<key>CFBundleExecutable</key>
	<string>Freeciv</string>
	<key>CFBundleIconFile</key>
	<string>freeciv-client.icns</string>
	<key>CFBundleDevelopmentRegion</key>
	<string>English</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>NSHighResolutionCapable</key>
	<true/>
</dict>
</plist>
EOF

if ! echo -n "APPL????" > "${CONTENTSDIR}/PkgInfo" ; then
  echo "Failed to create file \"${CONTENTSDIR}/PkgInfo\"" >&2
  exit 1
fi

# Launcher: points the game to data, icons, schemas and loaders inside the bundle
cat > "${CONTENTSDIR}/MacOS/Freeciv" <<'EOF'
#!/bin/bash
CONTENTS="$(cd "$(dirname "$0")/.." && pwd)"

export FREECIV_DATA_PATH="$CONTENTS/share/freeciv"
export FREECIV_LOCALE_PATH="$CONTENTS/share/locale"
export XDG_DATA_DIRS="$CONTENTS/share"
export GSETTINGS_SCHEMA_DIR="$CONTENTS/share/glib-2.0/schemas"
export PATH="$CONTENTS/bin:$PATH"

# sdl2-compat loads libSDL3 at runtime with dlopen(); make sure it is
# looked up inside the bundle
export DYLD_FALLBACK_LIBRARY_PATH="$CONTENTS/Frameworks"

# The gdk-pixbuf loaders cache needs absolute paths, so it is generated
# at startup (the bundle may live on a read-only disk image)
CACHE_IN="$CONTENTS/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache.in"
if [ -f "$CACHE_IN" ]; then
  CACHE_OUT="${TMPDIR:-/tmp}/freeciv-pixbuf-loaders.cache"
  sed "s|@CONTENTS@|$CONTENTS|g" "$CACHE_IN" > "$CACHE_OUT"
  export GDK_PIXBUF_MODULE_FILE="$CACHE_OUT"
fi

exec "$CONTENTS/bin/freeciv-gtk4" "$@"
EOF
chmod +x "${CONTENTSDIR}/MacOS/Freeciv"

mkdir build || { echo "Failed to create build directory" >&2; exit 1; }

cd build || exit 1

if ! meson setup .. \
       -Dtools=manual,ruleup \
       -Dsyslua=true \
       -Ddebug=false \
       -Dclients=gtk4 \
       -Dfcmp=gtk4 \
       -Dfollowtag=macos \
       -Ddefault_library=static \
       -Dprefix="${CONTENTSDIR}" ||
   ! ninja ||
   ! ninja install
then
  echo "Build failed!" >&2
  exit 1
fi

cd .. || exit 1

# --- GTK4 resources -------------------------------------------------------

# GSettings schemas (needed e.g. by the file chooser dialog)
mkdir -p "${CONTENTSDIR}/share/glib-2.0/schemas"
cp "${BREW}"/share/glib-2.0/schemas/*.xml "${CONTENTSDIR}/share/glib-2.0/schemas/"
glib-compile-schemas "${CONTENTSDIR}/share/glib-2.0/schemas"

# Icon themes
mkdir -p "${CONTENTSDIR}/share/icons"
for T in Adwaita hicolor ; do
  if [ -d "${BREW}/share/icons/${T}" ] ; then
    cp -R "${BREW}/share/icons/${T}" "${CONTENTSDIR}/share/icons/"
  fi
done

# gdk-pixbuf loaders
PIXDIR="lib/gdk-pixbuf-2.0/2.10.0"
PIXSRC="$(brew --prefix gdk-pixbuf)/${PIXDIR}/loaders"
if [ -d "${PIXSRC}" ] ; then
  mkdir -p "${CONTENTSDIR}/${PIXDIR}"
  cp -R "${PIXSRC}" "${CONTENTSDIR}/${PIXDIR}/"
  "$(brew --prefix gdk-pixbuf)/bin/gdk-pixbuf-query-loaders" \
      "${CONTENTSDIR}/${PIXDIR}"/loaders/*.so |
    sed "s|${CONTENTSDIR}|@CONTENTS@|g" > "${CONTENTSDIR}/${PIXDIR}/loaders.cache.in"
else
  echo "Warning: gdk-pixbuf loaders directory not found, skipping" >&2
fi

chmod -R u+w "${CONTENTSDIR}"

# --- Bundling libraries ---------------------------------------------------

# Collect all Mach-O binaries (executables, plugins, loaders)
MACHO=()
while IFS= read -r F ; do
  if file -b "$F" | grep -q 'Mach-O' ; then
    MACHO+=("$F")
  fi
done < <(find "${CONTENTSDIR}/bin" "${CONTENTSDIR}/lib" -type f 2>/dev/null)

ARGS=()
for F in "${MACHO[@]}" ; do
  ARGS+=(-x "$F")
done

if ! dylibbundler -od -b -cd "${ARGS[@]}" \
       -d "${CONTENTSDIR}/Frameworks/" \
       -p "@executable_path/../Frameworks/" \
       -s "${BREW}/lib" ; then
  echo "dylibbundler failed!" >&2
  exit 1
fi

# --- libSDL3 (loaded at runtime by sdl2-compat) -----------------------------
# Homebrew's libSDL2 is sdl2-compat, which dlopen()s libSDL3.0.dylib.
# dylibbundler does not see dlopen() dependencies, so without this step
# the app aborts at startup with "Failed loading SDL3 library."

SDL3LIB="$(brew --prefix sdl3 2>/dev/null)/lib/libSDL3.0.dylib"
if [ -f "${SDL3LIB}" ] ; then
  cp -L "${SDL3LIB}" "${CONTENTSDIR}/Frameworks/libSDL3.0.dylib" || {
    echo "Failed to copy libSDL3" >&2
    exit 1
  }
  chmod u+w "${CONTENTSDIR}/Frameworks/libSDL3.0.dylib"
  install_name_tool -id "@executable_path/../Frameworks/libSDL3.0.dylib" \
    "${CONTENTSDIR}/Frameworks/libSDL3.0.dylib"
  # sdl2-compat looks for the unversioned name "libSDL3.dylib"
  # (e.g. @loader_path/libSDL3.dylib), not "libSDL3.0.dylib"
  ln -sf libSDL3.0.dylib "${CONTENTSDIR}/Frameworks/libSDL3.dylib"
else
  echo "Warning: libSDL3.0.dylib not found (brew install sdl3)." >&2
  echo "         The app will most likely fail to start." >&2
fi

# --- Ad-hoc code signing (required on Apple Silicon) ----------------------

while IFS= read -r F ; do
  if file -b "$F" | grep -q 'Mach-O' ; then
    codesign --force -s - "$F"
  fi
done < <(find "${CONTENTSDIR}/Frameworks" "${CONTENTSDIR}/bin" "${CONTENTSDIR}/lib" -type f)

codesign --force --deep -s - "${APPDIR}"

# Sanity check: nothing should reference /opt/homebrew any more
LEFT=$(find "${CONTENTSDIR}/bin" "${CONTENTSDIR}/Frameworks" "${CONTENTSDIR}/lib" -type f -exec sh -c \
  'file -b "$1" | grep -q Mach-O && otool -L "$1" | grep -H --label="$1" "/opt/homebrew"' _ {} \;)
if [ -n "${LEFT}" ] ; then
  echo "Warning: references to /opt/homebrew remain:" >&2
  echo "${LEFT}" >&2
fi

# --- DMG ------------------------------------------------------------------

STAGE="${DISTDIR}/dmg-stage"
mkdir -p "${STAGE}"
cp -R "${APPDIR}" "${STAGE}/"
ln -s /Applications "${STAGE}/Applications"

DMG="${DISTDIR}/Freeciv-${VSTRING}.dmg"
if ! hdiutil create -volname "Freeciv ${VSTRING}" -srcfolder "${STAGE}" \
       -ov -format UDZO "${DMG}" ; then
  echo "Failed to create DMG" >&2
  exit 1
fi
rm -rf "${STAGE}"

echo "Done: ${DMG}"
