#!/usr/bin/env bash

##################################################################
# Polybar Pulseaudio Control                                     #
# https://github.com/marioortizmanero/polybar-pulseaudio-control #
##################################################################

# Script configuration (more info in the README)
OSD="no"  # On Screen Display message for KDE if enabled
INC=2  # Increment when lowering/rising the volume
MAX_VOL=100  # Maximum volume
AUTOSYNC="no"  # All programs have the same volume if enabled
VOLUME_ICONS=( " " " " " " )  # Volume icons array, from lower volume to higher
MUTED_ICON=" "  # Muted volume icon
MUTED_COLOR="%{F#A9AA83}"  # Color when the audio is muted
DEFAULT_SINK_ICON=""  # The default sink icon if a custom one isn't found
CUSTOM_SINK_ICONS=("" "")  # Custom sink icons in index of sink order
NOTIFICATIONS="no"  # Notifications when switching sinks if enabled
SINK_BLACKLIST=(  )  # Index blacklist for sinks when switching between them

# Environment constants
LANGUAGE=en_US  # Functions of this scripts depends on English outputs of pactl

# Global script variables
isMuted="no"
activeSink=""
endColor="%{F-}"

function getCurVol {
    curVol=$(pacmd list-sinks | grep -A 15 'index: '"$activeSink"'' | grep 'volume:' | grep -E -v 'base volume:' | awk -F : '{print $3}' | grep -o -P '.{0,3}%'| sed s/.$// | tr -d ' ')
}

function getCurSink {
    activeSink=$(pacmd list-sinks | awk '/* index:/{print $3}')
}

function volMuteStatus {
    isMuted=$(pacmd list-sinks | grep -A 15 "index: $activeSink" | awk '/muted/{ print $2}')
}

function getSinkInputs {
    inputArray=$(pacmd list-sink-inputs | grep -B 4 "sink: $1 " | awk '/index:/{print $2}')
}

function volUp {
    getCurVol
    maxLimit=$((MAX_VOL - INC))

    if [ "$curVol" -le "$MAX_VOL" ] && [ "$curVol" -ge "$maxLimit" ]; then
        pactl set-sink-volume "$activeSink" "$MAX_VOL%"
    elif [ "$curVol" -lt "$maxLimit" ]; then
        pactl set-sink-volume "$activeSink" "+$INC%"
    fi

    getCurVol

    if [ ${OSD} = "yes" ]; then
        qdbus org.kde.kded /modules/kosd showVolume "$curVol" 0
    fi

    if [ ${AUTOSYNC} = "yes" ]; then
        volSync
    fi
}

function volDown {
    pactl set-sink-volume "$activeSink" "-$INC%"
    getCurVol

    if [ ${OSD} = "yes" ]; then
        qdbus org.kde.kded /modules/kosd showVolume "$curVol" 0
    fi

    if [ ${AUTOSYNC} = "yes" ]; then
        volSync
    fi
}

function volSync {
    getSinkInputs "$activeSink"
    getCurVol

    for each in $inputArray; do
        pactl set-sink-input-volume "$each" "$curVol%"
    done
}

function volMute {
    case "$1" in
        mute)
            pactl set-sink-mute "$activeSink" 1
            curVol=0
            status=1
            ;;
        unmute)
            pactl set-sink-mute "$activeSink" 0
            getCurVol
            status=0
            ;;
    esac

    if [ ${OSD} = "yes" ]; then
        qdbus org.kde.kded /modules/kosd showVolume ${curVol} ${status}
    fi

}

