#!/bin/bash

ME=${0##*/}

MIN_PERCENT=2

export TEXTDOMAIN="backlight-brightness"
LOG_FILE=$ME.log

usage() {
    local ret=${1:-0}
cat <<Usage
Usage: $ME [options] [step-value]
Interactively adjust the brightness of the backlight from within a terminal.

Step value:

   +10   increase brightness by 10 percent
   -17   decrease brightness by 17 percent

Options:
  -c --color <scheme>  Set color scheme.  Valid schemes:
                           high, dark, low, low2, bw
  -g --get             Print out the current brightness percentage then exit
  -h --help            Show this usage
  -p --pause           Pause and wait for <Enter> after an error
  -s --set   <value>   Set the brightness percentage to <value> then exit
Usage

    exit $ret
}

main() {

    set_colors $COLOR_SCHEME
    local SET_PERCENT  GET_PERCENT EARLY_EXIT
    read_params "$@"
    set_colors $COLOR_SCHEME

    # Keyboard keys: up-arrow, down-arrow, page-up, page-down
    local UP_ARROW=$"up-arrow"  DOWN_ARROW=$"down-arrow" PAGE_UP=$"page-up" PAGE_DOWN=$"page-down"

    # Keyboard keys: left-arrow, right-arrow
    local LEFT_ARROW=$"left-arrow"  RIGHT_ARROW=$"right-arrow"

    local CONTROLLER  CONTROLLER_MAX  CONTROLLER_CUR
    find_controller CONTROLLER

    CONTROLLER_NAME=$CONTROLLER
    if [ "$TEST" -a -z "$CONTROLLER" ]; then
        echo 50 > ./brightness
        CONTROLLER_MAX=100
        CONTROLLER_CUR=50
        CONTROLLER=./
        CONTROLLER_NAME="Test Mode"
    fi

    [ -n "$CONTROLLER" ]           || fatal $"No controller found"
    test -w $CONTROLLER/brightness || fatal $"You need to be root to run this script"

    local PERCENT=$((100 * CONTROLLER_CUR / CONTROLLER_MAX))
    [ -n "$GET_PERCENT"  ] && echo $PERCENT
    [ -n "$SET_PERCENT"  ] && cmdline_brightness "$SET_PERCENT"
    [ -n "$STEP_PERCENT" ] && step_percent "$STEP_PERCENT" xxxx
    [ -n "$EARLY_EXIT"   ] && exit 0

    case $(tty) in
        */pts/*) SCREEN_IN_VT=     ;;
              *) SCREEN_IN_VT=true ;;
    esac

    trap on_exit EXIT
    hide_tty

    restart
    while true; do
        local key=$(get_key)
        case $key in
         [qQ]|escape) exit ;;
               enter) exit ;;
                left) step_percent  -5 ;;
               right) step_percent  +5 ;;
                  up) step_percent  +1 ;;
                down) step_percent  -1 ;;
             page-up) step_percent +20 ;;
           page-down) step_percent -20 ;;
                   c) cycle_color      ;;
                   d) color_cycle      ;;
                [rR]) restart          ;;
                   *) xyprintf 1 $HEIGHT "%-20s" "$key" ;;
        esac
    done
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
cycle_color() {
    case $COLOR_SCHEME in
         off)  COLOR_SCHEME=dark   ;;
        dark)  COLOR_SCHEME=low    ;;
         low)  COLOR_SCHEME=low2   ;;
        low2)  COLOR_SCHEME=bw     ;;
          bw)  COLOR_SCHEME=high   ;;
           *)  COLOR_SCHEME=dark   ;;
    esac
    set_colors $COLOR_SCHEME
    restart
    xyprintf 1 $HEIGHT "%-20s" "color scheme: $COLOR_SCHEME"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
color_cycle() {
    case $COLOR_SCHEME in
        dark)  COLOR_SCHEME=high   ;;
         low)  COLOR_SCHEME=dark   ;;
        low2)  COLOR_SCHEME=low    ;;
          bw)  COLOR_SCHEME=low2   ;;
           *)  COLOR_SCHEME=bw     ;;
    esac
    set_colors $COLOR_SCHEME
    restart
    xyprintf 1 $HEIGHT "%-20s" "color scheme: $COLOR_SCHEME"
}


restart() {
    local x_margin=5
    HEIGHT=$(screen_height)
    local mid_y=$((HEIGHT / 2))
    SCREEN_RAW_WIDTH=$(screen_width)
    local width=$(screen_width)

    X0=$x_margin
    local box_width=$((width - 2 * x_margin))
    Y0=$((mid_y - 5))
    local box_height=3

    MAX_X=$((box_width - 2))
    redraw
}



#==============================================================================
# Brightness Controller
#
#==============================================================================

find_controller() {
    local var_nam=${1:-CONTROLLER}
    local f  max  best cur  best_max=0
    for f in /sys/class/backlight/*; do
        test -e $f || continue
        read max 2>/dev/null < $f/max_brightness
        [ $best_max -lt ${max:-0} ] || continue
        best_max=$max
        best=$f
    done

    [ -n "$best" ] || return
    read cur 2>/dev/null <$best/brightness
    eval $var_nam=\$best
    eval ${var_nam}_MAX=\$best_max
    eval ${var_nam}_CUR=\$cur
}

set_brightness() {
    local percent=$1
    local value=$((percent * CONTROLLER_MAX / 100))
    echo $value > $CONTROLLER/brightness
}

cmdline_brightness() {
    local percent=$1
    case $percent in
        "") return 1          ;;
        [1-9]|[0-9][0-9]|100) ;;
        *) fatal "Brightness value must be between 1 and 100" ;;
    esac
    set_brightness $percent
    return 0
}

#==============================================================================
# Screen and Drawing
#
#==============================================================================

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
redraw() {
    clear
    box -b $X0 $Y0 $box_width $box_height $box_co
    cprintf "" $((Y0 - 2)) $"Backlight Brightness Control"
    cprintf "" $((Y0 - 1)) "$bold_co$(basename "$CONTROLLER_NAME")"

    local box_bot=$((Y0 + box_height - 1))
    local text_x=
    cprintf  ""          $((box_bot + 1)) $"Use <%s> and <%s> to adjust the brightness" "$(bq $LEFT_ARROW)" "$(bq $RIGHT_ARROW)"
    xyprintf $CPRINTF_X  $((box_bot + 2)) $"Use <%s> and <%s> for smaller steps"        "$(bq $UP_ARROW)"   "$(bq $DOWN_ARROW)"
    xyprintf $CPRINTF_X  $((box_bot + 3)) $"Use <%s> and <%s> for bigger steps"         "$(bq $PAGE_UP)"    "$(bq $PAGE_DOWN)"
    xyprintf $CPRINTF_X  $((box_bot + 4)) $"Press '%s' to quit" "$(bq q)"
    draw_bar
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
step_percent() {
    local step=$1  quit=$2
    PERCENT=$((PERCENT + step))
    [ $PERCENT -gt 100 ] && PERCENT=100
    [ $PERCENT -lt $MIN_PERCENT ] && PERCENT=$MIN_PERCENT
    set_brightness $PERCENT

    if [ -n "$quit" ]; then
        echo $PERCENT
	return
    fi

    draw_bar
    xyprintf 1 $HEIGHT "%4s: %-20s" "$step" "$PERCENT%"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
draw_bar() {
    [ $PERCENT -gt 100 ] && PERCENT=100
    [ $PERCENT -lt $MIN_PERCENT ] && PERCENT=$MIN_PERCENT
    local x=$((PERCENT * MAX_X / 100))
    local remain=$((MAX_X - x))
    xyprintf $((X0 + 1))  $((Y0 + 1)) "$bar_bg%${x}s$nc_co%${remain}s" "" ""
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
box() {
    local flag
    while [ $# -gt 0 -a -z "${1##-*}" ]; do
        flag=$flag${1#-}
        shift
    done

    #return
    local x0=$1 y0=$2 width=$3 height=$4 color=$5
    #log "box($flag $x0 $y0 $width $height ${color}XX$nc)"

    [ "$color" ] && printf "$nc$color"

    local iwidth=$((width - 2))
    local x1=$((x0 + width - 1))

    #-- Set up line style and colors

    [ "$ASCII_ONLY" ] && flag=A$flag
    case $flag in
      Ac) local hbar=" " vbar=" " tl_corn=" " bl_corn=" " tr_corn=" " br_corn=" " ;;
      Ad) local hbar="=" vbar="|" tl_corn="#" bl_corn="#" tr_corn="#" br_corn="#" ;;
      A*) local hbar="-" vbar="|" tl_corn="+" bl_corn="+" tr_corn="+" br_corn="+" ;;
       c) local hbar=" " vbar=" " tl_corn=" " bl_corn=" " tr_corn=" " br_corn=" " ;;
       b) local hbar="━" vbar="┃" tl_corn="┏" tr_corn="┓" bl_corn="┗" br_corn="┛" ;;
       d) local hbar="═" vbar="║" tl_corn="╔" tr_corn="╗" bl_corn="╚" br_corn="╝" ;;
       *) local hbar="─" vbar="│" tl_corn="┌" tr_corn="┐" bl_corn="└" br_corn="┘" ;;
    esac

    local bar=$(printf "%${iwidth}s" | sed "s/ /$hbar/g")
    printf "\e[$y0;${x0}H$tl_corn$bar$tr_corn"
    local y
    for y in $(seq $((y0 + 1)) $((y0 + height - 2))); do
        printf "\e[$y;${x0}H$vbar"
        printf "\e[$y;${x1}H$vbar"
    done
    printf "\e[$((y0 + height - 1));${x0}H$bl_corn$bar$br_corn"

}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
get_key() {
    local key k1 k2 k3 k4
    read -s -N1
    k1=$REPLY
    read -s -N2 -t 0.001 k2
    read -s -N1 -t 0.001 k3 2>/dev/null
    read -s -N1 -t 0.001 k4 2>/dev/null
    key=$k1$k2$k3$k4

    case $key in
        $'\x1b\x4f\x50\x00')     key=f1           ;;
        $'\x1b\x4f\x51\x00')     key=f2           ;;
        $'\x1b\x4f\x52\x00')     key=f3           ;;
        $'\x1b\x4f\x53\x00')     key=f4           ;;

        $'\x1b\x5b\x5b\x41')     key=f1           ;;
        $'\x1b\x5b\x5b\x42')     key=f2           ;;
        $'\x1b\x5b\x5b\x43')     key=f3           ;;
        $'\x1b\x5b\x5b\x44')     key=f4           ;;
        $'\x1b\x5b\x5b\x45')     key=f5           ;;

        $'\x1b\x5b\x31\x35\x7e') key=f5           ;;
        $'\x1b\x5b\x31\x37\x7e') key=f6           ;;
        $'\x1b\x5b\x31\x38\x7e') key=f7           ;;
        $'\x1b\x5b\x31\x39\x7e') key=f8           ;;
        $'\x1b\x5b\x32\x30\x7e') key=f9           ;;
        $'\x1b\x5b\x32\x31\x7e') key=f10          ;;
        $'\x1b\x5b\x32\x33\x7e') key=f11          ;;
        $'\x1b\x5b\x32\x34\x7e') key=f12          ;;
        $'\x1b\x5b\x32\x7e')     key=insert       ;;
        $'\x1b\x5b\x33\x7e')     key=delete       ;;
        $'\x1b\x5b\x31\x7e')     key=home         ;;
        $'\x1b\x5b\x34\x7e')     key=end          ;;
        $'\x1b\x5b\x35\x7e')     key=page-up      ;;
        $'\x1b\x5b\x36\x7e')     key=page-down    ;;
        $'\x1b\x5b\x41')         key=up           ;;
        $'\x1b\x5b\x42')         key=down         ;;
        $'\x1b\x5b\x43')         key=right        ;;
        $'\x1b\x5b\x44')         key=left         ;;

        $'\x7f')                 key=backspace    ;;
        $'\x08')                 key=backspace    ;;
        $'\x09')                 key=tab          ;;
        $'\x0a')                 key=enter        ;;
        $'\x1b')                 key=escape       ;;
        $'\x20')                 key=space        ;;
    esac
    printf "$key"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
xyprintf() {
    local x=$1  y=$2  fmt=$3
    shift 3
    #local msg=$(printf "$fmt" "$@")
    printf "$m_co\e[$y;${x}H$fmt$nc_co" "$@"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
cprintf() {
    local x0=${1:-$((SCREEN_RAW_WIDTH / 2))}  y=$2  fmt=$3
    shift 3
    local msg=$(printf "$fmt" "$@")
    local len=$(str_len "$msg")
    local x=$((1 + x0 - len/2))
    printf "$m_co$color\e[$y;${x}H$msg$nc_co"
    CPRINTF_X=$x
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
str_len() {
    local msg_nc=$(echo "$*" | sed -r -e 's/\x1B\[[0-9;]+[mK]//g' -e 's/./x/g')
    echo ${#msg_nc}
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
fatal() {
    local fmt=$1 ; shift
    printf "$bold_co$ME:$hi_co $fmt$nc_co\n" "$@" >&2
    [ "$PAUSE_ON_ERROR" ] && pause
    exit 3
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
screen_height() { stty size | cut -d" " -f1; }
screen_width()  { stty size | cut -d" " -f2; }

#------------------------------------------------------------------------------
# Hide cursor and prepare restore_cursor() to work just once
#------------------------------------------------------------------------------
hide_cursor() {
    RESTORE_CURSOR="\e[?25h\n"

    # Disable cursor
    printf "\e[?25l"
}

#------------------------------------------------------------------------------
# Only works once after hide_cursor() runs.  This allows me to call it in the
# normal flow and at clean up.
#------------------------------------------------------------------------------
restore_cursor() {
    printf "$RESTORE_CURSOR"
    RESTORE_CURSOR=
}

set_colors() {
    local color=${1:-high}

    local e=$(printf "\e")
    clear="$e[2;J"; cursor_off="$e[?25l"; cursor_on="$e[?25h"

    bar_char="■"

    rev_co="\e[7m"

    if [ "$color" = 'off' ]; then

         black=  ;    blue=  ;    green=  ;    cyan=  ;
           red=  ;  purple=  ;    brown=  ; lt_gray=  ;
       dk_gray=  ; lt_blue=  ; lt_green=  ; lt_cyan=  ;
        lt_red=  ; magenta=  ;   yellow=  ;   white=  ;
         nc_co=  ;   brown=  ;

         bold_co=            ;    fs_co=           ;      num_co=            ;
         date_co=            ;  head_co=           ;    quest_co=            ;
          dev_co=            ;    hi_co=           ;     quit_co=            ;
          err_co=            ;   lab_co=           ;  version_co=            ;
        fname_co=            ;     m_co=           ;     warn_co=            ;

        nc_co="$e[0m" ; $bar_bg="$e[47m"
         return
     fi

         black="$e[0;30m" ;    blue="$e[0;34m" ;    green="$e[0;32m" ;    cyan="$e[0;36m" ;
           red="$e[0;31m" ;  purple="$e[0;35m" ;    brown="$e[0;33m" ; lt_gray="$e[0;37m" ;
       dk_gray="$e[1;30m" ; lt_blue="$e[1;34m" ; lt_green="$e[1;32m" ; lt_cyan="$e[1;36m" ;
        lt_red="$e[1;31m" ; magenta="$e[1;35m" ;   yellow="$e[1;33m" ;   white="$e[1;37m" ;
         nc_co="$e[0m"    ;   brown="$e[0;33m" ;

         bg_black="$e[40m"      ;     bg_blue="$e[44m"
         bg_red="$e[41m"        ;     bg_magenta="$e[45m"
         bg_green="$e[42m"      ;     bg_cyan="$e[46m"
         bg_yellow="$e[43m"     ;     bg_white="$e[47m"

    case $color in
        high)
         bar_bg=$bg_cyan     ;   box_co=$white
         bold_co=$yellow     ;    fs_co=$lt_blue   ;      num_co=$magenta    ;
         date_co=$lt_cyan    ;  head_co=$white     ;    quest_co=$lt_green   ;
          dev_co=$white      ;    hi_co=$white     ;     quit_co=$yellow     ;
          err_co=$red        ;   lab_co=$lt_cyan   ;  version_co=$white      ;
        fname_co=$white      ;     m_co=$lt_cyan   ;     warn_co=$yellow     ; ;;

        dark)
         bar_bg=$bg_blue     ;   box_co=$cyan
         bold_co=$brown      ;    fs_co=$lt_blue   ;      num_co=$brown   ;
         date_co=$cyan       ;  head_co=$nc_co     ;    quest_co=$green   ;
          dev_co=$nc_co      ;    hi_co=$nc_co     ;     quit_co=$brown   ;
          err_co=$red        ;   lab_co=$cyan      ;  version_co=$nc_co   ;
        fname_co=$nc_co      ;     m_co=$cyan      ;     warn_co=$brown   ; ;;

        low)
         bar_bg=$bg_cyan     ;   box_co=$cyan
         bold_co=$white      ;    fs_co=$nc_co     ;      num_co=$white      ;
         date_co=$nc_co      ;  head_co=$white     ;    quest_co=$lt_green   ;
          dev_co=$white      ;    hi_co=$white     ;     quit_co=$lt_green   ;
          err_co=$red        ;   lab_co=$nc_co     ;  version_co=$white      ;
        fname_co=$white      ;     m_co=$nc_co     ;     warn_co=$yellow     ; ;;

        low2)
         bar_bg=$bg_white    ;   box_co=$cyan
         bold_co=$white      ;    fs_co=$nc_co     ;      num_co=$white      ;
         date_co=$nc_co      ;  head_co=$white     ;    quest_co=$green      ;
          dev_co=$white      ;    hi_co=$white     ;     quit_co=$green      ;
          err_co=$red        ;   lab_co=$nc_co     ;  version_co=$white      ;
        fname_co=$white      ;     m_co=$nc_co     ;     warn_co=$yellow     ; ;;

        bw)
         bar_bg=$bg_white    ;   box_co=$white
         bold_co=$white      ;    fs_co=$nc_co     ;      num_co=$white      ;
         date_co=$nc_co      ;  head_co=$white     ;    quest_co=$white      ;
          dev_co=$white      ;    hi_co=$white     ;     quit_co=$white      ;
          err_co=$white      ;   lab_co=$lt_gray   ;  version_co=$lt_gray    ;
        fname_co=$white      ;     m_co=$nc_co     ;     warn_co=$white      ; ;;

        *)
            error "Unknown color parameter: %s" "$color"
            fatal "Expected high, low. low2, bw, dark, or off" ;;
    esac
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
pq()  { echo "$hi_co$*$m_co"      ;}
vq()  { echo "$version_co$*$m_co" ;}
pqq() { echo "$hi_co$*$quest_co"  ;}
bqq() { echo "$bold_co$*$quest_co";}
pnq() { echo "$num_co$*$quest_co" ;}
pnh() { echo "$num_co$*$hi_co"    ;}
pqw() { echo "$warn_co$*$hi_co"   ;}
pqe() { echo "$hi_co$*$err_co"    ;}
pqh() { echo "$m_co$*$hi_co"      ;}
pqb() { echo "$m_co$*$bold_co"    ;}
bq()  { echo "$bold_co$*$m_co"    ;}
hq()  { echo "$bold_co$*$m_co"    ;}
cq()  { echo "$hi_co$*$m_co"      ;}
nq()  { echo "$num_co$*$m_co"     ;}


restore_tty() {
    [ -n "$ORIG_STTY" ] && stty $ORIG_STTY
    printf "$nc$clear$cursor_on"
    printf "\e[1;1H"
}

hide_tty() {
    [ "$ORIG_STTY" ]    || ORIG_STTY=$(stty -g)
    [ "$SCREEN_IN_VT" ] || trap restart WINCH
    clear
    stty cbreak -echo
    printf $cursor_off
}

read_params() {
    while [ $# -gt 0 ]; do
        local arg=$1 ; shift
        case $arg in
            [+-][0-9]|[+-][0-9][0-9])   STEP_PERCENT=$arg      ; EARLY_EXIT=true ;;
            [+-][0-9]%|[+-][0-9][0-9]%) STEP_PERCENT=${arg%%%} ; EARLY_EXIT=true ;;

           -c|--color) [ $# -lt 1 ] && fatal "Expected an value after %s" "$arg"
                       COLOR_SCHEME=$1 ; shift ;;

            -p|--pause) PAUSE_ON_ERROR=true          ;;

            --color=*) COLOR_SCHEME=${arg#*=}  ;;

             -g|--get) GET_PERCENT=true  ; EARLY_EXIT=true ;;

            -h|--help) usage ;;

              --set=*) SET_PERCENT=${arg#*=}
                       EARLY_EXIT=true ;;

             -s|--set) [ $# -lt 1 ] && fatal "Expected an value after %s" "$arg"
                       SET_PERCENT=$1 ; shift
                       EARLY_EXIT=true ;;

                    *) fatal "Unrecognized option %s" "$arg"
        esac
    done
}

pause() {
    local xxx
    read     -t .01 xxx
    read -N1 -t .01 xxx
    read -N1 -t .01 xxx
    read -N1 -t .01 xxx
    echo -n "${cyan}Press <Enter> to continue$nc"
    read -s xxx
    echo
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
on_exit() {
    [ -n "$ORIG_STTY" ] || return
    restore_tty
    #printf "\e[1;1H"
    clear
    printf "$cyan%s exited$nc\n" $ME
}

main "$@"
