#!/usr/bin/bash

SKIP_HW_DESC_INIT=1
source /etc/mod-hardware-descriptor.env

function lin_conv {
    x1=$1
    x2=$2
    y1=$3
    y2=$4
    x=$5

    # a = (y2 - y1) / (x2 - x1)
    # b = y2 - a*x2
    # y = a*x + b
    # warn: dc of busybox doesn't support register and doesn't require to underscore negative number
    a=$(/usr/bin/dc <<< "$y2 $y1 - $x2 $x1 - / p")
    b=$(/usr/bin/dc <<< "$y2 $a $x2 * - p")
    y=$(/usr/bin/dc <<< "$a $x * $b + p")

    /usr/bin/echo $y | /usr/bin/awk '{print sprintf("%.1f",$1);}'
}

function set_pga {
    CH_NAMES=("frontleft" "frontright")
    n=$(($1-1))
    x=$2

    y=$(lin_conv -12 12 0 48 $x)
    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${CH_NAMES[$n]} $y
}

function get_pga {
    CH_NAMES=("Left" "Right")
    n=$(($1-1))

    x=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget 'PGA Gain' | /usr/bin/grep -o "${CH_NAMES[$n]}: [0-9]*" | /usr/bin/cut -d' ' -f2)
    /usr/bin/echo $(lin_conv 0 48 -12 12 $x)
}

function set_stage {
    n=$(($1-1))
    val=$2

    STAGES=("Low" "Mid" "High")
    CH_NAMES=("Left" "Right")

    for i in ${!STAGES[@]}; do
        if [[ "${STAGES[$i]}" == "$val" ]]; then
            break
        fi
    done

    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "${CH_NAMES[$n]} Gain Stage" $i
}

function get_stage {
    n=$(($1-1))

    STAGES=("Low" "Mid" "High")
    CH_NAMES=("Left" "Right")

    i=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "${CH_NAMES[$n]} Gain Stage" | /usr/bin/grep -o "Mono: [0-9]" | /usr/bin/cut -d' ' -f2)
    /usr/bin/echo ${STAGES[$i]}
}

function set_dac {
    CH_NAMES=("frontleft" "frontright")
    n=$(($1-1))
    x=$2
    y=$(lin_conv -127.5 0 0 255 $x)

    if [[ $n -eq -1 ]]; then
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'DAC' ${CH_NAMES[0]} $y
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'DAC' ${CH_NAMES[1]} $y
    else
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'DAC' ${CH_NAMES[$n]} $y
    fi
}

function get_dac {
    CH_NAMES=("Left" "Right")
    n=$(($1-1))

    x=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget 'DAC' | /usr/bin/grep -o "${CH_NAMES[$n]}: [0-9]*" | /usr/bin/cut -d' ' -f2)
    /usr/bin/echo $(lin_conv 0 255 -127.5 0 $x)
}

function set_hp_vol {
    x=$1
    y=$(lin_conv -33 12 0 15 $x)
    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'Headphone' $y
}

function get_hp_vol {
    x=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget 'Headphone' | /usr/bin/grep -o "Mono:.* [0-9]*" | /usr/bin/cut -d' ' -f3)
    /usr/bin/echo $(lin_conv 0 15 -33 12 $x)
}

function set_tbp {
    n=$(($1-1))
    val=$2
    CH_NAMES=("Left" "Right")
    VALUES=("on" "off")

    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "${CH_NAMES[$n]} True-Bypass" ${VALUES[$val]}
}

function get_tbp {
    n=$(($1-1))
    CH_NAMES=("Left" "Right")

    /usr/bin/echo $(/usr/bin/amixer -D hw:${SOUNDCARD} sget "${CH_NAMES[$n]} True-Bypass" | /usr/bin/grep -oE "(\[.*\])" | /usr/bin/tr -d '[]')
}

# NOTE: we do not have this for the Duo X
function set_hp_bypass {
    val="$1"

    if [[ "$val" == "off" ]]; then
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'AUX OUT MUX' 1
    elif [[ "$val" == "on" ]]; then
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'AUX OUT MUX' 2
    elif [[ "$val" == "toggle" ]]; then
        if [[ "$(get_hp_bypass)" == "on" ]]; then
            set_hp_bypass "off"
        else
            set_hp_bypass "on"
        fi
    fi
}