function changeDevice {
    # Treat pulseaudio sink list to avoid calling pacmd list-sinks twice
    o_pulseaudio=$(pacmd list-sinks| grep -e 'index' -e 'device.description')

    # Get all sink indices in a list
    sinks=($(echo "$o_pulseaudio" | grep index | awk -F': ' '{print $2}'))

    # Get present default sink index
    activeSink=$(echo "$o_pulseaudio" | grep "\* index" | awk -F': ' '{print $2}')

    # Set new sink index, checks that it's not in the blacklist
    newSink=$activeSink

    # Remove blacklist devices from the sink list
    sinks=($(comm -23 <(echo "${sinks[@]}" | tr ' ' '\n' | sort) <(echo "${SINK_BLACKLIST[@]}" | tr ' ' '\n' | sort) | tr '\n' ' '))

    # If the resulting list is empty, do nothing
    if [ -z "${sinks}" ]; then exit; fi

    # If the current sink is greater or equal than last one, pick the first
    # sink in the list. Otherwise just pick the next sink avaliable.
    if [ "${activeSink}" -ge "${sinks[-1]}" ]; then
        newSink=${sinks[0]}
    else
        for sink in "${sinks[@]}"; do
            if [ "${activeSink}" -lt "${sink}" ]; then
                newSink=$sink
                break
            fi
        done
    fi

    # The new sink is set
    pacmd set-default-sink "${newSink}"

    # Move all audio threads to new sink
    inputs=$(pactl list sink-inputs short | cut -f 1)
    for i in $inputs; do
        pacmd move-sink-input "$i" "$newSink"
    done

    if [ $NOTIFICATIONS = "yes" ]; then
        sendNotification
    fi
}

function sendNotification {
    o_pulseaudio=$(pacmd list-sinks| grep -e 'index' -e 'device.description')
    deviceName=$(echo "$o_pulseaudio" | sed -n '/* index/{n;p;}' | grep -o '".*"' | sed 's/"//g')
    notify-send "Output cycle" "Changed output to ${deviceName}" --icon=audio-headphones-symbolic
}

function listen {
    firstrun=0

    pactl subscribe 2>/dev/null | {
        while true; do
            {
                # If this is the first time just continue
                # and print the current state
                # Otherwise wait for events
                # This is to prevent the module being empty until
                # an event occurs
                if [ $firstrun -eq 0 ]
                then
                    firstrun=1
                else
                    read -r event || break
                    if ! echo "$event" | grep -e "on card" -e "on sink" -e "on server"
                    then
                        # Avoid double events
                        continue
                    fi
                fi
            } &>/dev/null
            output
        done
    }
}

function output() {
    if [ -z "$(pgrep pulseaudio)" ]; then echo "Pulseaudio not running"; return 1; fi

    getCurSink
    getCurVol
    volMuteStatus

    # Fixed volume icons over max volume
    iconsLen="${#VOLUME_ICONS[@]}"
    if [ "$iconsLen" -ne 0 ]; then
        volSplit=$(( MAX_VOL / iconsLen ))
        for (( i=1; i<=iconsLen; i++ )); do
            if (( i*volSplit >= curVol )); then
                volIcon="${VOLUME_ICONS[$((i-1))]}"
                break
            fi
        done
    else
        volIcon=""
    fi

    # Uses custom sink icon if the array contains one
    sinksLen=${#CUSTOM_SINK_ICONS[@]}
    if [ "$activeSink" -le "$((sinksLen - 1))" ]; then
        sinkIcon=${CUSTOM_SINK_ICONS[$activeSink]}
    else
        sinkIcon=$DEFAULT_SINK_ICON
    fi

    # Showing the formatted message
    if [ "${isMuted}" = "yes" ]; then
        echo "${MUTED_COLOR}${MUTED_ICON}${curVol}% ${sinkIcon} ${activeSink}${endColor}"
    else
        echo "${volIcon}${curVol}% ${sinkIcon} ${activeSink}"
    fi
}


getCurSink
case "$1" in
    --up)
        volUp
        ;;
    --down)
        volDown
        ;;
    --togmute)
        volMuteStatus
        if [ "$isMuted" = "yes" ]
        then
            volMute unmute
        else
            volMute mute
        fi
        ;;
    --mute)
        volMute mute
        ;;
    --unmute)
        volMute unmute
        ;;
    --sync)
        volSync
        ;;
    --listen)
        # Listen for changes and immediately create new output for the bar
        # This is faster than having the script on an interval
        listen
        ;;
    --change)
        # Changes the audio device
        changeDevice
        ;;
    *)
        # By default print output for bar
        output
        ;;
esac