#!/bin/bash

#set -x

# standard FWD interactive client startup script

# defaults
whole_path="$(cd "$(dirname "$0")"; pwd)/$(basename "$0")"
srcname=$(basename "$whole_path")
srcpath=$(dirname "$whole_path")
[ ! -f ${srcpath}/appname ] && echo "FATAL: No appname to source in $srcpath" && exit
[ -z "$appname" ] && . ${srcpath}/appname
suspend="n"
prog="java"
dump="-XX:+HeapDumpOnOutOfMemoryError"
#dump="-XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='./stack_dump.sh %p'"
host="net:server:host=localhost"
batch_parms="client:mode:batch=true client:driver:background=true"
driver_type="gui"
instance=0
portbase=3333
secure=false
portstep=100
maxheap=""
# Set this to debug the ssl handshake and include in jvm parms
#ssl_debug="-Djavax.net.debug=ssl:handshake"

# Determine the configured JDK
#jver is 18 for java 1.8, 15 for java 1.5, 110 for java 11, 170 for java 17 etc.
jver=$(${prog} -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F '.' '{sub("^$", "0", $2); print $1$2}')

# Ensure UTF-8 is set
ensure_utf8_lang()
{
   # Get the current LANG setting and encoding portion (after the dot '.')
   current_lang="${LANG}"
   encoding="${current_lang##*.}"

   shopt -s nocasematch
   # Check if the encoding is not UTF-8
   if [[ ! "$encoding" =~ ^utf-?8$ ]]; then
      # Warn the user
      echo "Warning: The current LANG setting (${current_lang}) is not using UTF-8 encoding."

      # Set LANG to LANGUAGE.UTF-8, or fallback to en_US.UTF-8 if LANGUAGE is not set
      [ -z "$LANGUAGE" ] && new_lang="en_US.UTF-8" || new_lang="${LANGUAGE}.UTF-8"

      # Apply the new LANG setting
      export LANG="$new_lang"

      # Inform the user
      echo "LANG has been updated to: ${LANG}"
   fi
   shopt -u nocasematch
}

# help text
show_usage()
{
   i=0;        usage[$i]="\nUsage: $0 [-d<port>] [-s] [-c<cfgfile>] [-i<instance>] [-m<path>] [-2] [-b] [-k<password>] [-t<password>] [-p<procedure>] [-x<heap>] [-o<cmd-line-option:params>] [--test] [--wb] [--swing] [--terminal] \n"
   i=$((i+1)); usage[$i]="Where:\n"
   i=$((i+1)); usage[$i]="d \t= enable JVM debug mode, use the specified TCP port"
   i=$((i+1)); usage[$i]="s \t= in JVM debug mode enable, suspend the JVM on startup (does not suspend by default)"
   i=$((i+1)); usage[$i]="h \t= hostname or IP address for the server (defaults to localhost)"
   i=$((i+1)); usage[$i]="i \t= server instance number (0..9, assigns the FWD server port, defaults to 0) to which to connect"
   i=$((i+1)); usage[$i]="m \t= Enable heap dump on OOME to given path"
   i=$((i+1)); usage[$i]="c \t= config file name (FWD bootstrap config file name, defaults to standard_client.xml)"
   i=$((i+1)); usage[$i]="2 \t= enable C2 HotSpot compiler (server compiler for the JVM)"
   i=$((i+1)); usage[$i]="b \t= run with client in batch mode (forces --terminal)"
   i=$((i+1)); usage[$i]="k \t= password for keystore"
   i=$((i+1)); usage[$i]="t \t= password for trust"
   i=$((i+1)); usage[$i]="p \t= procedure to run"
   i=$((i+1)); usage[$i]="x \t= set JVM max heap size to <heap> (must be an integer number of MB)"
   i=$((i+1)); usage[$i]="o \t= options (params) to pass to cmd-line-option:params. Make sure this is the last on the command line, and in \"\""
   i=$((i+1)); usage[$i]="--test \t\t= test mode (displays the command but doesn't execute)"
   i=$((i+1)); usage[$i]="--wb \t\t= enable widget browser for debug purposes"
   i=$((i+1)); usage[$i]="--swing \t= use Swing client"
   i=$((i+1)); usage[$i]="--terminal\t= use ChUI driver"
   i=$((i+1)); usage[$i]="--no-appcds\t= disable AppCDS: full classpath discovery, no shared archive (pre-AppCDS behavior)"

   for i in "${usage[@]}"; do
      echo -e $i
   done
}

