#!/bin/bash

# Codesets on Debian
#
#  CyrAsia    Greek    Lat7
#  CyrKoi     Lat15    Uni2
#  CyrSlav    Lat2     Uni3

export TEXTDOMAIN="console-width-select"

lib_dir=/usr/lib/shell
loc_lib=./lib
test -e $loc_lib/lib-screen.sh && lib_dir=$loc_lib
. $lib_dir/lib-screen.sh
. $lib_dir/lib-grid.sh

ACTIVE_CONSOLES="/dev/tty[1-6]"
       FONT_DIR="/usr/share/consolefonts"
      FONT_EXTS=".psf.gz"
     FONT_SIZES="16 14 12 11 10 8 6"
      DUMMY_TTY="tty2"
       NEW_FONT=
      CONF_FILE=/etc/default/console-setup

main() {

    [ -n "$SCREEN_IN_VT" -a $UID -ne 0 ] && fatal $"This program must be run as root"

    local tty_arg
    [ "$SCREEN_IN_VT" ] || tty_arg="-tty=${DUMMY_TTY#tty}"
    local pixel_width=$(get_fbcondecor_width $tty_arg)

    [ -z "$pixel_width" ] \
        && fatal $"Could not get console width. Is the framebuffer enabled?"

    local cur_width=$(get_current_width)

    [ ${#CODESET} -eq 0 ] && CODESET=$(lang_to_codeset $LANG)
    WIDTH_MENU="$(width_menu $pixel_width)"
    [ ${#WIDTH_MENU} -eq 0 ] && fatal $"No fonts found"

    trap set_font_exit EXIT

    set_color_scheme light

    screen_set title1=$"Select the console width in characters"
    screen_set title2=$"Press: <Enter> to select a font <q> to quit, <h> for help"
    if [ "$SCREEN_IN_VT" ]; then
        screen_set title3="$(printf $"Screen is %s chars wide" "$cur_width")"
    else
        screen_set title3="$(printf $"%s is %s chars wide" "$DUMMY_TTY" "$cur_width")"
    fi
    screen_set border=0

    init

    #grid_center_labels

    hide_tty
    clear
    redraw
    main_loop
}

init() {
    screen_init
    grid_read_new console_width "$WIDTH_MENU"
    grid_narrow y=5 title=$"Select Console Width"
    grid_center_x
    grid_fill_y 15
    grid_finalize
}

redraw() {
    screen_draw
    grid_activate
}

restart() {
    local save_sel=$GRID_SEL
    local save_def=$GRID_DEFAULT_SEL
    init
    GRID_SEL=$save_sel
    GRID_DEFAULT_SEL=$save_def
    clear
    redraw
}

#------------------------------------------------------------------------------
# We use the "t" key to display information about the current font.
#------------------------------------------------------------------------------
key_callback() {
    local key=$1
    [ "$key" = "t" ] || return 1
    clear

    echo -n $cyan
    showconsolefont -i
    echo -n $nc
    showconsolefont
    echo

    echo "$cyan Press any key to continue$nc"

    local xxx
    read -s -n1 xxx
    clear
    redraw
    return 0
}

#------------------------------------------------------------------------------
# This routine runs when the user presses <Enter> on one of the selections.
# We get the entire label and use sed to get to font width that was near the
# end of the label.  This version sets the console font for tty1 -- tty6
# This is a call-back from lib-menu.
#------------------------------------------------------------------------------
console_width_on_enter() {
    local lab=$1  sel=$3

    local size=$(echo "$lab" | sed -nr "s/^.*\s+([1-9][0-9]?)\)?.*/\1/p")
    local font=$(get_console_font $size)

    GRID_DEFAULT_SEL=$sel
    if [ -z "$SCREEN_IN_VT" ]; then
        grid_activate
        db_msg "Would set font to %s" "$white$font"
        return
    fi

    NEW_FONT=$font

    local tty
    for tty in $ACTIVE_CONSOLES; do
        setfont $font -C $tty
    done

    local cur_width=$(get_current_width)

    screen_set title3="$(printf $"Screen is %s chars wide, font is %s" "$yellow$cur_width$magenta" "$yellow$font")"

    log "font=$font"
    clear

    restart
}

#------------------------------------------------------------------------------
# Given a font-width specification and a language, we try to find a font file
# that works.  We try a few options but usually the first one works so this is
# pretty fast.
#------------------------------------------------------------------------------
get_console_font() {
    local cmd_size=$1
    local lang=${3:-$LANG}  fdir=${4:-$FONT_DIR}
    local font_exts=${5:-$FONT_EXTS}
    local name="Terminus"

    local size
    case $cmd_size in
              [1-7]) size=12x6                ;;
               [89]) size=16                  ;;
                 10) size=20x10               ;;
                 11) size=22x11               ;;
              1[23]) size=24x12               ;;
              1[45]) size=28x14               ;;
            1[6789]) size=32x16               ;;
       [23456][0-9]) size=32x16               ;;
    esac

    local code
    case ${lang%%_*} in
                     kk|ky|tj) code='CyrAsia'  ;;
                        ru|uk) code='CyrKoi'   ;;
                  bg|mk|ru|sr) code='CyrSlav'  ;;
      bs|hr|cs|hu|pl|ro|sk|sl) code='Lat2'     ;;
        af|sq|ast|da|nl|et|fr) code='Lat15'    ;;
    'fi'|de|is|id|pt|es|sv|tr) code='Lat15'    ;;
                        lt|lv) code='Lat7'     ;;
                           el) code='Greek'    ;;
                            *) code='Uni2'     ;;
    esac

    # Now try to find an existing font file that matches what we want

    local try font face font_file ext
    for face in ${name}Bold VGA $name; do
        try=$code-$face$size
        #echo $try
        for ext in $font_exts; do
            font_file=$fdir/$try$ext
            #echo $font_file
            test -e $font_file || continue
            font=$try
            break
        done
        [ -n "$font" ] && break
    done
    echo $font
}

