#! /usr/bin/python3

import argparse
import sys
import json
import threading
import os
import urllib.parse

import paho.mqtt.client as mqtt

version = "3.1.1"

broker = urllib.parse.urlparse(os.getenv("MQTT_SERVER"))
MQTT_HOST = broker.hostname or "localhost"
MQTT_PORT = broker.port or 1883

def format_time(time_pos):
    if time_pos is None:
        return "end of file"
    if type(time_pos) is str and ":" in time_pos:
        return time_pos

    seconds = int(time_pos % 60)
    if seconds < 10:
        seconds = "0" + str(seconds)
    else:
        seconds = str(seconds)

    minutes = int((time_pos / 60) % 60)
    if minutes < 10:
        minutes = "0" + str(minutes)
    else:
        minutes = str(minutes)

    hours = int(time_pos / 60 / 60)
    if hours < 10:
        hours = "0" + str(hours)
    else:
        hours = str(hours)

    return hours + ":" + minutes + ":" + seconds

def display_error(data):
    if "details" in data:
        print("Error: {} ({})".format(data["error"], ", ".join(data["details"])))
    else:
        print("Error: {}".format(data["error"]))

def display_nothing(data):
    if "error" in data:
        display_error(data)
    else:
        print(data)

def display_list(data):
    if "error" in data:
        display_error(data)
    else:
        path = data["path"]
        files = data["files"]
        directories = data["directories"]
        for d in directories:
            print(d)
        for f in files:
            try:
                print(f["path"], end="")
                if "duration" in f:
                    print(" ", end="")
                    print(format_time(f["duration"]), end="")
                if "title" in f:
                    print(" ", end="")
                    print(f["title"], end="")
                print()
            except UnicodeEncodeError:
                print("UNICODE ERROR", f.encode("UTF-8", "ignore"))

def display_status(data):
    if "error" in data and data["error"] is not None:
        display_error(data)
    if "current" not in data:
        return
    if data["current"] is None:
        print("Idle")
    else:
        if data["percent_pos"] is None:
            data["percent_pos"] = 0
        print("Playing", data["current"])
        print("Position",
              format_time(data["time_pos"]), "/", format_time(data["duration"]),
              "%0.2f%%" % data["percent_pos"],
              end="")
        if data["pause"]:
            print(" (paused)")
        else:
            print()
        print("Volume:", "%0.0f%%" % int(data["volume"]))

def do_status(args):
    return ("status", {}, display_status)

def do_play(args):
    return ("play", {"path": args.path}, display_status)

def do_pause(args):
    return ("pause", {}, display_status)

def do_stop(args):
    return ("stop", {}, display_status)

def do_position(args):
    if args.pos is None:
        return ("position", {}, display_nothing)
    else:
        return ("position", {"pos": args.pos}, display_status)

def do_seek(args):
    return ("seek", {"offset": args.offset}, display_status)

def do_volume(args):
    if args.volume[0] == "+" or args.volume[0] == "-":
        return ("volume", {"add": int(args.volume)}, display_nothing)
    else:
        return ("volume", {"val": int(args.volume)}, display_nothing)

def do_mute(args):
    return ("pause", {}, display_nothing)

def do_last(args):
    return ("last", {"pattern": args.pattern}, display_nothing)

def do_continue(args):
    return ("continue", {"pattern": args.pattern}, display_status)

def do_next(args):
    return ("next", {}, display_status)

def do_previous(args):
    return ("previous", {}, display_status)

def do_screen(args):
    if args.on:
        return ("screen", {"state": True}, display_nothing)
    if args.off:
        return ("screen", {"state": False}, display_nothing)

def do_eject(args):
    return ("eject", {}, display_nothing)

