#!/usr/bin/env python

import argparse, os, sys, time

from s3turbo.MidiHandler import MidiHandler
from s3turbo.SysExParser import SysExParser
from s3turbo.MSCEIMessage import MSCEIMessage
from s3turbo.SampleDumpHandler import SampleDumpHandler
from s3turbo.StandardHandler import StandardHandler
from s3turbo.S3Turbo import S3Exception
from s3turbo.Util import is_sysex, str2file, str2hex

def main():
    # command-line parsing
    parser = argparse.ArgumentParser(
        description='Send and receive midi messages')
    parser.add_argument('-i', '--indev', type=int, default=-1, action='store',
                        help='MIDI input device')
    parser.add_argument('-o', '--outdev', type=int, default=-1, action='store',
                        help='MIDI output device')
    parser.add_argument('-l', '--listdev', action='store_true',
                        help='list MIDI devices and exit')
    parser.add_argument('-c', '--command', type=str, action='store',
                        help='send MIDI command')
    parser.add_argument('-v', '--verbose', action='store_true',
                        help='enable verbose output')
    parser.add_argument('-e', '--exit', action='store_true',
                        help='exit after executing command')
    parser.add_argument('--samples', nargs="+",
                        help='list of SDS files for sample upload')
    parser.add_argument('-f', '--file', type=str2file, action='append',
                        dest='payload', metavar="FILE",
                        help='add filename to payload' +
                        '(will be whitespace-filled to 11 characters length)')
    parser.add_argument('-p', '--path', type=str2hex, action='append',
                        dest='payload', metavar='PATH',
                        help='add path to payload' +
                        '(will be 0-terminated, max 80 chars)')
    parser.add_argument('-d', '--int', type=int, action='append',
                        dest='payload', nargs='+', metavar='NUMBER',
                        help='add integer to payload')
    parser.add_argument('-x', '--hex', type=lambda x: int(x, 16), nargs='+',
                        action='append', dest='payload', metavar='HEXNUMBER',
                        help='add hexadecimal integer to payload')

    # set defaults from configuration file
    confargs = {}
    conffile = os.path.join( os.environ["HOME"], ".s3turbo.conf")
    if os.path.exists(conffile):
        for line in open(conffile):
            k,v = line.strip().split(':')
            confargs[k] = v
    parser.set_defaults(**confargs)
    args = parser.parse_args()
    payload = []
    if args.payload:
        for i in args.payload:
            if type(i) == type(list()):
                payload += i
            else:
                payload.append(i)
    
    try:
        # list MIDI devices and exit
        if args.listdev:
            print "Inputs:"
            MidiHandler.print_dev(MidiHandler.INPUT)
            print "Outputs:"
            MidiHandler.print_dev(MidiHandler.OUTPUT)
            sys.exit(0)
            
        # MidiHandler, filter clock messages
        filter = [
           [ 0xf8 ], # midi clock
        ]
        midi = MidiHandler(indev=args.indev, outdev=args.outdev,
                           msgfilter=filter, debug=args.verbose)
        midi.start()

        # SysExParser
        parser = SysExParser(send_conn=midi.send_conn,debug=args.verbose)
        if args.command:
            parser.sendSysEx(MSCEIMessage(*payload, fromName=args.command))
        elif len(payload):
            midi.send_conn.send( (0,payload))

        standard_handler = StandardHandler(debug=args.verbose)

        # poll for incoming messages
        currentHandler = None
        while True:
            if midi.recv_conn.poll(1):
                timestamp, msg = midi.recv_conn.recv()
                if not len(msg):
                    continue
                if not is_sysex(msg):
                    if not standard_handler.handle(msg,timestamp): break
                    else: continue
                if msg[1] == 0x7E:
                    # SampleDumpHeader, SampleDumpDataPacket, SampleDumpRequest
                    if msg[3] in [ 0x1, 0x2, 0x3 ]:
                        if not currentHandler:
                            currentHandler = SampleDumpHandler(
                                debug=args.verbose,samplelist=args.samples)
                        midi.send_conn.send((0,currentHandler.parse(msg)))
                    # WAIT
                    elif msg[3] == 0x7C:
                        pass
                    # CANCEL
                    elif msg[3] == 0x7D:
                        if currentHandler:
                            del currentHandler
                            currentHandler = None
                    # ACK
                    elif msg[3] == 0x7F:
                        if currentHandler:
                            midi.send_conn.send((0,currentHandler.parse(msg)))
                else:
                    if not parser.parse(msg, timestamp): break
            elif parser.dump_ram:
                parser.stopDump()
                break
            elif args.exit:
                break
    except EOFError, e:
        print "EOFError", e
        sys.exit(1)
    except KeyboardInterrupt:
        if parser.dump_on:
            parser.sendSysEx(MSCEIMessage(fromName="F_CANCEL"))
            time.sleep(.1)
        print
    except S3Exception, e:
        print e
        sys.exit(1)

    # give MIDI handler some time to finish sending data (produces MIDI
    # timeouts if removed, e.g. on STAT_REQUEST)
    # should be solved properly at some point...
    time.sleep(.1)
    
if __name__ == '__main__':
    main()
