#!/bin/bash

# -- Config & Setup --
# Destination file
if [[ -z "$1" ]]; then
  echo "Usage: mapper [gamecontrollerdb.txt]"
  exit -1
fi

MAPPERDIR="$(dirname "$0")"
CONTROLLER_DB="$1"
if [[ ! -f "${CONTROLLER_DB}" ]]; then
  echo "File ${CONTROLLER_DB} does not exist."
  exit -1
fi

# Where the emulationstation configuration file is
ES_CONFIG="${HOME}/.config/emulationstation/es_input.cfg"

if [ -f "${HOME}/configs/emulationstation/es_input.cfg" ]; then
  # Batocera / Knulli
  ES_CONFIG="${HOME}/configs/emulationstation/es_input.cfg"
fi

# -- Helper function --
# Pre Map
function pre_map {
  INPUT_NAME=$1
  TYPE=$2
  ID=$3
  VALUE=$4

  map_x_result=""
  INVERT_VALUE=""
  if [ "$VALUE" = "-1" ]; then
    INVERT_VALUE="1"
  elif [ "$VALUE" = "1" ]; then
    INVERT_VALUE="-1"
  fi

  case "${INPUT_NAME}" in
    "joystick1left")
      map "leftanalogleft" "$TYPE" "$ID" "$VALUE"
      map "leftanalogright" "$TYPE" "$ID" "$INVERT_VALUE"
      ;;
    "joystick1up")
      map "leftanalogup" "$TYPE" "$ID" "$VALUE"
      map "leftanalogdown" "$TYPE" "$ID" "$INVERT_VALUE"
      ;;
    "joystick2left")
      map "rightanalogleft" "$TYPE" "$ID" "$VALUE"
      map "rightanalogright" "$TYPE" "$ID" "$INVERT_VALUE"
      ;;
    "joystick2up")
      map "rightanalogup" "$TYPE" "$ID" "$VALUE"
      map "rightanalogdown" "$TYPE" "$ID" "$INVERT_VALUE"
      ;;
    *)
      map "$INPUT_NAME" "$TYPE" "$ID" "$VALUE"
      ;;
  esac
}

# Map the actual button/hat/axis
function map {
  INPUT_NAME=$1
  TYPE=$2
  ID=$3
  VALUE=$4

  case "${INPUT_NAME}" in
    "b")                TR_NAME="a";;
    "a")                TR_NAME="b";;
    "y")                TR_NAME="x";;
    "x")                TR_NAME="y";;
    "hotkeyenable")     TR_NAME="guide";;
    "hotkey")           TR_NAME="guide";;
    "up")               TR_NAME="dpup";;
    "down")             TR_NAME="dpdown";;
    "left")             TR_NAME="dpleft";;
    "right")            TR_NAME="dpright";;
    "leftshoulder")     TR_NAME="leftshoulder";;
    "l2")               TR_NAME="leftshoulder";;
    "leftthumb")        TR_NAME="leftstick";;
    "l3")               TR_NAME="leftstick";;
    "lefttrigger")      TR_NAME="lefttrigger";;
    "rightshoulder")    TR_NAME="rightshoulder";;
    "rightthumb")       TR_NAME="rightstick";;
    "righttrigger")     TR_NAME="righttrigger";;
    "select")           TR_NAME="back";;
    "start")            TR_NAME="start";;
    "leftanalogup")     TR_NAME="-lefty";;
    "leftanalogleft")   TR_NAME="-leftx";;
    "leftanalogdown")   TR_NAME="+lefty";;
    "leftanalogright")  TR_NAME="+leftx";;
    "rightanalogup")    TR_NAME="-righty";;
    "rightanalogleft")  TR_NAME="-rightx";;
    "rightanalogdown")  TR_NAME="+righty";;
    "rightanalogright") TR_NAME="+rightx";;
    *)
      echo "Invalid mapping ${INPUT_NAME}."
      return
      ;;
  esac

  echo "${INPUT_NAME} -> ${TR_NAME}"

  case "${TYPE}" in
  "axis")
    if (( $VALUE < 0 )); then
      map_x_result="${TR_NAME}:${map_x_result}-a${ID},"
    else
      # Most (save for a few misbehaved children...) triggers are [0, 1] instead of [-1, 1]
      # Shitty workaround for an emulationstation issue
      if [[ $INPUT_NAME =~ .*"trigger" ]]; then
        map_x_result="${TR_NAME}:${map_x_result}a${ID},$map_x_result"
      else
        map_x_result="${TR_NAME}:${map_x_result}+a${ID},$map_x_result"
      fi
    fi
    ;;
  "button")
    map_x_result="${TR_NAME}:${map_x_result}b${ID},$map_x_result"
    ;;
  "hat")
    map_x_result="${TR_NAME}:${map_x_result}h${ID}.${VALUE},$map_x_result"
    ;;
  *)
    echo "Invalid entry ${TYPE}"
    ;;
  esac
}


function get_map_suffix {
  map_suffix="platform:Linux,"
}

function get_map_prefix {
  map_prefix="${GUID},${NAME},"
}

# query controllers mapped in emulationstation, ignore devices without a GUID
ES_QUERY="$($MAPPERDIR/xmlstarlet sel -T -t -m "inputList/inputConfig[@deviceGUID!='']" -n -v "concat(@deviceName,';',@deviceGUID)" $ES_CONFIG)"
printf "\n# Custom Entries\n" >> "${CONTROLLER_DB}"

echo "## ES Dev Mapper ##"
while IFS=";" read -r NAME GUID; do
  echo "$NAME :: $GUID"
  # Ignore keyboards
  if [[ "${GUID}" == -1 ]]; then
    continue
  fi

  # Check if GUID exists in gamecontrollerdb.txt
  if [ -z "$(fgrep -- ${GUID} "${CONTROLLER_DB}")" ]; then
    # Query this specific GUID on the mappings
    MAPPING_CFG=$($MAPPERDIR/xmlstarlet sel -T -t -m "//inputConfig[@deviceGUID = '${GUID}']/input" -n -v "concat(@name,';',@type,';',@id,';',@value)" $ES_CONFIG)

    MAPPING=""
    while IFS=";" read -r -e INPUT_NAME TYPE ID VALUE; do
      # Map the controller
      pre_map "${INPUT_NAME}" "${TYPE}" "${ID}" "${VALUE}"

      # Only concatenate valid mappings
      if [[ ! -z ${map_x_result} ]]; then
        MAPPING="${MAPPING}${map_x_result}"
      fi
    done <<< ${MAPPING_CFG:1}

    get_map_prefix
    get_map_suffix
    if [[ ! -z "${MAPPING}" ]]; then
      echo "${map_prefix}${MAPPING}${map_suffix}" >> "${CONTROLLER_DB}"
      echo "${map_prefix}${MAPPING}${map_suffix}"
      echo ""
    else
      echo "Failed to map anything."
      echo ""
    fi
  else
    echo "Already mapped..."
  fi
done <<< ${ES_QUERY:1}