# NOTE: we do not have this for the Duo X
function get_hp_bypass {
    val=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget 'AUX OUT MUX' | /usr/bin/grep -o "Mono:.* [0-9]*" | /usr/bin/cut -d' ' -f2)
    if [[ "$val" == "1" ]]; then
        /usr/bin/echo "off"
    elif [[ "$val" == "2" ]]; then
        /usr/bin/echo "on"
    fi
}

function get_xvol {
    n=$(($1-1))

    CH_NAMES=("Left" "Right")
    stage=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "${CH_NAMES[$n]} Gain Stage" | /usr/bin/grep -o "Mono: [0-9]" | /usr/bin/cut -d' ' -f2)
    pga=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget 'PGA Gain' | /usr/bin/grep -o "${CH_NAMES[$n]}: [0-9]*" | /usr/bin/cut -d' ' -f2)

    if [[ $stage -gt 2 ]]; then
        /usr/bin/echo 100
    elif [[ $stage -eq 2 ]]; then
        /usr/bin/echo $((26 + pga))
    elif [[ $stage -eq 1 ]]; then
        /usr/bin/echo $((11 + pga))
    else
        /usr/bin/echo $pga
    fi
}

function get_dvol {
    n=$(($1-1))

    CH_NAMES=("Left" "Right")
    stage=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "${CH_NAMES[$n]} Gain Stage" | /usr/bin/grep -o "Mono: [0-9]" | /usr/bin/cut -d' ' -f2)
    pga=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget 'PGA Gain' | /usr/bin/grep -o "${CH_NAMES[$n]}: [0-9]*" | /usr/bin/cut -d' ' -f2)

    if [[ $stage -gt 2 ]]; then
        /usr/bin/echo 100
    elif [[ $stage -eq 2 ]]; then
        /usr/bin/echo $((50 + pga))
    elif [[ $stage -eq 1 ]]; then
        /usr/bin/echo $((24 + pga))
    else
        /usr/bin/echo $pga
    fi
}

function set_xvol {
    n=$(($1-1))
    val=$(/usr/bin/echo $2 | /usr/bin/cut -d'.' -f1)

    if [[ $val -lt 0 ]] || [[ $val -gt 78 ]]; then
        return
    fi

    if [[ $val -ge 48 ]]; then
        stage=2
        pga=$((val - 26))
    elif [[ $val -ge 32 ]]; then
        stage=1
        pga=$((val - 11))
    else
        stage=0
        pga=$val
    fi

    if [[ $n -eq -1 ]]; then
        n=0
        stereo_link=1
    fi

    PGA_CHANNEL_NAMES=("frontleft" "frontright")
    STAGE_CHANNEL_NAMES=("Left" "Right")

    s=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "${STAGE_CHANNEL_NAMES[$n]} Gain Stage" | /usr/bin/grep -o "Mono: [0-9]" | /usr/bin/cut -d' ' -f2)

    # reduce pga to 0 first if changing to a different gain stage
    if [[ $s -ne $stage ]]; then
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[$n]} 0

        if [[ $stereo_link -eq 1 ]]; then
            /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[1]} 0
        elif [[ $n -eq 1 ]]; then
            # some units cannot handle input2 being changed too fast
            /usr/bin/sleep 0.1
        fi
    fi

    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "${STAGE_CHANNEL_NAMES[$n]} Gain Stage" $stage
    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[$n]} $pga

    if [[ $stereo_link -eq 1 ]]; then
        /usr/bin/sleep 0.1 # some units cannot handle input2 being changed too fast
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "${STAGE_CHANNEL_NAMES[1]} Gain Stage" $stage
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[1]} $pga
    fi
}

