#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

import os
import signal
import sys

import dbus
import dbus.service
import dbus.mainloop.glib

import gi

gi.require_version("Gst", "1.0")
gi.require_version("GLib", "2.0")
from gi.repository import GLib, Gst

import bluezutils

mainloop = None
pipeline = None
seqnum: int = 0


def signal_handler(_sig, _frame):
    print("Got interrupt")
    mainloop.quit()


signal.signal(signal.SIGINT, signal_handler)


def usage():
    print(f"Usage: simple-asha <remote addr> <audio file name> (optional volume 0-127)")


def start_playback(fd: int, omtu: int):
    global mainloop, pipeline
    pktsize = 161

    if omtu < pktsize:
        print("Weird mtu", omtu)

    outdata = bytearray(pktsize)

    Gst.init(None)

    pipeline = Gst.parse_launch(
        f"""
          filesrc location="{sys.argv[2]}" ! decodebin !
          audioconvert ! audioresample !
          audiobuffersplit output-buffer-duration="20/1000" ! avenc_g722 !
          appsink name=sink emit-signals=true
    """
    )

    def on_new_sample(sink):
        global seqnum

        sample = sink.emit("pull-sample")
        buf = sample.get_buffer()

        with buf.map(Gst.MapFlags.READ) as info:
            pos = 0

            if info.size != pktsize - 1:
                print("Unexpected buffer size: ", info.size)

            outdata[pos] = seqnum % 256
            pos += 1

            for byte in info.data:
                outdata[pos] = byte
                pos += 1

            try:
                n = os.write(fd, outdata)
                if n != pktsize:
                    print("Wrote less than expected: ", n)
            except:
                return Gst.FlowReturn.ERROR

        seqnum += 1

        return Gst.FlowReturn.OK

    sink = pipeline.get_by_name("sink")
    sink.connect("new-sample", on_new_sample)

    def bus_message(_bus, message, _data) -> bool:
        typ = message.type

        if typ == Gst.MessageType.EOS:
            print("End of stream")
            mainloop.quit()
        elif typ == Gst.MessageType.ERROR:
            err, debug = message.parse_error()
            print(f"Pipeline error: {err} ({debug})")
            mainloop.quit()

        return True

    bus = pipeline.get_bus()
    bus.add_watch(GLib.PRIORITY_DEFAULT, bus_message, None)

    pipeline.set_state(Gst.State.PLAYING)


if __name__ == "__main__":
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    mainloop = GLib.MainLoop()
    bus = dbus.SystemBus()

    if (len(sys.argv) == 3) or (len(sys.argv) == 4):
        device = bluezutils.find_device(sys.argv[1])
        if device is None:
            print("Could not find device: ", sys.argv[1])
            exit(255)
    else:
        usage()
        sys.exit(255)

    asha_object_path = device.object_path + "/asha"

    print("Looking up ASHA object", asha_object_path)
    asha = bus.get_object("org.bluez", asha_object_path)

    print("Looking up endpoint properties for", asha.object_path)
    props = asha.GetAll(
        "org.bluez.MediaEndpoint1",
        dbus_interface="org.freedesktop.DBus.Properties",
    )
    path = props["Transport"]

    print("Trying to acquire", path)
    transport = dbus.Interface(
        bus.get_object("org.bluez", path),
        "org.bluez.MediaTransport1",
    )

    # Keep default volume at 25%
    volume = 32
    if len(sys.argv) == 4:
        volume = int(sys.argv[3])
        if volume < 0 or volume > 127:
            print("Volume must be between 0 (mute) and 127 (max)")

    print("Setting initial volume to", volume)
    transport.Set(
        "org.bluez.MediaTransport1",
        "Volume",
        dbus.UInt16(volume, variant_level=1),
        dbus_interface="org.freedesktop.DBus.Properties",
    )

    print("Acquiring transport")
    (fd, imtu, omtu) = transport.Acquire()

    print("Starting playback, hit Ctrl-C to stop")
    start_playback(fd.take(), omtu)

    mainloop.run()

    pipeline.set_state(Gst.State.NULL)
    transport.Release()
