#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# mididings
#
# Copyright (C) 2013-2014  Dominic Sacré  <dominic.sacre@gmx.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#

import sys
import argparse
import _mididings


def parse_event(string, data_offset):
    ev = _mididings.MidiEvent()

    args = string.split(",")

    # type is first argument, case insensitive
    typestr = args.pop(0).upper()

    # valid event type values have exactly one bit set
    if not (
        hasattr(_mididings.MidiEventType, typestr)
        and bin(getattr(_mididings.MidiEventType, typestr)).count("1") == 1
        and typestr != "DUMMY"
    ):
        raise ValueError(f"invalid MIDI event type '{typestr}'")

    try:
        ev.type_ = getattr(_mididings.MidiEventType, typestr)
        ev.port_ = 0
        # set channel for non-system events only
        if not typestr.startswith("SYS"):
            ev.channel_ = int(args.pop(0)) - data_offset

        if typestr in ("NOTEON", "NOTEOFF", "CTRL", "POLY_AFTERTOUCH", "SYSCM_SONGPOS"):
            ev.data1 = int(args.pop(0))
            ev.data2 = int(args.pop(0))
        elif typestr in ("AFTERTOUCH", "PITCHBEND"):
            ev.data2 = int(args.pop(0))
        elif typestr == "PROGRAM":
            ev.data2 = int(args.pop(0)) - data_offset
        elif typestr in ("SYSCM_QFRAME", "SYSCM_SONGSEL"):
            ev.data1 = int(args.pop(0))
        elif typestr == "SYSEX":
            ev.sysex_ = bytearray(int(x, 16) for x in args)
            del args[:]
    except IndexError:
        raise ValueError(
            f"too few parameters specified for MIDI event type '{typestr}'"
        )

    if len(args) != 0:
        raise ValueError(
            f"too many parameters specified for MIDI event type '{typestr}'"
        )

    return ev


if __name__ == "__main__":
    usage = "send_midi [options] dest_port event [...]"
    description = "Send one or more MIDI events via ALSA or JACK."
    version = f"mididings {_mididings.__version__}"

    parser = argparse.ArgumentParser(
        description=description,
        usage=usage,
    )

    group = parser.add_mutually_exclusive_group()

    group.add_argument(
        "-A",
        "--alsa",
        dest="backend",
        action="store_const",
        const="alsa",
        default="alsa",
        help="use ALSA (default)",
    )

    group.add_argument(
        "-J",
        "--jack",
        dest="backend",
        action="store_const",
        const="jack-rt",
        help="use JACK",
    )

    parser.add_argument(
        "-0",
        "--zero-based",
        dest="data_offset",
        action="store_const",
        const=0,
        default=1,
        help="channel and program numbers are zero-based",
    )

    parser.add_argument(
        "dest_port",
        help="port to send events to",
    )

    parser.add_argument(
        "events",
        nargs=argparse.REMAINDER,
        help="events to send to destination port",
    )

    parser.add_argument("--version", action="version", version=version)

    args = parser.parse_args()

    try:
        # parse all events
        events = [parse_event(arg, args.data_offset) for arg in args.events]
        # send all events to the given destination port
        _mididings.send_midi(args.backend, args.dest_port, events)
    except Exception as ex:
        sys.exit(f"send_midi: error: {ex}")
