#!/usr/bin/env bash
#
# A helper to manage workspaces across multiple monitors.
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# TODO: Only move output if it is on the wrong monitor

############## Setup #########################

# function cleanup {
#     log "Killing child process $wlsunset_pid"
#     kill "$wlsunset_pid"
#     true
# }
# for sig in EXIT; do
#   trap "set +e; cleanup" "$sig"
# done

function die {
    local status_code="$1"
    shift
    (>&2 echo "${@}")
    exit "$status_code"
}

function log {
    (>&2 echo "${@}")
}

############## Program #########################

function main {
    local cmd="$1"
    shift
    if [ "$cmd" = "go_to_next_monitor" ]; then
        go_to_next_monitor "${@}"
    elif [ "$cmd" = "send_to_next_monitor" ]; then
        send_to_next_monitor "${@}"
    elif [ "$cmd" = "go_to_previous_monitor" ]; then
        go_to_previous_monitor "${@}"
    elif [ "$cmd" = "send_to_previous_monitor" ]; then
        send_to_previous_monitor "${@}"
    elif [ "$cmd" = "go_to_workspace" ]; then
        go_to_workspace "${@}"
    elif [ "$cmd" = "send_to_workspace" ]; then
        send_to_workspace "${@}"
    else
        die 1 "Unrecognized command $cmd"
    fi
}

function go_to_next_monitor {
    local monitor_index workspace_index workspace
    workspace=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused == true) | .name')
    workspace_index=$(parse_workspace_index "${workspace}")
    # monitor_index=$(parse_monitor_index "${workspace}")
    monitor_index=$(swaymsg -t get_outputs | jq 'map(.focused) | to_entries | .[] | select(.value) | .key')
    local target output target_monitor
    target_monitor=$(normalize_monitor_index "$((monitor_index+1))")
    target=$(encode_workspace "${target_monitor}" "${workspace_index}")
    output=$(get_output "${target_monitor}")
    swaymsg workspace "${target}" \; move workspace to output "$output"
}

function send_to_next_monitor {
    local monitor_index workspace_index workspace
    workspace=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused == true) | .name')
    workspace_index=$(parse_workspace_index "${workspace}")
    # monitor_index=$(parse_monitor_index "${workspace}")
    monitor_index=$(swaymsg -t get_outputs | jq 'map(.focused) | to_entries | .[] | select(.value) | .key')
    local target output target_monitor
    target_monitor=$(normalize_monitor_index "$((monitor_index+1))")
    target=$(encode_workspace "${target_monitor}" "${workspace_index}")
    output=$(get_output "${target_monitor}")
    swaymsg move container to workspace "${target}" \; workspace "$target" move to output "$output"
}

function go_to_previous_monitor {
    local monitor_index workspace_index workspace
    workspace=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused == true) | .name')
    workspace_index=$(parse_workspace_index "${workspace}")
    # monitor_index=$(parse_monitor_index "${workspace}")
    monitor_index=$(swaymsg -t get_outputs | jq 'map(.focused) | to_entries | .[] | select(.value) | .key')
    local target output target_monitor
    target_monitor=$(normalize_monitor_index "$((monitor_index-1))")
    target=$(encode_workspace "${target_monitor}" "${workspace_index}")
    output=$(get_output "${target_monitor}")
    swaymsg workspace "${target}" \; move workspace to output "$output"
}

function send_to_previous_monitor {
    local monitor_index workspace_index workspace
    workspace=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused == true) | .name')
    workspace_index=$(parse_workspace_index "${workspace}")
    # monitor_index=$(parse_monitor_index "${workspace}")
    monitor_index=$(swaymsg -t get_outputs | jq 'map(.focused) | to_entries | .[] | select(.value) | .key')
    local target output target_monitor
    target_monitor=$(normalize_monitor_index "$((monitor_index-1))")
    target=$(encode_workspace "${target_monitor}" "${workspace_index}")
    output=$(get_output "${target_monitor}")
    swaymsg move container to workspace "${target}" \; workspace "$target" move to output "$output"
}

function go_to_workspace {
    local monitor_index workspace_index workspace output
    workspace_index="$1"
    monitor_index=$(swaymsg -t get_outputs | jq 'map(.focused) | to_entries | .[] | select(.value) | .key')
    workspace=$(encode_workspace "$monitor_index" "$workspace_index")
    output=$(get_output "${monitor_index}")
    swaymsg workspace "${workspace}" \; move workspace to output "$output"
}

function send_to_workspace {
    local monitor_index workspace_index workspace output
    workspace_index="$1"
    monitor_index=$(swaymsg -t get_outputs | jq 'map(.focused) | to_entries | .[] | select(.value) | .key')
    workspace=$(encode_workspace "$monitor_index" "$workspace_index")
    output=$(get_output "${monitor_index}")
    swaymsg move container to workspace "${workspace}" \; workspace "${workspace}" move to output "$output"
}

function get_output {
    local monitor_index output
    monitor_index="$1"
    output=$(swaymsg -t get_outputs | jq -r ".[${monitor_index}].name")
    echo "$output"
}

function encode_workspace {
    local monitor_index workspace_index
    monitor_index="$1"
    workspace_index="$2"
    if [ "$monitor_index" = "0" ]; then
        echo "$workspace_index"
    else
        echo "${monitor_index}-${workspace_index}"
    fi
}

function parse_monitor_index {
    local workspace="$1"
    if [[ $workspace = *-* ]]; then
        cut -d '-' -f 1 <<<"$workspace"
    else
        echo "0"
    fi
}

function parse_workspace_index {
    local workspace="$1"
    if [[ $workspace = *-* ]]; then
        cut -d '-' -f 2 <<<"$workspace"
    else
        echo "$workspace"
    fi
}

function normalize_monitor_index {
    local monitor_index num_monitors
    monitor_index="$1"
    num_monitors=$(swaymsg -t get_outputs | jq length)
    while [ "$monitor_index" -lt 0 ]; do
        monitor_index=$((monitor_index + num_monitors))
    done
    while [ "$monitor_index" -ge "$num_monitors" ]; do
        monitor_index=$((monitor_index - num_monitors))
    done
    echo "$monitor_index"
}

main "${@}"