#------------------------------------------------------------------------------
# A simple menu showing options for width of the screen in chars and the width
# of the char.  We don't provide options if the number of columns would end up
# less than 80.
#------------------------------------------------------------------------------
width_menu() {
    local pixel_width=$1  font_width  num_cols

    # How many columns wide the screen is, and the font width
    local col_lab=$"columns"  fw_lab=$"font width"
    local fmt="%3s $col_lab  ($fw_lab %2s)\n"
    for font_width in $FONT_SIZES; do
        num_cols=$((pixel_width / font_width + 1))
        [ $num_cols -lt 80 ] && continue
        printf "$fmt" "$num_cols" "$font_width"
    done
}

#------------------------------------------------------------------------------
# This version uses the program "fbcondecor_ctl" or fbcondecor_ctl.static" to
# find out the width of the active text area of the current theme in pixels.
# This is the best way to do it but it requires that program which is not in
# initrd ATM.  So similar code in the initrd assumes the default theme is
# being used.
#------------------------------------------------------------------------------
get_fbcondecor_width() {
    local tty_arg=$1  res
    local fbsize_file=/sys/class/graphics/fb0/virtual_size
    read res 2>/dev/null <$fbsize_file
    [ -z "$res" ] && return
    local width
    local name=fbcondecor_ctl prog
    for prog in $name $name.static; do
        which $prog &>/dev/null || continue
        width=$($prog $tty_arg -c getcfg 2>/dev/null | sed -nr "s/^twidth:\s+//p")
        break
    done
    [ ${width:-0} -eq 0 ] && width=$(echo "$res" | cut -d, -f1)
    printf "%s\n" "$width"
}

#------------------------------------------------------------------------------
# Convenience routine to give the width of the screen in CHARACTERS
#------------------------------------------------------------------------------
get_current_width() { stty size | cut -d" " -f2; }


