#! /usr/bin/bash

# Global vars/settings
command_name=$(basename $0)
rapl_path="/sys/class/powercap/intel-rapl:0"
pl1_path="${rapl_path}/constraint_0_power_limit_uw"
pl2_path="${rapl_path}/constraint_1_power_limit_uw"
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
gui_name="GPD TDP Manager"
gui_options=""

# Set up help text
help_text="${command_name}: GDP Win 3 TDP management script

This script checks and sets the TDP using intel_rapl

Usage:
  ${command_name} COMMAND [ARGUMENTS]

Command:
  check [ARGS]     Checks current TDP and prints it in watts
  c [ARGS]         Shortened version of check

  set WATTS [ARGS] Sets TDP to the requested number in watts
  s WATTS [ARGS]   Shortened version of set
  
  gui              Runs graphical user interface

  help             Prints this help text
  COMMAND --help   Prints help for specified command"

check_help_text="${command_name} check [ARG]
Short command: c

Example:
  ${command_name} check --detail
  ${command_name} c -d

Arguments:
  --detail, -d     Prints PL2 as well as PL1
  --help           Prints this help text"

set_help_text="${command_name} set WATT [ARGUMENTS]
Short command: s

Example:
  ${command_name} set 10 --detail --same
  ${command_name} s 10 -d -s

Arguments:
  --detail, -d     Prints PL2 as well as PL1
  --same, -s       PL2 will be set to the same as PL1 rather than 2W higher
  --help           Prints this help text"
  
gui_help_text="${command_name} gui

No help is available for the GUI.
If it does not work, ensure the \"zenity\" command is installed and available"

print_help () {
  case $1 in
    "check")
      echo "$check_help_text"
      ;;
    "set")
      echo "$set_help_text"
      ;;
    "gui")
      echo "$gui_help_text"
      ;;
    *)
      echo "$help_text"
      ;;
  esac
}

print_unknown () {
  echo "Unknown command or incorrect arguments.
  Try \"${command_name} help\" or \"${command_name} COMMAND --help\""
}
# End of help text

# Conversion tools
uw_to_w () {
  if ! [[ $1 =~ ^-?[0-9]+$ ]]; then
    exit
  else
    echo $(expr $1 / 1000000)
  fi
}

w_to_uw () {
  if ! [[ $1 =~ ^-?[0-9]+$ ]]; then
    exit
  else
    echo $(expr $1 '*' 1000000)
  fi
}

# Common checks/tasks
is_detailed () {
  if [[ "$@" == *"--detail"* ]] || [[ "$@" == *"-d"* ]]; then
    exit 0
  else
    exit 1
  fi
}

is_help () {
  if [[ "$@" == *"--help"* ]] || [[ "$@" == *"-h"* ]]; then
    exit 0
  else
    exit 1
  fi
}

is_number () {
  if ! [[ $1 =~ ^-?[0-9]+$ ]]; then
    exit 1
  else
    exit 0
  fi
}

gui_msg () {
  zenity --info --title="$gui_name" $gui_options --text="$1" --no-wrap
}

gui_read () {
  zenity --entry --title="$gui_name" $gui_options --text="$1:"
}

gui_ask () {
  if $(zenity --question --title="$gui_name" $gui_options --text="$1" --no-wrap); then
    exit 0
  else
    exit 1
  fi
}

set_pl () {
  if [ $1 -eq 1 ] || [ $1 -eq 2 ] || [ ! -z $2 ]; then
    if [ $1 -eq 1 ]; then
      pl_path=$pl1_path
    elif [ $1 -eq 2 ]; then
      pl_path=$pl2_path
    fi
    if [ "$EUID" -eq 0 ]; then
      echo $(w_to_uw $2) > $pl_path
    elif [ $gui_used == 1 ]; then
      sudo -A bash -c "echo $(w_to_uw $2) > ${pl_path}"
    else
      sudo bash -c "echo $(w_to_uw $2) > ${pl_path}"
    fi
  else
    exit 1
  fi
}

get_pl () {
  if [ $1 -eq 1 ]; then
    cat $pl1_path
  elif [ $1 -eq 2 ]; then
    cat $pl2_path
  fi
}

