#!/usr/bin/bash

# MoodyRain v2 by Glutanimate
# License: GNU GPLv3
# Dependencies: yad, sox/mpv, vorbis-tools

# Settings

AudioPlayer="sox" # must be either mpv or sox

# Variables

Executable="$(readlink -f "$0")"
ProgDir="${Executable%/*}"
SoundDir="$ProgDir/sounds"
ConfigFile="$HOME/.config/moodyrain.cfg"

# Gui

TrayIcon="rhythmbox-panel"
WmIcon="preferences-desktop-multimedia"
ThumbIcon="preferences-desktop-multimedia"
YadTitle="MoodyRain"
WmClass="moodyrain"
YadHeight="150"
YadWidth="600"

GuiTxtDescr="<b>Please select one or more ambient sounds</b>.
Place ogg files in './sounds' to add more choices."
GuiTxtPlay="Play selected sounds without saving soundscape"
GuiTxtPlaySave="Save soundscape and play selected sounds"
GuiTxtCancel="Cancel and quit"
GuiTxtReset="Reset all volume sliders to 0"

# Functions

arg_compile_sounds(){
  unset Sounds SoundID File
  while IFS= read -r -d '' File; do
    if [[ ! "$(file -Lib "$File")" == */ogg* ]]; then
      echo "Error: '$File' is not an ogg file. Ignoring."
      continue
    fi
    TagData="$(ogginfo "$File")"
    Sounds[$SoundID]="$File"
    Titles[$SoundID]="$(echo "$TagData" | sed -n 's/\tTITLE=//ip')"
    Artists[$SoundID]="$(echo "$TagData" | sed -n 's/\tARTIST=//ip')"
    if [[ -z "${Titles[$SoundID]}" ]]; then
      Titles[$SoundID]="${File##*/}"
    fi
    if [[ -z "${Artists[$SoundID]}" ]]; then
      Artists[SoundID]="unknown"
    fi
    ((SoundID++))
  done < <(find -L "$SoundDir" -type f -name '*.ogg' -print0 | sort -z --version-sort)
}

arg_compose_gui(){
  OLDIFS=$IFS
  IFS=$'\n'
  GuiScaleArray=(\
    $(for i in "${!Sounds[@]}"; do echo "--field=${Titles[$i]} (<i>${Artists[$i]}</i>): :SCL"; done)\
  )
  IFS=$OLDIFS
  export ScaleEndField=$((4 + ${#Sounds[@]})) # set last volume slider field in yad gui
  YadHeight=$(($YadHeight + ${#Sounds[@]} * 45)) # adjust GUI height
  if [[ "$YadHeight" -gt "800" ]]; then # set maximum height
    YadHeight="700"
  fi
}

arg_read_config(){
    [[ -f "$ConfigFile" ]] && CustomVolume="$(cat "$ConfigFile" | cut -d ' ' -f 1-"${#Sounds[@]}")" 
    # ↑ cut off surplus values
    [[ -z "$CustomVolume" ]] && CustomVolume=$(printf "%0.s0 " $(seq 1 "${#Sounds[@]}"))
}

gui_select_sounds(){
  YadResult="$(yad --form --scroll \
  --always-print-result \
  --image="$ThumbIcon" --window-icon="$WmIcon" \
  --class="$WmClass" \
  --title="$YadTitle" \
  --field="$GuiTxtDescr":LBL "" \
  --center --height=$YadHeight --width=$YadWidth \
  --field="":LBL "" \
  --field "_Reset all volume sliders!gtk-clear!$GuiTxtReset":FBTN \
      "@bash -c gui_resetbtn_onclick" \
  --field="":LBL "" \
  "${GuiScaleArray[@]}" \
  --button="_Cancel!gtk-cancel!$GuiTxtCancel:1" \
  --button="_Save and play!gtk-ok!$GuiTxtPlaySave:2" \
  --button="_Play!gtk-ok!$GuiTxtPlay:4" \
  $CustomVolume)"

  YadChoicesRet="$?"
  CustomVolume=($(echo "$YadResult" | cut -d '|' -f5- | tr '|' ' '))
  # Volumes begin in field 5 of yad output

  if [[ "$YadChoicesRet" = "2" ]]; then
    echo "${CustomVolume[@]}" > "$ConfigFile"
  elif [[ "$YadChoicesRet" != "4" ]]; then
    echo "GUI exit"
    exit 1
  fi
}

gui_resetbtn_onclick(){
  # reset yad sliders
  for ((i=5;i<=ScaleEndField;++i)); do
    echo "$i:0"
  done
}

gui_systray () {
    yad --notification \
    --text="MoodyRain" \
    --image="$TrayIcon" \
    --no-middle --command="" \
    --menu="Ambience selection!bash -c gui_systray_onclick!$ThumbIcon||Exit!kill -s TERM $TopPid!exit"&
    YadPid="$!"
}

gui_systray_onclick(){
  kill -s TERM "$TopPid"
  "$TopScript" > /dev/null 2>&1 & 
}

player(){
  for i in "${!CustomVolume[@]}"; do
    if [[ "${CustomVolume[$i]}" = 0 || -z "${CustomVolume[$i]}" ]]; then
      continue
    fi
    echo "Playing ${Titles[$i]} by ${Artists[$i]} at ${CustomVolume[$i]}% volume"
    if [[ "$AudioPlayer" = "mpv" ]]; then
      mpv --loop=inf --vo null --volume="${CustomVolume[$i]}" --gapless-audio=yes "${Sounds[$i]}" > /dev/null 2>&1 &
      PlayerPids+=("$!")
    else
      play -v $(echo "${CustomVolume[$i]}" | awk '{print $1/100}') "${Sounds[$i]}" repeat 199999999 > /dev/null 2>&1 &
      PlayerPids+=("$!")
    fi
  done
  [[ -n "${PlayerPids[0]}" ]] && wait "${PlayerPids[0]}"
}

cleanup(){
  for i in "${PlayerPids[@]}"; do
    kill -s TERM "$i"
  done
  [[ -n "$YadPid" ]] && kill -s TERM "$YadPid"
  kill -s TERM "$TopPid"
}

# Preamble

export -f gui_resetbtn_onclick
export -f gui_systray_onclick
export TopScript="$0"
export TopPid=$$
PlayerPids=()

trap "cleanup; exit" EXIT

# Main

arg_compile_sounds
arg_compose_gui
arg_read_config
gui_select_sounds
gui_systray
player