/*
 * File: seqlib.m - Interface to the alsa sequencer library.
 * 
 * Copyright (C) 1999 Steve Ratcliffe
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */


#include "glib.h"
#include "elements.h"
#include "except.h"
#include "intl.h"

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "seqlib.h"
#include "seqpriv.h"

/*
 * Create a main client for an application. A queue is
 * allocated and a client is created.
 */
seq_context_t *
seq_create_context()
{
	seq_context_t *ctxp;
	int  q;

	ctxp = g_new(seq_context_t, 1);
	ctxp->main = ctxp; /* This is the main context */

	//if (snd_seq_open(&ctxp->handle, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0)
		except(ioError, "Could not open sequencer: %s", snd_strerror(errno));

	q = snd_seq_alloc_queue(ctxp->handle);
	ctxp->client = snd_seq_client_id(ctxp->handle);
	ctxp->queue = q;

	ctxp->destlist = g_array_new(0, 0, sizeof(snd_seq_addr_t));

	ctxp->queue = q;
	ctxp->source.client = ctxp->client;
	ctxp->source.port = 0;

	seq_new_port(ctxp);

	return ctxp;
}

/*
 * Create another client context based on the specified
 * context. The same queue will be used for both clients. A new
 * client will be created.
 *  Arguments:
 *    ctxp      - 
 */
seq_context_t *
seq_new_client(seq_context_t *ctxp)
{

	return NULL;/*XXX*/

}

/*
 * Free a context and any associated data and resources. If the
 * main context is freed then the sequencer is closed. TODO:
 * link all together so can all be closed at once.
 */
void 
seq_free_context(seq_context_t *cxtp)
{

	if (cxtp->main == cxtp) {
		/* This is the main context */
		snd_seq_free_queue(cxtp->handle, cxtp->queue);
		snd_seq_close(cxtp->handle);
	}

	g_free(cxtp);
}

/*
 * Creates a new port on the specified context and returns the
 * port number.
 *  Arguments:
 *    ctxp      - Context to create the port for
 */
int 
seq_new_port(seq_context_t *ctxp)
{
	int  ret=-1;

	//	ret = snd_seq_create_simple_port(ctxp->handle, NULL,
	//					 SND_SEQ_PORT_CAP_WRITE |
	//					 SND_SEQ_PORT_CAP_SUBS_WRITE |
	//					 SND_SEQ_PORT_CAP_READ,
	//					 SND_SEQ_PORT_TYPE_MIDI_GENERIC);
	if (ret < 0) {
		printf("Error creating port %s\n", snd_strerror(errno));
		return -1;
	}
	ctxp->port_count++;
	ctxp->source.port = ret;

	return ret;/*XXX*/
}

void 
seq_destroy_port(seq_context_t *cxtp, int port)
{
}

/*
 * Set up the context so that all subsequent events will be sent
 * to the specified client and port combination. A
 * subscription is made to the client/port combination.
 *  Arguments:
 *    ctxp      - Context to modify
 *    client    - Client to send subsequent events to
 *    port      - Port on the client to send events to
 */
int 
seq_connect_add(seq_context_t *ctxp, int client, int port)
{
	snd_seq_addr_t addr;
	int  ret;

	memset(&addr, 0, sizeof(addr));
	addr.client = client;
	addr.port = port;

	g_array_append_val(ctxp->destlist, addr);

	ret = snd_seq_connect_to(ctxp->handle, ctxp->source.port, client, port);

	return ret;
}

/*
 * Set the initial time base and tempo. This should only be used
 * for initialisation when there is nothing playing. To
 * change the tempo during a song tempo change events are used.
 * If realtime is false the resolution is in ticks per quarter
 * note. If true, the the resolution is microseconds. There is
 * a macro XXX to convert from SMPTE codes.
 * 
 *  Arguments:
 *    ctxp      - Application context
 *    resolution - Ticks per quarter note or realtime resolution
 *    tempo     - Beats per minute
 *    realtime  - True if absolute time base
 */
int 
seq_init_tempo(seq_context_t *ctxp, int resolution, int tempo, int realtime)
{
	snd_seq_queue_tempo_t *qtempo;
	int  ret;

	snd_seq_queue_tempo_alloca(&qtempo);
	memset(qtempo, 0, snd_seq_queue_tempo_sizeof());
	snd_seq_queue_tempo_set_ppq(qtempo, resolution);
	snd_seq_queue_tempo_set_tempo(qtempo, 60*1000000/tempo);

	ret = snd_seq_set_queue_tempo(ctxp->handle, ctxp->queue, qtempo);

	return ret;
}

/*
 * Set the context to use the specified queue. All events sent
 * on this context will now use this queue.
 *
 *  Arguments:
 *    ctxp      - Context to modify
 *    q         - The queue number
 */
void 
seq_set_queue(seq_context_t *ctxp, int q)
{
}

/*
 * Send the event to the specified client and port.
 * 
 *  Arguments:
 *    ctxp      - Client context
 *    ev        - Event to send
 *    client    - Client to send the event to
 *    port      - Port to send the event to
 */
int 
seq_sendto(seq_context_t *ctxp, snd_seq_event_t *ev, int client, int port)
{
	ev->source = ctxp->source;
	ev->queue = ctxp->queue;
	ev->dest.client = client;
	ev->dest.port = port;
	seq_write(ctxp, ev);

	return 0;
}

/*
 * Start the timer. (What about timers other than the system
 * one?)
 */
void 
seq_start_timer(seq_context_t *ctxp)
{
  //seq_control_timer(ctxp, SND_SEQ_EVENT_START);
}

/*
 * Stop the timer. (What about timers other than the system
 * one?)
 */
void 
seq_stop_timer(seq_context_t *ctxp)
{
  //seq_control_timer(ctxp, SND_SEQ_EVENT_STOP);
}

void 
seq_control_timer(seq_context_t *ctxp, int onoff)
{

  //if (onoff == SND_SEQ_EVENT_START)
		snd_seq_start_queue(ctxp->handle, ctxp->queue, 0);
		//else
		snd_seq_stop_queue(ctxp->handle, ctxp->queue, 0);

#ifdef USE_DRAIN
	snd_seq_drain_output(ctxp->handle);
#else
	snd_seq_flush_output(ctxp->handle);
#endif
}

/*
 * Write out the event. This routine blocks until
 * successfully written. 
 * 
 *  Arguments:
 *    ctxp      - Application context
 *    ep        - Event
 */
int 
seq_write(seq_context_t *ctxp, snd_seq_event_t *ep)
{
	int  err = 0;

	err = snd_seq_event_output(ctxp->handle, ep);
	if (err < 0)
		return err;

	return err;
}

/*
 * Return the handle to the underlying snd_seq stream.
 *  Arguments:
 *    ctxp      - Application context
 */
void *
seq_handle(seq_context_t *ctxp)
{
	return ctxp->handle;
}