def do_list(args):
    return ("list", {"path": args.path}, display_list)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Zeromedia command line client")
    parser.add_argument('--version', action='version', version='%(prog)s ' + version)
    parser.add_argument("-b", "--host",
                        dest="host",
                        default=MQTT_HOST,
                        help="MQTT broker address")
    parser.add_argument("-p", "--port",
                        dest="port",
                        default=MQTT_PORT,
                        help="MQTT broker port")
    parser.add_argument("-t", "--topic",
                        dest="topic",
                        default="zeromedia",
                        help="MQTT base topic")
    subparsers = parser.add_subparsers()

    parser_status = subparsers.add_parser('status', help='Display player status')
    parser_status.set_defaults(command=do_status)

    parser_play = subparsers.add_parser('play', help='Play the given path')
    parser_play.add_argument('path', type=str, help='Path in the form alias:something')
    parser_play.set_defaults(command=do_play)

    parser_pause = subparsers.add_parser('pause', help='Pause the player')
    parser_pause.set_defaults(command=do_pause)

    parser_stop = subparsers.add_parser('stop', help='Stop the player')
    parser_stop.set_defaults(command=do_stop)

    parser_position = subparsers.add_parser('position', help='Display or set the position')
    parser_position.add_argument('pos', type=float, nargs="?", default=None, help='If provided, set the position to this value (in seconds)')
    parser_position.set_defaults(command=do_position)

    parser_seek = subparsers.add_parser('seek', help='Seek forward or backward')
    parser_seek.add_argument('offset', type=float, help='Offset in seconds')
    parser_seek.set_defaults(command=do_seek)

    parser_volume = subparsers.add_parser('volume', help='Display or set the position')
    parser_volume.add_argument('volume', type=str, help='If provided, set the volume to this value')
    parser_volume.set_defaults(command=do_volume)

    parser_mute = subparsers.add_parser('mute', help='Mute the player')
    parser_mute.set_defaults(command=do_mute)

    parser_last = subparsers.add_parser('last', help='Display the last played path')
    parser_last.add_argument('pattern', nargs="?", default="", type=str, help='If provided, search for this pattern in the history')
    parser_last.set_defaults(command=do_last)

    parser_continue = subparsers.add_parser('continue', help='Resume a stopped path or continue to the next path')
    parser_continue.add_argument('pattern', nargs="?", default="*", type=str, help='If provided, search for this pattern in the history')
    parser_continue.set_defaults(command=do_continue)

    parser_next = subparsers.add_parser('next', help='Switch to the next path')
    parser_next.set_defaults(command=do_next)

    parser_previous = subparsers.add_parser('prev', help='Switch to the previous path')
    parser_previous.set_defaults(command=do_previous)

    parser_screen = subparsers.add_parser('screen', help='Switch the screen on or off')
    parser_screen.add_argument('--on', action="store_true", default=False, help='On')
    parser_screen.add_argument('--off', action="store_true", default=False, help='Off')
    parser_screen.set_defaults(command=do_screen)

    parser_eject = subparsers.add_parser('eject', help='Eject the disc player')
    parser_eject.set_defaults(command=do_eject)

    parser_list = subparsers.add_parser('list', help='List aliases or path in a directory')
    parser_list.add_argument('path', nargs="?", default="", type=str, help='If empty, list aliases')
    parser_list.set_defaults(command=do_list)

    args = parser.parse_args()
    if "command" not in args:
        parser.print_help()
        sys.exit(1)

    (topic, payload, display) = args.command(args)

    client = mqtt.Client()

    def on_connect(client, userdata, flags, rc):
        if int(rc) != 0:
            print("Connection error:", rc)
        client.subscribe(args.topic + "/data/" + topic)
        client.publish(args.topic + "/command/" + topic, json.dumps(payload))
    client.on_connect = on_connect

    def on_message(client, userdata, msg):
        data = json.loads(msg.payload.decode("UTF-8"))
        display(data)
        client.loop_stop()
    client.on_message = on_message

    client.connect(args.host, args.port, 60)
    client.loop_start()

    main_thread = threading.main_thread()
    for thread in threading.enumerate():
        if thread != main_thread:
            break
    thread.join()