# Retrieves current TDP and prints it
check_tdp () {
  if [ -z "$1" ] || ( is_detailed $@ ); then
    local pl1=$(uw_to_w $(get_pl 1))
    local pl2=$(uw_to_w $(get_pl 2))

    echo "PL1 is ${pl1}W" # Placeholder
    if ( is_detailed $@ ); then
      echo "PL2 is ${pl2}W" # Placeholder
    fi

  elif ( is_help $@ ); then
    print_help "check"

  else
    print_unknown
  fi
}

# Sets PL1 to number provided as first argument, and PL2 2W higher
set_tdp () {
  if [ -z "$1" ]; then
    echo "Please specify wattage"
    exit
  elif ( is_help $@ ); then
    print_help "set"
    exit
  elif ! (is_number $1); then
    echo "TDP is not a number or argument unknown!)"
    print_unknown
    exit
  fi

  if [ $1 -lt 5 ] || [ $1 -gt 30 ]; then
    echo "TDP too high or low, should be between 5W and 30W"
    echo "This is a sanity limit to prevent you from throttling to a near unusable state"
  else
    #PL1
    local watts=$1
    set_pl 1 $watts

    #PL2
    local watts2=$(expr $watts + 2)
    if [[ "$@" == *"--same"* ]] || [[ "$@" == *"-s"* ]]; then
      watts2=$watts
    fi
    set_pl 2 $watts2


    echo "PL1 is now ${watts}W (Long-term)"
    if ( is_detailed $@ ); then
      echo "PL2 is now ${watts2}W (Short-term)"
    fi
  fi
}

# Basic GUI using Zenity
gui_handler() {
  if [ -z $(which zenity 2>/dev/null) ]; then
    echo "Zenity is not available, GUI will not work until it is installed"
  elif (is_help $@); then
    print_help "gui"
  else
    # Enables graphical sudo prompt
    export SUDO_ASKPASS="${script_dir}/gpd-sudo-prompt"
    export gui_used=1
    while : ; do
      # Clear variables
      gui_tdp=""
      # Contains some ugly hacks to "widen" the entries for the small GPD screen, might tweak later
      gui_action=$(zenity --list --title="${gui_name}" --width=400 --height=350 $gui_options --text="Chose action to perform:" --hide-column=1 --column="" --column="Action"\
        check "`printf "\n  Check current TDP\n "`" \
        preset "`printf "\n  Change TDP (requires sudo)\n "`" \
        set "`printf "\n  Set custom TDP (requires sudo)\n "`" \
        exit "`printf "\n  Exit TDP Manager\n "`")
      
      case $gui_action in
      
        "check")
        gui_msg "$($command_name check --detail)"
        ;;
      
        "preset")
        gui_tdp=$(
          zenity --list --title "${gui_name}" --width=400 --height=350 $gui_options --text="Chose TDP to set:" --hide-column=1 --column="" --column="Wattage" \
            8 "`printf "\n  8W (Ultra-low, for simple 2D games)\n "`" \
            12 "`printf "\n  12W (Low power)\n "`" \
            15 "`printf "\n  15W (Balanced)\n "`" \
            20 "`printf "\n  20W (High power)\n "`" \
            28 "`printf "\n  28W (Max power / Docked)\n "`"
        )
        echo $gui_tdp
        if ! [ -z $gui_tdp ]; then
          gui_msg "$(tdp set $gui_tdp --detail)"
        fi
        ;;
      
        "set")
        gui_tdp=$(gui_read "Please enter TDP")
        
        if ! [ -z $gui_tdp ]; then
          if $(gui_ask "Should PL2 be set to the same wattage?\nIf unsure, answer No."); then
            gui_msg "$(tdp set $gui_tdp --same --detail)"
          else
            gui_msg "$(tdp set $gui_tdp --detail)"
          fi
          gui_tdp=""
        fi
        ;;
      
        *)
        break
        ;;
      esac
    done
  fi
}

# Command handler
# This checks which command was entered and forwards all other arguments to it
case $1 in

  "check" | "c")
    check_tdp "${@:2}"
    ;;

  "set" | "s")
    set_tdp "${@:2}"
    ;;

  "gui") # Pass to GUI handler
    gui_handler "${@:2}"
    ;;

  "help")
    print_help
    ;;

  *)
    print_unknown
    ;;
esac