#!/bin/env bash

init (){
	case $1 in
		"sink")
			sink=$2
			pactl list sinks short | grep $sink && exit
			pactl load-module module-null-sink sink_name=$sink sink_properties="device.description="$sink""
		;;
		"source")
			source=$2
			pactl list sources short | grep $source && exit
			pactl load-module module-null-sink sink_name=$source"_sink" # sink_properties=device.description=$source"_sink"
			if which pulseaudio 2>/dev/null 1>&2; then
				pactl load-module module-remap-source source_name=$source master=$source"_sink.monitor" source_properties=device.description=$source 
			else
				pactl load-module module-remap-source source_name=$source master=$source"_sink" source_properties=device.description=$source 
			fi
		;;
	esac
}

connect (){
	source=$1
	sink=$2
	latency=${3:-200}

	[ -z "$source" -o -z "$sink" ] && exit
	LC_ALL=C pactl list modules | grep -B 2 "$sink source=$source" >/dev/null || \
	pactl load-module module-loopback sink=$sink source=$source sink_dont_move=true source_dont_move=true latency_msec=$latency
}

disconnect (){
	source=$1
	sink=$2

	[ -z "$source" -o -z "$sink" ] && exit
	index="$(LC_ALL=C pactl list modules | grep -B 2 "$sink source=$source" | grep Module | sed 's/.*#//g')"
	[ -z "$index" ] || pactl unload-module $index
}

remove (){
	device=$1

	index="$(LC_ALL=C pactl list modules | grep -B 2 "$device " | grep Module | sed 's/.*#//g')"
	[ -z "$index" ] || pactl unload-module $index
}

mute (){
	device_type=$1
	device=$2
	status=$3

	pactl set-$device_type-mute $device $status
}

list_virtual_sinks (){
	list=$(LC_ALL=C pactl list sinks | 
		awk '/Sink #/{print $2} /Source #/{print $2} /Name:/ {print $0} 
			/Description/ {print $0} /ladspa/{print $0} /remap-source/ {print $0}
			/module-null-sink/ {print $0} /Volume/ {print $0} /null-audio-sink/ {print $0}' |
			sed '/Base Volume/d; s/%.*//g; s/:.*\/ /: /g; /Volume/s/$/%/; /id/!s/: /:"/g; /#/!s/$/"/g;')


	#printf "%s" "$list"
	#exit

	# turn into json
	list=$(echo "$list" | sed ':a $!N;s/\n/, /;ta P;D;' |
		sed ':a $!N;s/#/\n{"id":/;ta P;D;' |
		sed '/^$/d; s/ 	/ /g; s/Description/"description"/g; 
			s/Volume:"/"volume":/g; s/%"//g; s/Name/"name"/g;
			s/$/}/; s/, }/}/' |
		sed '/null/!d; s/, \tfactory.*"//g' |
		grep -vwE Monitor | grep -vwE remap-source | grep -vwE module-remap-source |
		grep -vwE ladspa | grep -vwE .*_sink | grep -vwE .*rnnoise | sed 's/, Driver:.*\.c"//g')

	printf "%s" "$list"
}

list_virtual_sources (){
	list=$(LC_ALL=C pactl list sources | 
		awk '/Source #/{print $2} /Name:/ {print $0} 
			/Description/ {print $0} /remap-source/ {print $0}
			/Volume/ {print $0}' |
			sed '/Base Volume/d; s/%.*//g; s/:.*\/ /: /g; /Volume/s/$/%/; /id/!s/: /:"/g; /#/!s/$/"/g;')

	#printf "%s" "$list"
	#exit
	# turn into json
	list=$(echo "$list" | sed ':a $!N;s/\n/, /;ta P;D;' |
		sed ':a $!N;s/#/\n{"id":/;ta P;D;' |
		sed '/^$/d; s/ 	/ /g; s/Description/"description"/g; 
			s/Volume:"/"volume":/g; s/%"//g; s/Name/"name"/g;
			s/$/}/; s/, }/}/' |
		grep -vwE Monitor | grep -wE remap | sed 's/, \t.*node.group.*"//g; s/, Driver:.*\.c"//g')

	printf "%s" "$list"
}


volume (){
	vi=$2
	[ -z $vi ] && exit
	pactl set-$1-volume $vi $3%
}

