#!/bin/bash

# Exit on unset variable.
set -u

print_help() {
    cat << EOF
Usage: ${0##*/} [-hq]
Collect debug logs.
  -h Display this help and exit.
  -q Run in silent mode without prompting user for inputs.
EOF
}

#######################################
# Prints a string if not in silent mode.
# Globals:
#   FLAG_SILENT_MODE
# Arguments:
#   printf arguments
# Outputs:
#   Writes a formatted string to stdout.
#######################################
print_info() {
  if [[ "$FLAG_SILENT_MODE" != "true" ]]; then
    echo "$@"
  fi
}

FLAG_SILENT_MODE=false

# Parse command-line arguments.
while getopts "hq" opt; do
    case $opt in
        h)
            print_help
            exit
            ;;
        q)
            FLAG_SILENT_MODE=true
            ;;
        *)
            print_help >&2
            exit 1
        # Add more options in the future.
    esac
done

if (( "${EUID}" != 0 )); then
  echo "This script requires root privileges." >&2
  echo "Please re-run with sudo:" >&2
  echo "  sudo ${0}" >&2
  exit 1
fi

LOG_FILE=$(mktemp)
print_info "Writing diagnostic logs to $LOG_FILE"

{
  echo "TinyPilot log dump"
  echo "https://tinypilotkvm.com"
  printf "Timestamp: %s" "$(date --iso-8601=seconds)"
  printf "\n\n"
} >> "${LOG_FILE}"

echo "Software versions" >> "${LOG_FILE}"

print_info "Checking TinyPilot version..."
cd /opt/tinypilot && \
  printf 'TinyPilot version: %s\n' "$(cat VERSION)" >> "${LOG_FILE}"

print_info "Checking uStreamer version..."
cd /opt/ustreamer && \
  printf "uStreamer version: %s\n\n" "$(/opt/ustreamer/ustreamer --version)" \
    >> "${LOG_FILE}"

echo "System information" >> "${LOG_FILE}"

print_info "Checking system information..."
{
  printf "OS version: %s\n" "$(uname --all)"
  printf "Kernel architecture: %s\n" "$(uname --machine)"
  printf "Userland architecture: %s (%s)\n" "$(dpkg --print-architecture)" \
    "$(getconf LONG_BIT)-bit"
  printf "Distribution name: %s\n" "$(lsb_release --id --short)"
  printf "Distribution version: %s\n" "$(lsb_release --release --short)"
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking hardware information..."
{
  echo "Hardware information"
  grep "^Hardware\|^Revision\|^Model" /proc/cpuinfo | sed "s/\s*:\s*/: /g"
  printf "\n"
} >> "${LOG_FILE}"

echo "TinyPilot state" >> "${LOG_FILE}"

print_info "Checking if filesystem is read-only..."
{
  READ_ONLY_FILESYSTEM="off"
  if grep --quiet "boot=overlay" /proc/cmdline ; then
    READ_ONLY_FILESYSTEM="on"
  fi
  readonly READ_ONLY_FILESYSTEM
  echo "Read-only filesystem: ${READ_ONLY_FILESYSTEM}"
} >> "${LOG_FILE}"

print_info "Checking if SSH is enabled..."
{
  SSH_STATUS="disabled"
  if runuser tinypilot --command '/opt/tinypilot/scripts/is-ssh-enabled' ; then
    SSH_STATUS="enabled"
  fi
  readonly SSH_STATUS
  echo "SSH access: ${SSH_STATUS}"
} >> "${LOG_FILE}"

print_info "Checking network interfaces..."
{
  echo "Network interfaces:"
  readonly INTERFACES_ROOT="/sys/class/net"
  for interface_path in "${INTERFACES_ROOT}"/* ; do
    ifname="${interface_path##*/}"
    echo "  ${ifname} ($(cat ${INTERFACES_ROOT}/"${ifname}"/operstate))"
  done
} >> "${LOG_FILE}"

print_info "Checking if mouse jiggler is enabled..."
{
  MOUSE_JIGGLER_STATUS="disabled"
  if [[ -e /etc/cron.d/tinypilot-mouse-jiggle ]]; then
    MOUSE_JIGGLER_STATUS="enabled"
  fi
  readonly MOUSE_JIGGLER_STATUS
  echo "Mouse jiggler: ${MOUSE_JIGGLER_STATUS}"
} >> "${LOG_FILE}"

