#!/bin/sh

YELLOW="$(tput setaf 11)"
GREEN="$(tput setaf 10)"
RED="$(tput setaf 9)"
RESET="$(tput sgr0)"

die () {
  msg="$1"
  code="$2"
  # exit code defaults to 1
  if printf "%s" "$code" | grep -q '^[0-9]+$'; then
    code=1
  fi
  # output message to stdout or stderr based on code
  if [ -n "$msg" ]; then
    if [ "$code" -eq 0 ]; then
      printf "%s\\n" "$msg"
    else
      printf "%s\\n" "$msg" >&2
    fi
  fi
  exit "$code"
}

finish () {
  rm -f "$tmp"
}
trap finish EXIT

parse_input () {
  while getopts "$arg_options" opt; do
    case "$opt" in
      h)
        flag_help=1
        arg_log=0
        ;;
      v)
        flag_version=1
        arg_log=0
        ;;
      d)
        flag_debug=1
        arg_log=0
        ;;
      s)
        arg_ship="$OPTARG"
        ;;
      z)
        flag_shortlist=1
        ;;
      n)
        arg_log=0
        arg_new="$OPTARG"
        ;;
      f)
        arg_log=0
        flag_file=1
        arg_file="$OPTARG"
        ;;
      t)
        flag_title=1
        arg_title="$OPTARG"
        ;;
      *)
        die "Internal error: $1" 3
        ;;
    esac
  done
}

show_help () {
  printf "%s [-hvd] [-s shipname]\\n\\n" "$(basename "$0")"
  printf "  -h                          Show this help\\n"
  printf "  -v                          Show current version info\\n"
  printf "  -d                          Debug mode\\n"
  printf "  -s [shipname]               Only log messages for ship named\\n"
  printf "  -n [filename]               Create new message with filename\\n"
  printf "                              (Users with multiple ships must use -s)\\n"
  printf "\\n"
  printf "Passing no options will enter interactive mode.\\n\\n"
  printf "\\n"
}

yesno () {
  old_stty_cfg=$(stty -g)
  stty raw -echo
  yn=$(while ! head -c 1 | grep -i '[ny]'; do true; done)
  stty "$old_stty_cfg"
  case $yn in
    y ) result=0 ;;
    * ) result=1 ;;
  esac
  return $result
}

check_log () {
  ship="$*"
  printf ">> %s" "${YELLOW}${ship}${RESET}"
  # look at log entries in gophermap
  # compare against files in ship directory
  # store list of unpublished logs
  logs=$(grep "^0${ship}" "/var/gopher/listing.gophermap" | awk -F'\t' '{print $2}')
  files=$(find "/var/gopher/$ship" -regex ".*\\.txt$" -not -path '*/\.*' -type f | sed 's|/var/gopher||')
  uniq=$(printf "%s\\n%s" "$logs" "$files" | sort | uniq -u)

  if [ -z "$uniq" ]; then
    printf " .. No new messages.\\n"
  else
    printf "\\n" # break to new line for each message query
    # check each unpublished message for sending
    IFS='
'
    for u in $uniq
    do
      printf "   Send [%s%s%s]? " "${YELLOW}" "$(basename "$u" | sed 's/\.[^.]*$//')" "${RESET}"
      if yesno; then
        # prompt for title and prepare output
        printf ".. %sYES%s.\\n" "${GREEN}" "${RESET}"
        # check for UTF compatability or fail
        if iconv -f UTF-8 "/var/gopher${u}" > /dev/null 2> /dev/null; then
          if [ "$(wc -L < "/var/gopher${u}")" -gt 80 ]; then
            printf "   ${RED}Warning!${RESET} Message [%s] exceeds 80 columns.\\n" "$(basename "$u" | sed 's/\.[^.]*$//')"
          fi
          get_title
        else
          printf "   Message [%s] is not encoded in UTF-8. %sABORT%s\\n" "$(basename "$u" | sed 's/\.[^.]*$//')" "${RED}" "${RESET}"
        fi
      else
        printf ".. %sNO%s.\\n" "${RED}" "${RESET}"
      fi
    done
    unset IFS
  fi
}

get_title () {
  printf "   Title for [%s%s%s] (null exits): " "${YELLOW}" "$(basename "$u" | sed 's/\.[^.]*$//')" "${RESET}"
  read -r title
  if [ -z "$title" ]; then
    printf "   .. No title. %sABORT%s.\\n" "${RED}" "${RESET}"
    return
  else
    title=$(printf "%s" "$title" | cut -c -60)
    printf "   .. Confirm title [%s%s%s]? " "${YELLOW}" "$title" "${RESET}"
    if yesno; then
      printf ".. %sYES%s.\\n" "${GREEN}" "${RESET}"
      if printf "%s" "$title" | grep -Pq "^[\\x20-\\x7E]*$"; then
        printf "0%s - %s\\t%s\\n" "$ship" "$title" "$u" | cat - /var/gopher/listing.gophermap > "$tmp" && cat "$tmp" > /var/gopher/listing.gophermap && rm "$tmp"
        printf "   .. [%s] .. %sSENT%s.\\n" "$title" "${GREEN}" "${RESET}" 
      else
        printf "   .. Titles must be ASCII characters. %sTry Again%s.\\n" "${RED}" "${RESET}"
        get_title
      fi
    else
      printf ".. %sNO%s.\\n" "${RED}" "${RESET}"
      get_title
    fi
  fi
}