# process options
while getopts "?d:sh:i:m:c:2bk:t:p:o:x:-:" opt; do
   case $opt in
      d  ) debug=true
           dport=$OPTARG ;;
      s  ) suspend="y" ;;
      h  ) host="net:server:host="$OPTARG ;;
      i  ) instance=$OPTARG ;;
      m  ) dump+=" -XX:HeapDumpPath=${OPTARG}" ;;
      c  ) cfg=$OPTARG ;;
      2  ) srvr="-server" ;;
      b  ) batch="${batch_parms}"
           use_terminal=true ;;
      k  ) kpasswd=$OPTARG ;;
      t  ) tpasswd=$OPTARG ;;
      p  ) procedure=$OPTARG ;;
      x  ) maxheap="-Xmx"${OPTARG}"m" ;;
      o  ) # Save the remaining command-line arguments into 'params'
           shift $((OPTIND - 2))  # Shift to the first argument after '-o'
           params=$*              # Capture all remaining text
           break                  # Exit the loop as we are done
           ;;
      -  ) case ${OPTARG} in
              test     ) test=true ;;
              wb       ) wb="-Dwidget.browser=true" ;;
              swing    ) use_swing=true ;;
              terminal ) use_terminal=true
                         driver_type="chui" ;;
              no-appcds ) no_appcds=true ;;
           esac ;;
      \? ) show_usage
           exit 1 ;;
   esac
done
shift $(($OPTIND - 1))

# Determine driver and token
if [ "$use_swing" == true ]; then
   client_driver_type="client:driver:type=${driver_type}_swing"
   if [ "$use_terminal" == true ]; then
      client_driver_type+=" client:chui:rows=24 client:chui:columns=80 client:chui:background=0x000000 client:chui:foreground=0xFFA500 client:chui:selection=0x0000FF client:chui:fontname=monospaced client:chui:fontsize=12"
   fi
else
   if [ "$use_terminal" == true ]; then
      client_driver_type="client:driver:type=${driver_type}_native"
   else
      client_driver_type="client:driver:type=${driver_type}_swing"
   fi
fi
token="client:project:token=${driver_type}"
if [ -n "$batch" ]; then
   secure=true
   unset client_driver_type
fi

# pass the remaining command line arguments directly to the java driver
# WARNING: any arguments with embedded spaces are not passed properly; for
#          example, if you pass this to the script:
#             client:cmd-line-option:parameter="\"\\\"some text here\\\"\""
#          you would expect the following to be passed to the java driver:
#             client:cmd-line-option:parameter="\"some text here\""
#          but that does not happen; when running the driver directly this would
#          work fine; via the script the result is that the driver sees add'l 
#          parameters, which will cause it to think it has add'l bootstrap config
#          files to load 
if [ -z "$params" ]; then
   remainder="$@"
fi

# final arg prep
sport=$(($portbase + $instance))
iport=$(($portbase + $instance + $portstep))
port="net:connection:secure=${secure} net:server:secure_port=$sport net:server:insecure_port=$iport"

# Prevent NIO sockets
nio="net:socket:nio=false"

if [ "$debug" == true ]; then
   [ -f "/.dockerenv" ] && daddress="0.0.0.0:${dport}" || daddress="${dport}"
   dtxt="-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspend},address=${daddress}"
fi

if [ -n "$cfg" ]; then
   # Path could be relative to the srcpath, so calculate it and test existence.
   p=$(cd $(dirname ${srcpath}/${cfg}) 2>/dev/null; echo $(pwd))
   f=$(basename $cfg)
   [ -f "${p}/${f}" ] && cfg="${p}/${f}"
   token=""
   host=""
fi

# Server with store   
if [ -n "$tpasswd" ]; then
   tpasswd="ssl-socket:truststore:password=${tpasswd}"
fi   
# Connect with trust
if [ -n "$kpasswd" ]; then
   kpasswd="ssl-socket:keystore:password=${kpasswd}"
fi

# Pass in: client:cmd-line-option:startup-procedure=<file.p>
if [ -n "$procedure" ]; then
   procedure="client:cmd-line-option:startup-procedure=${procedure}"