function set_dvol {
    n=$(($1-1))
    val=$(/usr/bin/echo $2 | /usr/bin/cut -d'.' -f1)

    if [[ $val -lt 0 ]] || [[ $val -gt 102 ]]; then
        return
    fi

    if [[ $val -ge 64 ]]; then
        stage=2
        pga=$((val - 50))
    elif [[ $val -ge 38 ]]; then
        stage=1
        pga=$((val - 24))
    else
        stage=0
        pga=$val
    fi

    if [[ $n -eq -1 ]]; then
        n=0
        stereo_link=1
    fi

    PGA_CHANNEL_NAMES=("frontleft" "frontright")
    STAGE_CHANNEL_NAMES=("Left" "Right")

    s=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "${STAGE_CHANNEL_NAMES[$n]} Gain Stage" | /usr/bin/grep -o "Mono: [0-9]" | /usr/bin/cut -d' ' -f2)

    # reduce pga to 0 first if changing to a different gain stage
    if [[ $s -ne $stage ]]; then
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[$n]} 0

        if [[ $stereo_link -eq 1 ]]; then
            /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[1]} 0
        fi
    fi

    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "${STAGE_CHANNEL_NAMES[$n]} Gain Stage" $stage
    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[$n]} $pga

    if [[ $stereo_link -eq 1 ]]; then
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "${STAGE_CHANNEL_NAMES[1]} Gain Stage" $stage
        /usr/bin/amixer -q -D hw:${SOUNDCARD} sset 'PGA Gain' ${PGA_CHANNEL_NAMES[1]} $pga
    fi
}

# NOTE: Duo X only stuff follows
function get_cvhp {
    val=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "Headphone/CV Mode" | /usr/bin/grep -oE "(\[.*\])" | /usr/bin/tr -d '[]')
    if [[ "$val" == "off" ]]; then
        /usr/bin/echo "hp"
    elif [[ "$val" == "on" ]]; then
        /usr/bin/echo "cv"
    fi
}
function set_cvhp {
    val=$1
    if [[ "$val" == "hp" ]] || [[ "$val" == "0" ]]; then
        val="off"
    elif [[ "$val" == "cv" ]] || [[ "$val" == "1" ]]; then
        val="on"
    fi
    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "Headphone/CV Mode" "$val"
}
function get_cvexp {
    val=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "CV/Exp.Pedal Mode" | /usr/bin/grep -oE "(\[.*\])" | /usr/bin/tr -d '[]')
    if [[ "$val" == "off" ]]; then
        /usr/bin/echo "cv"
    elif [[ "$val" == "on" ]]; then
        /usr/bin/echo "exp"
    fi
}
function set_cvexp {
    val=$1
    if [[ "$val" == "cv" ]] || [[ "$val" == "0" ]]; then
        val="off"
    elif [[ "$val" == "exp" ]] || [[ "$val" == "1" ]]; then
        val="on"
    fi
    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "CV/Exp.Pedal Mode" "$val"
}
function get_exppedal {
    val=$(/usr/bin/amixer -D hw:${SOUNDCARD} sget "Exp.Pedal Mode" | /usr/bin/grep -oE "(\[.*\])" | /usr/bin/tr -d '[]')
    if [[ "$val" == "off" ]]; then
        /usr/bin/echo "tip"
    elif [[ "$val" == "on" ]]; then
        /usr/bin/echo "ring"
    fi
}
function set_exppedal {
    val=$1
    if [[ "$val" == "tip" ]] || [[ "$val" == "0" ]]; then
        val="off"
    elif [[ "$val" == "ring" ]] || [[ "$val" == "1" ]]; then
        val="on"
    fi
    /usr/bin/amixer -q -D hw:${SOUNDCARD} sset "Exp.Pedal Mode" "$val"
}

function get_dall_in {
    val1=$(get_dvol 1)
    val2=$(get_dvol 2)
    /usr/bin/echo "${val1}|${val2}"
}

function get_dall_out {
    val1=$(get_dac 1)
    val2=$(get_dac 2)
    val3=$(get_hp_bypass | /usr/bin/grep -q "on" && /usr/bin/echo "2" || /usr/bin/echo "1")
    /usr/bin/echo "${val1}|${val2}|${val3}"
}

function get_xall_in {
    val1=$(get_xvol 1)
    val2=$(get_xvol 2)
    val3=$(get_cvexp | /usr/bin/grep -q "exp" && /usr/bin/echo "1" || /usr/bin/echo "0")
    val4=$(get_exppedal | /usr/bin/grep -q "ring" && /usr/bin/echo "1" || /usr/bin/echo "0")
    /usr/bin/echo "${val1}|${val2}|${val3}|${val4}"
}

