#!/usr/bin/python

'''Move all PipeWire sink inputs to the distination sink.'''

import re
import subprocess


def get_nodes():
    '''
    Returns a tuple with collections of PipeWire nodes:
        result[0] - list of tuples kind of (id, node.name, media.description) which describes sinks.
        result[1] - list of output streams ids.
    '''
    call_result = subprocess.run(
        ['pw-cli', 'list-objects', 'Node'], capture_output=True, text=True, check=True)

    sinks = []
    output_streams = []

    node_id = None
    node_name = None
    node_description = None
    for line in [l.strip() for l in call_result.stdout.splitlines()]:
        if line.startswith('id '):
            node_id = line.partition(',')[0].partition(' ')[2]
        else:
            key, _, value = line.partition(' = ')
            value = value.strip('"')
            if key == 'node.name':
                node_name = value
            elif key == 'node.description':
                node_description = value
            elif key == 'media.class':
                if value == 'Audio/Sink':
                    sinks.append((node_id, node_name, node_description))
                elif value == 'Stream/Output/Audio':
                    output_streams.append(node_id)

    return (sinks, output_streams)


def get_default_sink_name():
    '''Returns the default PipeWire sink name'''
    call_result = subprocess.run(
        ['pw-metadata', '0'], capture_output=True, text=True, check=True)
    for line in [l.strip() for l in call_result.stdout.splitlines()]:
        if "key:'default.audio.sink'" in line:
            m = re.search('"name": *"(.*)"', line)
            return m[1]
    return None


if __name__ == "__main__":
    import argparse
    import sys

    parser = argparse.ArgumentParser(
        description='Move all PipeWire sink inputs to the distination sink.')
    parser.add_argument('node_description', metavar='node-description', nargs='?',
                        help='''a "node.description" attribute value of the destination sink
                             (if empty prints a list of sinks)''')

    args = parser.parse_args()
    destination_sink_description = args.node_description

    sinks, output_streams = get_nodes()

    if destination_sink_description:
        # Switch to the new sink
        try:
            destination_sink = next(
                s for s in sinks if s[2] == destination_sink_description)
        except StopIteration:
            print(
                f'Couldn\'t find a sync node with description "{destination_sink_description}"',
                file=sys.stderr)
            sys.exit(1)

        subprocess.run(['pw-metadata', '0', 'default.configured.audio.sink',
                       f'{{ "name": "{destination_sink[1]}" }}'], check=True)

        for output_stream in output_streams:
            subprocess.run(['pw-metadata', output_stream,
                           'target.node', destination_sink[0]], check=False)
    else:
        # Show a list of sinks
        default_sink_name = get_default_sink_name()
        for node_id, node_name, node_description in sinks:
            mark = '*' if node_name == default_sink_name else ' '
            print(f'{mark}{node_description}')