fi

# Pass in options (params): client:cmd-line-option:parameter=<params>
if [ -n "$params" ]; then
   params="'client:cmd-line-option:parameter=${params}'"
fi

# getcp.sh and getspi.sh are helpers for setting up the classpath and spi for the given application. 
# This allows more flexibility in where the FWD configuration is placed. It can be one directory up,
# and then in lib, or in /opt/fwd/lib, or specified by FWD_LIB exported or up to the build directory,
# then down to lib. If the helper isn't found, we can only fall back to the original method.
helper=$(cd ${srcpath}/..; echo $(pwd))
export PATH=$helper:$PATH
if command -v getcp.sh >/dev/null 2>&1; then
   # --appcds tells getcp.sh to prefer the curated client classpath published by
   # deploy_appcds.sh (appcds/client/<type>.cpath), matching the AppCDS archive.
   # It falls back to the normal FWD library discovery when no curated file
   # exists, so this is safe on deployments without AppCDS. With --no-appcds we
   # omit the flag so getcp.sh does full FWD library discovery (pre-AppCDS behavior).
   appcds_flag="--appcds"
   [ "$no_appcds" == true ] && appcds_flag=""
   cpath=$(getcp.sh $appname $srcpath $appcds_flag)
   lpath="-Djava.library.path="$(getspi.sh "$cpath" "p2j.jar")
   cpath="-classpath $cpath"
else
   # getcp.sh not available: build the classpath here, but still honor AppCDS so the
   # archive can map. Mirror getcp.sh --appcds: if a curated <type>.cpath exists (and
   # AppCDS isn't disabled) use it verbatim so the classpath matches the dumped
   # archive; otherwise fall back to globbing ../lib.
   appcds_type="default"
   appcds_cpath=""
   if [ "$no_appcds" != true ]; then
      cand="${srcpath}/../appcds/client/${appcds_type}.cpath"
      [ -f "$cand" ] && appcds_cpath="$(cat "$cand")"
   fi
   if [ -n "$appcds_cpath" ]; then
      cpath="$appcds_cpath"
   else
      cpath="../lib/p2j.jar"
      for each in ../lib/*.jar; do
         [ "$each" = "../lib/p2j.jar" ] || cpath="$cpath:$each"
      done
   fi
   lpath="-Djava.library.path=../lib/"
   cpath="-classpath $cpath"
fi

# Enable AppCDS for the client when a curated archive is present. This is the
# complement to the curated classpath above: -XX:SharedArchiveFile points the JVM
# at the pre-parsed class archive built by deploy_appcds.sh, while the classpath
# (from getcp.sh --appcds) determines which jars are actually loaded. They are
# independent JVM inputs -- the archive only accelerates classes that are also on
# the classpath, and -Xshare:auto silently ignores the archive if the runtime
# classpath does not match the one it was dumped against. This mirrors the
# webClient jvmArgs in directory.xml and the cdsparams assembled in server.sh.
appcds_type="default"
cdsparams=""
if [ "$no_appcds" != true ]; then
   cand="${srcpath}/../appcds/client/${appcds_type}.jsa"
   if [ -f "$cand" ]; then
      [ "$debug" == true ] && p="-Xshare:on -Xlog:class+path=info" || p="-Xshare:auto"
      cdsparams="${p} -XX:SharedArchiveFile=$cand"
   fi
fi

# Ensure UTF-8 encoding
original_lang="${LANG}"
ensure_utf8_lang

# run the FWD client
java_args="$srvr $maxheap $dump $wb $dtxt $lpath $cpath $cdsparams"

# Compatibility with Java 25
if [ $jver -ge 240 ]; then
   java_args+=" --enable-native-access=ALL-UNNAMED" 
fi

client_driver_args="$kpasswd $tpasswd $token $host $nio $port $client_driver_type $cfg $batch $procedure $params $remainder"
cmd="$prog $java_args com.goldencode.p2j.main.ClientDriver $client_driver_args"
if [ "$test" == "true" ]; then
   echo $cmd
else
   eval $cmd
fi

# Restore the original LANG setting, if sourced ad set
[ "${BASH_SOURCE[0]}" != "${0}" ] && [ ."$original_lang". != ".." ] && export LANG="$original_lang"