#------------------------------------------------------------------------------
# Convert the two-character language code to the corresponding font code-set.
#------------------------------------------------------------------------------
lang_to_codeset() {
    local lang=$1
    case ${lang%%_*} in
                     kk|ky|tj) echo CyrAsia  ;;
                        ru|uk) echo CyrKoi   ;;
                  bg|mk|ru|sr) echo CyrSlav  ;;
      bs|hr|cs|hu|pl|ro|sk|sl) echo Lat2     ;;
        af|sq|ast|da|nl|et|fr) echo Lat15    ;;
    "fi"|de|is|id|pt|es|sv|tr) echo Lat15    ;;
                        lt|lv) echo Lat7     ;;
                           el) echo Greek    ;;
                            *) echo Uni2     ;;
    esac
}

#------------------------------------------------------------------------------
# Edit /etc/default/console-setup so it will use the selected font on the next
# (installed) boot.  It doesn't make sense to do this on the live system, even
# with persistence, becauese we will be overwriting these entries anyway.
#------------------------------------------------------------------------------
set_default_font() {
    font=${1#*-}  conf_file=${2:-$CONF_FILE}

    # Don't set font on the live system since we will set at boot time
    its_alive           && return

    [ "$SCREEN_IN_VT" ] || return
    [ -n "$font" ]      || return
    [ -n "$conf_file" ] || return
    test -w $conf_file  || return

    local face=$font

    # Strip off the size from the font face
    face=${face%%[1-9]*}

    # Get back the size by removing the font face
    local size=${font#$face}

    [ "$size"         ] || return
    [ "$face"         ] || return

    say $"Setting default font to %s" "$yellow$font$cyan"

    # See if both lines already exist in the file (usual case)
    # We try to minimize the number of times we write to the file
    if grep -q "^\s*FONTFACE=" $conf_file && grep -q "^\s*FONTSIZE=" $conf_file; then
        # if so then only edit the conf_file at most once
        local expr
        grep -q "^\s*FONTFACE=\"$face\"" $conf_file \
            || expr="$expr -e s/^\s*(FONTFACE=).*/\1\"$face\"/"

        grep -q "^\s*FONTSIZE=\"$size\""  $conf_file \
            || expr="$expr -e s/^\s*(FONTSIZE=).*/\1\"$size\"/"

        [ ${#expr} -gt 0 ] && xsed -r -i $expr $conf_file
    else
        add_or_replace  "^\s*FONTFACE="  "FONTFACE=\"$face\""  $conf_file create
        add_or_replace  "^\s*FONTSIZE="  "FONTSIZE=\"$size\""  $conf_file create
    fi
}

#------------------------------------------------------------------------------
# Routine to see what sed actions get performed
#------------------------------------------------------------------------------
xsed() {
    #echo "sed $*"
    /bin/sed "$@"
}
#------------------------------------------------------------------------------
# Either replace an existing line or add a new line if an existing line that
# sets the variable we want to set is missing.  If the "create" flag is given
# then we will create the file if it doesn't already exist.
#------------------------------------------------------------------------------
add_or_replace() {
    local where=$1  line=$2  file=$3  create=$4

    if ! test -e "$file"; then
        [ "$create" ] || return
        mkdir -p $(basename $file)
        echo "$line" > $file
        return
    fi

    if grep -q "$where" "$file"; then
        # Don't over-write the same string
        grep -q "^$line" "$file" && return
        # Edit the existing line
        xsed -i -r "/$where/ s|.*|$line|" $file
    else
        # Add it under the top line of the file
         xsed -i "1 a$line" $file
    fi
}


#------------------------------------------------------------------------------
# Send colored text to the console
#------------------------------------------------------------------------------
say() {
    local fmt=$1 ; shift
    printf "$cyan$fmt$nc\n" "$@"
}

#------------------------------------------------------------------------------
# When we exit, set the default font to be the last font selected by the user.
# Should we ask first?
#------------------------------------------------------------------------------
set_font_exit() {
    restore_tty

    set_default_font "$NEW_FONT"
    say "%s exited" "$ME"

    # Chain to exit routine in lib-screen
    #on_exit
}

my_dir=$(readlink -f "$(dirname "$0")")

HELP_PAGE=console-width-select

[ "$DEBUG" ] || log_file=/dev/null

main "$@" 2>> $log_file