rename () {
	pacmd update-sink-proplist $1 device.description=$2
}


list (){
	list=$(LC_ALL=C pactl list $1 | \
		awk '/Sink #/{print $2} /Source #/{print $2} /Name:/ {print $0} 
			/Description/ {print $0} /ladspa/{print $0} /remap-source/ {print $0}
			/module-null-sink/ {print $0} /Volume/ {print $0} /null-audio-sink/ {print $0}' |\
			sed '/Base Volume/d; s/%.*//g; s/:.*\/ /: /g; /Volume/s/$/%/; /id/!s/: /:"/g; /#/!s/$/"/g;')

	# turn into json
	list=$(echo "$list" | sed ':a $!N;s/\n/, /;ta P;D;' |\
		sed ':a $!N;s/#/\n{"id":/;ta P;D;' |\
		sed '/^$/d; s/ 	/ /g; s/Description/"description"/g; s/Volume:"/"volume":/g; s/%"//g; s/Name/"name"/g; s/$/}/; s/, }/}/' |\
		grep -vwE Monitor | grep -vwE null | grep -vwE remap-source | grep -vwE module-remap-source | grep -vwE ladspa)

	printf "%s" "$list"
}

get_source_by_id() {
	id=$1
	LC_ALL=C pactl list sources | grep "Source #$id$" -A 3 | grep 'Name: ' | sed 's/.*: //g'
}

move_sink_input_master (){
	sink_name=$1
	master=$2
	index=$(LC_ALL=C pactl list modules | grep -B 2 "$sink_name m" | grep Module | sed 's/.*#//g')
	[ -z "$index" ] && exit
	id=$(LC_ALL=C pactl list sink-inputs | grep -B 2 $index | grep '#' | sed 's/.*#//g')
	[ -z "$id" ] && exit
	pactl move-sink-input $id $master
}

move_sink_input (){
	app=$1
	master=$2
	pactl move-sink-input $app $master
}

move_source_output () {
	app=$1
	master=$2
	pactl move-source-output $app $master
}

list_source_outputs (){
	apps=$(LC_ALL=C pactl list source-outputs | awk '/Source Output #/{print $3} /Source: /{print $1 $2} /icon.name =/{print $0} /binary/{print $0} /application.name = /{print $0}')

	# turn source number into name
	sources=$(echo "$apps" | grep Source:)
	for source in $sources; do
		name=$(LC_ALL=C pactl list sources | grep -A 2 "#$(echo $source | sed "s/Source://g")$" | awk '/Name/ {print $2}')
		apps=$(echo "$apps" | sed "s/$source$/device:\"$name\"/")
	done


	# sed hell to transform into json
	apps=$(echo "$apps" | sed ':a $!N;s/\n/, /;ta P;D;' |\
		sed ':a $!N;s/#/\n{"id":/;ta P;D;' |\
		sed '/^$/d; s/application\.name = /name:/g; s/application.icon_name = /"icon":/g; s/media.icon_name = /"icon":/g; s/application.process.binary = /"binary":/g; s/$/}/; s/, }/}/' |\
		grep -vwE pavucontrol | grep -vwE pulse-vumeter | grep name | \
		sed 's/ 		/ /g; s/device/"device"/g; s/name/"name"/g')
	printf "%s" "$apps"
}

list_sink_inputs (){
	apps=$(LC_ALL=C pactl list sink-inputs | awk '/Sink Input #/{print $3} /Sink: /{print $1$2} /icon_name/{print $0} /application.name = /{print $0}')

	# turn sink number into name
	sinks=$(echo "$apps" | grep Sink:)
	for sink in $sinks; do
		name=$(LC_ALL=C pactl list sinks | grep -A 2 "\#$(echo $sink | sed "s/Sink://g")$" | awk '/Name/ {print $2}')
		apps=$(echo "$apps" | sed "s/$sink$/\"device\":\"$name\"/")
	done

	# sed hell to transform into json
	apps=$(echo "$apps" | sed ':a $!N;s/\n/, /;ta P;D;' |\
		sed ':a $!N;s/#/\n{"id":/;ta P;D;' |\
		sed '/^$/d;/^$/d; s/application.icon_name = /"icon":/g; s/media.icon_name = /"icon":/g; s/application.name = /"name":/g' |\
		grep name |\
		sed 's/ 		/ /g' | sed 's/, $//; s/$/}/')
	printf "%s" "$apps"
}

eq (){
	status=$1
	sink_name=$2
	master=$3
	control=$4
	label=mbeq
	plugin=mbeq_1197

	for i in '/usr/lib/ladspa' '/usr/local/lib/ladspa'; do
		[ -f "$i/$plugin.so" ] && found=1
	done

	[ -z "$found" ] && exit

	info="$(LC_ALL=C pactl list modules | grep -B 2 "$sink_name m")"
	if [ "$status" = "remove" ];then
		index=$(echo "$info" | grep Module | sed 's/.*#//g')
		[ -z "$index" ] || pactl unload-module $index
		exit
	fi

	# update module if control is different
	if ! [ -z "$info" ]; then
		index=$(echo "$info" | grep Module | sed 's/.*#//g')
		if ! [ -z "$index" ]; then
			old_control=$(echo "$info" | grep control | sed 's/.*control=//g')
			[ "$control" = "$old_control" ] && [ "$master" != 'remove' ] && exit
			pactl unload-module $index
		fi
	fi


	pactl load-module module-ladspa-sink \
		sink_name=$sink_name	\
		master=$master			\
		plugin=mbeq_1197		\
		label=mbeq				\
		control=$control		#\
		#sink_properties=device.description=$sink_name 
}

compressor (){
	sink_name=$1
	master=$2
	control=$3

	#LC_ALL=C pactl list modules | grep "$sink_name" >/dev/null || \
	index=$(LC_ALL=C pactl list modules | grep -B 2 "$sink_name m" | grep Module | sed 's/.*#//g')
	[ -z "$index" ] || pactl unload-module $index

	[ "$master" = "remove" ] && exit

	pactl load-module module-ladspa-sink \
		sink_properties=device.description=$sink_name \
		sink_name=$sink_name	\
		master=$master			\
		plugin=sc4m_1916		\
		label=sc4m				\
		control=$control
}

ladspa() {
	master=$1
	sink_name=$2
	control=$3
	status=$4
	latency=${5:-200}
	label=$6
	plugin=$7

	for i in '/usr/lib/ladspa' '/usr/local/lib/ladspa'; do
		[ -f "$i/$plugin.so" ] && found=1
	done

	[ -z "$found" ] && exit 1

	if [ "$status" = "connect" ]; then
		pactl list sinks short | grep "$sink_name.*null" > /dev/null && exit
		pactl load-module module-null-sink sink_name=$sink_name

		pactl load-module module-ladspa-sink master=$sink_name sink_name=$sink_name"_in" label=$label plugin=$plugin control=$control
		pactl load-module module-loopback sink=$sink_name"_in" source=$master latency_msec=$latency source_dont_move=true sink_dont_move=true
	else
		index=$(LC_ALL=C pactl list modules | grep -B 2 $label | grep -B 2 -A 1 ladspa | grep Module | sed 's/.*#//g')
		[ -z "$index" ] || pactl unload-module $index

		#index=$(LC_ALL=C pactl list modules | awk '/Module/{print $0} /Name:/ {print $0} /Argument/' | grep -B 2 $sink_name | grep -B 1 null | grep Module | sed 's/.*#//g')
		#[ -z "$index" ] || pactl unload-module $index
	fi

}

rnnoise() {
	sink_name=$1
	master=$2
	control=$3
	status=$4
	latency=${5:-200}


	label=noise_suppressor_mono
	plugin=librnnoise_ladspa
 
	for i in '/usr/lib/ladspa' '/usr/local/lib/ladspa'; do
		if [ -f "$i/rnnoise_ladspa.so" ]; then
			label=noisetorch
			plugin=rnnoise_ladspa
			found=1
			break
		elif [ -f "$i/$plugin.so" ]; then
			found=1
			break
		fi

	done

	[ -z "$found" ] && exit 1

	if [ "$status" = "connect" ]; then
		pactl list sinks short | grep "$sink_name" > /dev/null && exit
		
		# sink that will recive filtered data
		pactl load-module module-null-sink sink_name=$sink_name

		# sink that will filter data
		pactl load-module module-ladspa-sink sink_name=$sink_name"_in" master=$sink_name label=$label plugin=$plugin control=$control

		# loopback from source to ladspa sink
		pactl load-module module-loopback sink=$sink_name"_in" source=$master latency_msec=$latency source_dont_move=true sink_dont_move=true

	else
		# unload sink that will recive filtered data
		index="$(LC_ALL=C pactl list modules | grep -B 2 "$sink_name$" | grep Module | sed 's/.*#//g')"
		[ -z "$index" ] || pactl unload-module $index

		# pipewire doesn't remove devices that depend on others,
		# so we have to remove them by hand
		if ! [ which pulseaudio 2>/dev/null 1>&2 ]; then
			# unload sink that will filter data
			index="$(LC_ALL=C pactl list modules | grep -B 2 "$sink_name"_in" m" | grep Module | sed 's/.*#//g')"
			[ -z "$index" ] ||  pactl unload-module $index

			# loopback from source to ladspa sink
			index="$(LC_ALL=C pactl list modules | grep -B 2 "$sink_name"_in" source=$master" | grep Module | sed 's/.*#//g')"
			[ -z "$index" ] || pactl unload-module $index
		fi

	fi
}

get_sink_input_chann_num (){
	output=$(LC_ALL=C pactl list sink-inputs | grep -e 'Sink Input #' -e 'Volume' | sed 's/.*#//g' | grep $1$ -A 1 | grep Volume)
	printf "%s" "${output//[^%]}" | wc -m
}

get_source_output_chann_num (){
	output=$(LC_ALL=C pactl list source-outputs | grep -e 'Source Output #' -e 'Volume' | sed 's/.*#//g' | grep $1$ -A 1 | grep Volume)
	printf "%s" "${output//[^%]}" | wc -m
}

get_sink_input_volume(){
	output=$(LC_ALL=C pactl list sink-inputs | grep -e 'Sink Input #' -e 'Volume' | sed 's/.*#//g' | grep $1$ -A 1 | grep Volume | sed -e 's,.* \([0-9][0-9]*\)%.*,\1,')
	printf "%d" $output 
}

get_source_output_volume(){
	output=$(LC_ALL=C pactl list source-outputs | grep -e 'Source Output #' -e 'Volume' | sed 's/.*#//g' | grep $1$ -A 1 | grep Volume | sed -e 's,.* \([0-9][0-9]*\)%.*,\1,')
	printf "%d" $output 
}

if ! pactl info >/dev/null 2>&1; then
	echo "ERROR PULSEAUDIO NOT RUNNING" >&2 
	exit 1
fi

case $1 in
	"init")
		init $2 $3
	;;

	"connect") 
		connect $2 $3 $4
	;;

	"disconnect")
		disconnect $2 $3 $4
	;;

	"remove")
		remove $2 $3
	;;

	"volume")
		volume $2 $3 $4
	;;

	"list")
		list $2
	;;

	"rename")
		rename $2 $3
	;;

	"mute")
		mute $2 $3 $4
	;;

	"move_sink_input_master")
		move_sink_input_master $2 $3
	;;

	"get-source-by-id")
		get_source_by_id $2
	;;

	"eq")
		eq $2 $3 $4 $5
	;;

	"compressor")
		compressor $2 $3 $4
	;;

	"rnnoise")
		rnnoise $2 $3 $4 $5 $6
	;;

	"ladspa")
		ladspa $2 $3 $4 $5 $6 $7 $8
	;;

	"list-sink-inputs")
		list_sink_inputs
	;;

	"list-source-outputs")
		list_source_outputs
	;;

	"move-sink-input")
		move_sink_input $2 $3
	;;

	"move-source-output")
		move_source_output $2 $3
	;;

	"get-sink-input-chann")
		get_sink_input_chann_num $2
	;;

	"get-source-output-chann")
		get_source_output_chann_num $2
	;;

	"get-sink-input-volume")
		get_sink_input_volume $2
	;;

	"list-virtual-sinks")
		list_virtual_sinks
	;;

	"list-virtual-sources")
		list_virtual_sources
	;;

	"get-source-output-volume")
		get_source_output_volume $2
	;;
	"*") echo "command not found";;
esac
