/*
 * Copyright 1993 Network Computing Devices, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name Network Computing Devices, Inc. not be
 * used in advertising or publicity pertaining to distribution of this 
 * software without specific, written prior permission.
 * 
 * THIS SOFTWARE IS PROVIDED `AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
 * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
 * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
 * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
 * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
 * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * $NCDId: @(#)auevents.c,v 1.9 1994/04/21 21:50:59 greg Exp $
 */

#include        "misc.h"
#include        "dixstruct.h"

#ifdef sgi
# define                _BSD_SIGNALS
#endif

#ifdef __NetBSD__
# include       <sys/types.h>
#endif

#include        <sys/signal.h>
#include        <audio/audio.h>
#include        <audio/Aproto.h>
#include        "au.h"

extern void WriteEventsToClient(), AuProcessClockedFlows();

typedef struct _EventQueueRec {
    auEvent event;
    AuBool processClockedFlows;
    FlowPtr flow;
    ClientPtr client;
    struct _EventQueueRec *prev, *next;
} EventQueueRec, *EventQueuePtr;

EventQueuePtr AuEventQueue;
static EventQueuePtr AuEventQueueTail;

#define AddToEventQueue(e)                                                     \
{                                                                              \
    if (AuEventQueueTail)                                                      \
    {                                                                          \
        AuEventQueueTail->next = (e);                                          \
        (e)->prev = AuEventQueueTail;                                          \
    }                                                                          \
    else                                                                       \
    {                                                                          \
        (e)->prev = NULL;                                                      \
        AuEventQueue = (e);                                                    \
    }                                                                          \
                                                                               \
    (e)->next = NULL;                                                          \
    AuEventQueueTail = (e);                                                    \
}

static void
WriteAuEventsToClient(ClientPtr pClient, int count, auEvent * events)
{
    /* pebl: check whether client is still there */
    if (pClient->clientGone == FALSE)
        WriteEventsToClient(pClient, count, events);
}

static void
fillInEvent(int kind, int reason, FlowElementPtr el, auEvent * ev)
{
    AuUint32 numBytes = 0;
    ComponentPtr c = el->component;

    switch (el->raw->type) {
    case AuElementTypeImportClient:
        numBytes = c->dataSize - c->currentSize - c->incoming;

        if (el->state != AuStateStop)
            c->incoming += numBytes;
        break;
    case AuElementTypeImportDevice:
        break;
    case AuElementTypeImportBucket:
        break;
    case AuElementTypeImportWaveForm:
        break;
    case AuElementTypeExportClient:
        numBytes = c->currentSize - c->outgoing;
        c->outgoing += numBytes;
        break;
    case AuElementTypeExportDevice:
        break;
    case AuElementTypeExportBucket:
        break;
    }

    UpdateCurrentTimeIf();

    ev->u.u.type = AuEventTypeElementNotify;
    ev->u.u.time = currentTime.milliseconds;

    ev->u.elementNotify.flow = el->flow->flowId;
    ev->u.elementNotify.element_num = el->elementNum;
    ev->u.elementNotify.kind = kind;
    ev->u.elementNotify.reason = reason;
    ev->u.elementNotify.prev_state = el->prevState;
    ev->u.elementNotify.cur_state = el->state;
    ev->u.elementNotify.num_bytes = numBytes;
}

void
ProcessAudioEvents(void)
{
    AuBlock l;
    EventQueuePtr p, next;

    l = AuBlockAudio();
    p = AuEventQueue;
    AuUnBlockAudio(l);

    while (p) {
        if (p->processClockedFlows)
            AuProcessClockedFlows();
        else {
            p->event.u.u.sequenceNumber = p->client->sequence;
            WriteAuEventsToClient(p->client, 1, &p->event);
        }

        l = AuBlockAudio();
        RemoveFromLinkedList(AuEventQueue, p);

        if (!AuEventQueue)
            AuEventQueueTail = NULL;

        next = p->next;
        AuUnBlockAudio(l);

        AuProtectedFree(p);
        p = next;
    }
}

void
AuRequestElementNotifyEvent(int kind, int reason, FlowElementPtr el)
{
    EventQueuePtr p;

    if (!(p = (EventQueuePtr) AuProtectedMalloc(sizeof(EventQueueRec))))
        return;

    if (kind == AuElementNotifyKindSpecial)
        p->processClockedFlows = AuTrue;
    else {
        p->processClockedFlows = AuFalse;
        p->client = (ClientPtr) (el->client);
        p->flow = el->flow;

        fillInEvent(kind, reason, el, &p->event);
    }

    AddToEventQueue(p);
    AuCallbackIf(AuEventPostedCB, ());
    WAKEUP_SERVER();
}

#define MONITOR_EVENT_DATA_SPACE        12

void
AuRequestMonitorNotifyEvent(FlowElementPtr el, AuUint8 * data)
{
    EventQueuePtr p;
    auEvent *ev;
    AuUint32 count, numBytes;
    ComponentPtr c = el->component;

    numBytes = c->bytesPerSample * 2;
    count = (numBytes + (MONITOR_EVENT_DATA_SPACE - 1)) /
            MONITOR_EVENT_DATA_SPACE;

    while (count--) {
        if (!
            (p = (EventQueuePtr) AuProtectedMalloc(sizeof(EventQueueRec))))
            return;

        p->processClockedFlows = AuFalse;
        p->client = (ClientPtr) (el->client);
        p->flow = el->flow;

        ev = &p->event;

        UpdateCurrentTimeIf();

        ev->u.u.type = AuEventTypeMonitorNotify;
        ev->u.u.time = currentTime.milliseconds;

        ev->u.monitorNotify.flow = el->flow->flowId;
        ev->u.monitorNotify.element_num = el->elementNum;
        ev->u.monitorNotify.format = c->format;
        ev->u.monitorNotify.num_tracks = c->numTracks;
        ev->u.monitorNotify.count = count;
        ev->u.monitorNotify.num_fields = 2;

        bcopy(data, (char *) &ev->u.monitorNotify.data,
              MONITOR_EVENT_DATA_SPACE);
        data += MONITOR_EVENT_DATA_SPACE;

        AddToEventQueue(p);
    };

    AuCallbackIf(AuEventPostedCB, ());
    WAKEUP_SERVER();
}

void
AuDequeueEvents(FlowPtr flow)
{
    AuBlock l;
    EventQueuePtr p, next;

    l = AuBlockAudio();
    p = AuEventQueue;
    AuUnBlockAudio(l);

    while (p) {
        l = AuBlockAudio();
        next = p->next;
        AuUnBlockAudio(l);

        if (p->flow == flow) {
            l = AuBlockAudio();

            RemoveFromLinkedList(AuEventQueue, p);

            if (!AuEventQueue)
                AuEventQueueTail = NULL;

            AuUnBlockAudio(l);
            AuProtectedFree(p);
        }

        p = next;
    }
}