function get_xall_out {
    val1=$(get_dac 1)
    val2=$(get_dac 2)
    val3=$(get_hp_vol)
    val4=$(get_cvhp | /usr/bin/grep -q "cv" && /usr/bin/echo "1" || /usr/bin/echo "0")
    /usr/bin/echo "${val1}|${val2}|${val3}|${val4}"
}

function main {
    SOURCE=$1
    CHANNEL=$2
    CONTROL=$3
    VALUE=$4

    if [[ "$SOURCE" == "save" ]]; then
        /usr/sbin/alsactl store ${SOUNDCARD} -f /data/asound.state

    elif [[ "$SOURCE" == "in" ]]; then
        if [[ "$VALUE" == "" ]]; then
            if [[ "$CHANNEL" == "dall" ]]; then
                get_dall_in
            elif [[ "$CHANNEL" == "xall" ]]; then
                get_xall_in
            elif [[ "$CONTROL" == "vol" ]]; then
                get_pga $CHANNEL
            elif [[ "$CONTROL" == "stg" ]]; then
                get_stage $CHANNEL
            elif [[ "$CONTROL" == "xvol" ]]; then
                get_xvol $CHANNEL
            elif [[ "$CONTROL" == "dvol" ]]; then
                get_dvol $CHANNEL
            else
                /usr/bin/echo "unknown in command $CONTROL"
            fi
        else
            if [[ "$CONTROL" == "vol" ]]; then
                set_pga $CHANNEL $VALUE
            elif [[ "$CONTROL" == "stg" ]]; then
                set_stage $CHANNEL $VALUE
            elif [[ "$CONTROL" == "xvol" ]]; then
                set_xvol $CHANNEL $VALUE
            elif [[ "$CONTROL" == "dvol" ]]; then
                set_dvol $CHANNEL $VALUE
            else
                /usr/bin/echo "unknown in command $CONTROL"
            fi
        fi

    elif [[ "$SOURCE" == "out" ]]; then
        if [[ "$VALUE" == "" ]]; then
            if [[ "$CHANNEL" == "dall" ]]; then
                get_dall_out
            elif [[ "$CHANNEL" == "xall" ]]; then
                get_xall_out
            elif [[ "$CONTROL" == "dvol" ]] || [[ "$CONTROL" == "xvol" ]]; then
                get_dac $CHANNEL
            else
                /usr/bin/echo "unknown out command $CONTROL"
            fi
        else
            if [[ "$CONTROL" == "dvol" ]] || [[ "$CONTROL" == "xvol" ]]; then
                set_dac $CHANNEL $VALUE
            else
                /usr/bin/echo "unknown out command $CONTROL"
            fi
        fi

    elif [[ "$SOURCE" == "tbp" ]]; then
        VALUE=$3

        if [[ "$VALUE" == "" ]]; then
            get_tbp $CHANNEL
        else
            set_tbp $CHANNEL $VALUE
        fi

    elif [[ "$SOURCE" == "hp" ]]; then
        CONTROL=$2
        VALUE=$3

        if [[ "$VALUE" == "" ]]; then
            if [[ "$CONTROL" == "dvol" ]] || [[ "$CONTROL" == "xvol" ]]; then
                get_hp_vol
            elif [[ "$CONTROL" == "byp" ]]; then
                get_hp_bypass
            fi
        else
            if [[ "$CONTROL" == "dvol" ]] || [[ "$CONTROL" == "xvol" ]]; then
                set_hp_vol $VALUE
            elif [[ "$CONTROL" == "byp" ]]; then
                set_hp_bypass $VALUE
            fi
        fi

    # Duo X CV stuff
    elif [[ "$SOURCE" == "cvhp" ]]; then
        VALUE=$2
        if [[ "$VALUE" == "" ]]; then
            get_cvhp
        else
            set_cvhp $VALUE
        fi
    elif [[ "$SOURCE" == "cvexp" ]]; then
        VALUE=$2
        if [[ "$VALUE" == "" ]]; then
            get_cvexp
        else
            set_cvexp $VALUE
        fi
    elif [[ "$SOURCE" == "exppedal" ]]; then
        VALUE=$2
        if [[ "$VALUE" == "" ]]; then
            get_exppedal
        else
            set_exppedal $VALUE
        fi
    fi
}

# run Forrest, run
main $@
