#!/bin/bash
# TinyFleet for macOS: the executable inside TinyFleet.app (scripts/package.py builds the bundle).
# Serves the game in Contents/Resources/game on localhost and opens it in a chromeless Chrome/Edge
# window, or the default browser. Nothing to install: it uses the perl that ships with macOS,
# falling back to python3. The port is fixed so saves (localStorage) survive between launches
# and between versions.

PORT=47615
URL="http://127.0.0.1:$PORT/"
# the game is played from the online server (always the newest), this copy only when it can't be reached
LAUNCH="${URL}launch.html"
GAME="$(cd "$(dirname "$0")/../Resources/game" && pwd)"
PROFILE="$HOME/Library/Application Support/TinyFleet/browser-profile"

dialog() { # message, button
  osascript -e "display dialog \"$1\" with title \"TinyFleet\" buttons {\"$2\"} default button 1 with icon note" >/dev/null 2>&1
}

up() { curl -s -o /dev/null --max-time 1 "$URL"; }

browser() {
  for app in "Google Chrome" "Microsoft Edge" "Brave Browser" "Chromium"; do
    for dir in "/Applications" "$HOME/Applications"; do
      local bin="$dir/$app.app/Contents/MacOS/$app"
      if [ -x "$bin" ]; then echo "$bin"; return; fi
    done
  done
}

# Opens a game window. Succeeds only once that window has closed; fails if we can't tell when it
# does (no Chrome-type browser, or one already running on the profile took the window over).
play() {
  local bin; bin="$(browser)"
  if [ -n "$bin" ]; then
    mkdir -p "$PROFILE"
    local opened=$SECONDS
    "$bin" --app="$LAUNCH" --user-data-dir="$PROFILE" --no-first-run --no-default-browser-check \
      --disable-features=Translate --window-size=1600,900 --start-maximized >/dev/null 2>&1
    [ $((SECONDS - opened)) -ge 5 ]
    return
  fi
  open "$LAUNCH"
  return 1
}

# Another copy is already serving the game: just open another window onto it.
if up; then play; exit 0; fi

if [ ! -f "$GAME/index.html" ]; then
  dialog "Couldn't find the game files inside TinyFleet.app. Please download it again." "OK"
  exit 1
fi

if [ -x /usr/bin/perl ]; then
  /usr/bin/perl -e '
    use strict; use IO::Socket::INET;
    my ($root, $port) = @ARGV;
    my $srv = IO::Socket::INET->new(LocalAddr => "127.0.0.1", LocalPort => $port,
      Listen => 32, ReuseAddr => 1, Proto => "tcp") or die "listen: $!\n";
    my %mime = (html => "text/html; charset=utf-8", js => "text/javascript; charset=utf-8",
      mjs => "text/javascript; charset=utf-8", css => "text/css; charset=utf-8",
      json => "application/json", svg => "image/svg+xml", png => "image/png", jpg => "image/jpeg",
      jpeg => "image/jpeg", webp => "image/webp", gif => "image/gif", ico => "image/x-icon",
      wasm => "application/wasm", woff => "font/woff", woff2 => "font/woff2", ttf => "font/ttf",
      mp3 => "audio/mpeg", ogg => "audio/ogg", wav => "audio/wav", glb => "model/gltf-binary",
      gltf => "model/gltf+json", txt => "text/plain; charset=utf-8");
    $SIG{CHLD} = "IGNORE";
    while (1) {
      my $c = $srv->accept or next;
      if (fork) { close $c; next; }
      close $srv;
      my $line = <$c> // exit 0;
      while (my $h = <$c>) { last if $h =~ /^\r?\n$/; }
      my ($method, $path) = $line =~ m{^(GET|HEAD) (\S+)} or do {
        print $c "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"; exit 0;
      };
      $path =~ s/[?#].*//; $path =~ s/%([0-9A-Fa-f]{2})/chr hex $1/ge;
      $path = "/index.html" if $path eq "/";
      my $file = $root . $path;
      $file .= "/index.html" if -d $file;
      if ($path =~ m{(^|/)\.\.(/|$)} || !-f $file || !open(my $fh, "<:raw", $file)) {
        print $c "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"; exit 0;
      } else {
        my ($ext) = $file =~ /\.([^.\/]+)$/;
        my $type = $mime{lc($ext // "")} // "application/octet-stream";
        my $size = -s $file;
        binmode $c;
        print $c "HTTP/1.1 200 OK\r\nContent-Type: $type\r\nContent-Length: $size\r\n" .
          "Cache-Control: no-store\r\nConnection: close\r\n\r\n";
        if ($method eq "GET") { local $/ = \65536; while (my $buf = <$fh>) { print $c $buf; } }
      }
      exit 0;
    }
  ' "$GAME" "$PORT" &
elif xcode-select -p >/dev/null 2>&1 && command -v python3 >/dev/null 2>&1; then
  # /usr/bin/python3 is only a stub that offers to install developer tools unless they are there.
  python3 -m http.server "$PORT" --bind 127.0.0.1 --directory "$GAME" >/dev/null 2>&1 &
else
  dialog "TinyFleet needs perl or python3 to run, and this Mac has neither." "OK"
  exit 1
fi
SERVER=$!
trap 'kill $SERVER 2>/dev/null' EXIT

for _ in $(seq 1 50); do up && break; sleep 0.1; done
if ! up; then
  dialog "Couldn't start the local game server on port $PORT." "OK"
  exit 1
fi

if ! play; then
  dialog "TinyFleet is running in your browser at $URL\n\nPress Stop to stop the game." "Stop"
fi