print_info "Checking status of USB data connection (HID)..."
{
  echo "USB data connected: $(runuser tinypilot --command '/opt/tinypilot/scripts/check-usb-data-status')"
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking storage..."
{
  # Get the available space and total size of the root directory.
  DF_OUTPUT="$(df --output=avail,size --human-readable / | tail -1)"
  readonly DF_OUTPUT
  AVAILABLE_DISK_SPACE="$(echo "${DF_OUTPUT}" | awk '{print $1}')"
  readonly AVAILABLE_DISK_SPACE
  TOTAL_DISK_SIZE="$(echo "${DF_OUTPUT}" | awk '{print $2}')"
  readonly TOTAL_DISK_SIZE
  echo "Disk space: ${AVAILABLE_DISK_SPACE}iB free (of ${TOTAL_DISK_SIZE}iB)"
} >> "${LOG_FILE}"

print_info "Checking temperature..."
{
  vcgencmd measure_temp | sed 's/temp=/CPU Temperature: /g'
} >> "${LOG_FILE}"

print_info "Checking throttled state..."
{
  CPU_THROTTLED="yes"
  if vcgencmd get_throttled | grep --line-regexp --quiet "throttled=0x0" ; then
    CPU_THROTTLED="no"
  fi
  readonly CPU_THROTTLED
  echo "CPU throttled since boot: ${CPU_THROTTLED}"
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking for voltage issues..."
{
  printf "Voltage issues: "
  if LAST_VOLTAGE_LINE="$(journalctl --quiet --reverse --lines=1 --grep='voltage')" ; then
    printf "yes - %s" "${LAST_VOLTAGE_LINE}"
  else
    printf "no"
  fi
  printf "\n\n"
} >> "${LOG_FILE}"

print_info "Checking TinyPilot streaming mode..."
{
  echo "Streaming mode"
  echo "Selected mode: $(runuser tinypilot --command '/opt/tinypilot/scripts/cli streaming-mode')"
  printf "Current mode: "
  # H264 mode is considered active when the last Janus video log line contains
  # "Memsink opened; reading frames".
  # https://github.com/tiny-pilot/ustreamer/blob/89fd83bfc187ed592e9582be077fa1a005098532/janus/src/plugin.c#L148
  if LAST_JANUS_VIDEO_LINE="$(journalctl --unit janus.service --quiet --reverse --lines 1 --grep 'ustreamer/video')" &&
      [[ "${LAST_JANUS_VIDEO_LINE}" =~ 'Memsink opened; reading frames' ]]; then
    printf "H264"
  else
    printf "MJPEG"
  fi
  printf "\n\n"
} >> "${LOG_FILE}"

print_info "Checking status of HDMI capture chip..."
{
  echo "HDMI capture chip status"
  HAS_POWER="$(/opt/tinypilot-privileged/scripts/check-hdmi-status --power)"
  HAS_SIGNAL="$(/opt/tinypilot-privileged/scripts/check-hdmi-status --signal)"
  echo "Power: ${HAS_POWER:-n/a}"
  echo "Signal: ${HAS_SIGNAL:-n/a}"
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking TinyPilot settings..."
{
  printf "TinyPilot settings.yml\n"
  cat /home/tinypilot/settings.yml
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking TinyPilot configuration..."
{
  printf "TinyPilot configuration\n"
  cat /lib/systemd/system/tinypilot.service
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking TinyPilot logs..."
{
  printf "TinyPilot logs\n"
  journalctl --unit tinypilot | tail --lines 200
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking TinyPilot updater logs..."
{
  printf "TinyPilot update logs\n"
  "$(dirname "$0")/read-update-log" | tail --lines 200
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking uStreamer configuration..."
{
  printf "uStreamer configuration\n"
  tail --lines +1 /opt/ustreamer-launcher/configs.d/*
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking uStreamer logs..."
{
  printf "uStreamer logs\n"
  journalctl --unit ustreamer | tail --lines 80
  printf "\n"
} >> "${LOG_FILE}"

print_info "Checking nginx logs..."
{
  echo "nginx logs"
  journalctl --unit nginx
  printf "\n\n"
  tail --lines 100 /var/log/nginx/error.log
  printf "\n\n"
  tail --lines 30 /var/log/nginx/access.log
  printf "\n"
} >> "${LOG_FILE}"

print_info "Your log:"
print_info ""
cat "${LOG_FILE}"
print_info "-------------------------------------"
print_info ""
print_info ""

SHOULD_UPLOAD=false
if [[ "$FLAG_SILENT_MODE" != "true" ]]; then
    echo -n "Upload your log file? You can review it above to see what information it contains (y/n)? "
    read -r answer
    printf "\n"
    if [ "$answer" != "${answer#[Yy]}" ]; then
        SHOULD_UPLOAD=true
    fi
fi

readonly LOG_UPLOAD_URL="https://logs.tinypilotkvm.com"

if [[ "$SHOULD_UPLOAD" == "true" ]]; then
    URL=$(curl --silent --show-error --form "_=@${LOG_FILE}" "${LOG_UPLOAD_URL}")
    printf "Copy the following URL into your bug report:\n\n\t"
    printf "%s\n\n" "${URL}"
else
    print_info "Log file not uploaded."
    print_info "If you decide to share it, run:"
    print_info ""
    print_info "  curl --form \"_=@${LOG_FILE}\" ${LOG_UPLOAD_URL}"
    print_info ""
fi