contains() {
  string="$1"
  substring="$2"
  if test "${string#*$substring}" != "$string"
  then
      return 0    # $substring is in $string
  else
      return 1    # $substring is not in $string
  fi
}

new_message () {
  ship="$1"
  post_file="/var/gopher/${ship}/${2}"
  template_file="/var/gopher/${ship}/.template" 
  temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || die "Failed to create temporary file" 1
  if [ -f "$post_file" ]; then
    cp "$post_file" "$temp_post"
  elif [ -f "$template_file" ]; then
    cp "$template_file" "$temp_post"
  fi
  temp_post_time=$(stat -c %Y "$temp_post")
  ${EDITOR:-nano} "$temp_post"
  temp_post_time_check=$(stat -c %Y "$temp_post")
  if [ "$temp_post_time" -ne "$temp_post_time_check" ] ; then
    printf "Drafted message %s (%s)\\n" "$2" "$1"
    touch "${post_file}"
    cat "${temp_post}" > "${post_file}"
    rm "${temp_post}"
  else
    printf "Aborted message.\\n"
    rm "${temp_post}"
  fi
}

main() {

  run_user=$(id -un)
  if [ "$run_user" = "root" ] || [ "$run_user" = "publish" ]; then
    # if running as publish, check who sudoed and create ship for that user
    user="$SUDO_USER"
  else
    user="$run_user"
  fi

  version="0.1.1"
  user="$SUDO_USER"
  ships=$(find /var/gopher -user "$user" -type d -exec basename {} \;)
  numships=$(echo "${ships}" | wc -l)
  tmp=$(mktemp -t "$(basename "$0").tmp.XXXXXXX") || die "Failed to create temporary file" 1

  flag_help=0
  flag_version=0
  flag_debug=0
  flag_shortlist=0
  flag_file=0
  flag_title=0

  arg_options="hvdt:f:s:zn:"
  arg_log=1
  arg_ship=""
  arg_new=""
  arg_file=""
  arg_title=""

  parse_input "$@"

  # debug
  if [ $flag_debug -gt 0 ]; then
    set -x
  fi

  # shortlist for bash completion
  if [ $flag_shortlist -gt 0 ]; then
    out="$ships"
    die "${out}" 0
  fi

  # version
  if [ $flag_version -gt 0 ]; then
    printf "%s\\n" "$version"
  fi

  # print help
  if [ $flag_help -gt 0 ]; then
    show_help
  fi

  # standard log if no params
  if [ $arg_log -gt 0 ]; then
    printf "%sINITIALIZING QEC%s .. %sGOOD%s\\n" "${YELLOW}" "${RESET}" "${GREEN}" "${RESET}"
    printf "Ready to transmit for [%s]\\n" "${GREEN}${user}${RESET}"
    if [ "$numships" -eq 0 ]; then
      printf "No registered ships found in system.\\n"
    elif [ "$numships" -eq 1 ]; then
      if [ -z "$arg_ship" ] || [ "$ships" = "$arg_ship" ]; then
        check_log "$ships"
      fi
    else
      IFS='
'
      for f in $ships
      do
        if [ -z "$arg_ship" ] || [ "$f" = "$arg_ship" ]; then
          check_log "$(basename "$f")"
        fi
      done
      unset IFS
    fi
  fi

  # new message
  if [ -n "$arg_new" ]; then
    if [ "$numships" -eq 0 ]; then
      printf "No registered ships found in system.\\n"
    elif [ "$numships" -eq 1 ]; then
      if [ -z "$arg_ship" ] || [ "$ships" = "$arg_ship" ]; then
        new_message "$ships" "$arg_new"
      fi
    else
      IFS='
'
      for f in $ships
      do
        if [ "$f" = "$arg_ship" ]; then
          new_message "$arg_ship" "$arg_new"
        fi
      done
      unset IFS
    fi
  fi

  # handle -f switch
  if [ $flag_file -gt 0 ]; then
    if [ -r "$arg_file" ]; then
      owner=$(stat -c %U "$arg_file")
      if [ "$user" = "$owner" ]; then
        # if file exists and user owns it
        realfile=$(readlink -f "$arg_file")
        for ship in $ships
        do
          if contains "$realfile" "/var/gopher/${ship}";  then
            if [ "$(wc -L < "$realfile")" -lt 81 ]; then
              # looking for -t titles only for now
              if [ $flag_title -gt 0 ]; then
                if printf "%s" "$arg_title" | grep -Pq "^[\\x20-\\x7E]*$"; then
                  gopherfile="$(printf "%s" "$realfile" | sed 's|/var/gopher||')"
                  printf "0%s - %s\\t%s\\n" "$ship" "$arg_title" "$gopherfile" | cat - /var/gopher/listing.gophermap > "$tmp" && cat "$tmp" > /var/gopher/listing.gophermap && rm "$tmp"
                fi
              fi
            fi
          fi
        done
      fi
    fi
  fi
}

##############################################################################
##############################################################################
##############################################################################

run_user=$(id -un)
if [ "$run_user" = "root" ] || [ "$run_user" = "publish" ]; then
  main "$@"
else
  exec sudo -u publish "$0" "$@"
fi
