/* ************************************************************ *
 *						     		*
 *  Decoding and switching routines for the NAS protocol	*
 *						      		*
 *	James Peterson, 1988			      		*
 *	(c) Copyright MCC, 1988 		      		*
 *						      		*
 * ************************************************************ */

#include "scope.h"
#include "nas.h"

/*
  There are 4 types of things in NAS: requests, replies, errors, and events.

  Each of them has a format defined by a small integer that defines
  the type of the thing.

  Requests have an opcode in the first byte.
  Events have a code in the first byte.
  Errors have a code in the second byte (the first byte is 0)
  Replies ...

  Replies have a sequence number in bytes 2 and 3.  The sequence
  number should be used to identify the request that was sent, and
  from that request we can determine the type of the reply.
*/

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/*
  We need to keep the sequence number for a request to match it with
  an expected reply.  The sequence number is associated only with the
  particular connection that we have. We would expect these replies
  to be handled as a FIFO queue.
*/

#define DUMP_REQUEST	0
#define DUMP_REPLY	1
#define DUMP_EVENT	2
#define DUMP_ERROR	3

static const char *const simple_names[] = {
    "REQUEST",
    "REPLY  ",
    "EVENT  ",
    "ERROR  ",
};

static void
AudioSimpleDump(int type, FD fd, short Major, short Minor, long bytes)
{
    PrintTime();
    fprintf(stdout, "@@%s %3d %3d %3d %7ld\n",
            simple_names[type], ClientNumber(fd), Major, Minor, bytes);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

void
DecodeAudioRequest(FD fd, const unsigned char *buf, long n)
{
    short Request = IByte(&buf[0]);
    short RequestMinor = Request >= 128 ? IByte(&buf[1]) : 0;
    unsigned long seq;

    CS[fd].SequenceNumber += 1;
    seq = CS[fd].SequenceNumber;
    if (CS[fd].littleEndian) {
        SBf[0] = seq;
        SBf[1] = seq >> 8;
        SBf[2] = seq >> 16;
        SBf[3] = seq >> 24;
    }
    else {
        SBf[0] = seq >> 24;
        SBf[1] = seq >> 16;
        SBf[2] = seq >> 8;
        SBf[3] = seq;
    }
    SetIndentLevel(PRINTCLIENT);

    if (NasVerbose == 0) {
        AudioSimpleDump(DUMP_REQUEST, fd, Request, RequestMinor, n);
        return;
    }

    if (NasVerbose > 3)
        DumpItem("Request", fd, buf, n);
    switch (Request) {
    case 1:
        AudioListDevices(buf);
        ReplyExpected(fd, Request);
        break;
    case 2:
        AudioGetDeviceAttributes(buf);
        ReplyExpected(fd, Request);
        break;
    case 3:
        AudioSetDeviceAttributes(buf);
        break;
    case 4:
        AudioCreateBucket(buf);
        break;
    case 5:
        AudioDestroyBucket(buf);
        break;
    case 6:
        AudioListBuckets(buf);
        ReplyExpected(fd, Request);
        break;
    case 7:
        AudioGetBucketAttributes(buf);
        ReplyExpected(fd, Request);
        break;
    case 8:
        AudioSetBucketAttributes(buf);
        break;
    case 9:
        AudioCreateRadio(buf);
        break;
    case 10:
        AudioDestroyRadio(buf);
        break;
    case 11:
        AudioListRadios(buf);
        ReplyExpected(fd, Request);
        break;
    case 12:
        AudioGetRadioAttributes(buf);
        ReplyExpected(fd, Request);
        break;
    case 13:
        AudioSetRadioAttributes(buf);
        break;
    case 14:
        AudioCreateFlow(buf);
        break;
    case 15:
        AudioDestroyFlow(buf);
        break;
    case 16:
        AudioGetFlowAttributes(buf);
        ReplyExpected(fd, Request);
        break;
    case 17:
        AudioSetFlowAttributes(buf);
        break;
    case 18:
        AudioGetElements(buf);
        ReplyExpected(fd, Request);
        break;
    case 19:
        AudioSetElements(buf);
        break;
    case 20:
        AudioGetElementStates(buf);
        ReplyExpected(fd, Request);
        break;
    case 21:
        AudioSetElementStates(buf);
        break;
    case 22:
        AudioGetElementParameters(buf);
        ReplyExpected(fd, Request);
        break;
    case 23:
        AudioSetElementParameters(buf);
        break;
    case 24:
        AudioWriteElement(buf);
        break;
    case 25:
        AudioReadElement(buf);
        ReplyExpected(fd, Request);
        break;
    case 26:
        AudioGrabComponent(buf);
        break;
    case 27:
        AudioUngrabComponent(buf);
        break;
    case 28:
        AudioSendEvent(buf);
        break;
    case 29:
        AudioGetAllowedUsers(buf);
        ReplyExpected(fd, Request);
        break;
    case 30:
        AudioSetAllowedUsers(buf);
        break;
    case 31:
        AudioListExtensions(buf);
        ReplyExpected(fd, Request);
        break;
    case 32:
        AudioQueryExtension(buf);
        ReplyExpected(fd, Request);
        break;
    case 33:
        AudioGetCloseDownMode(buf);
        ReplyExpected(fd, Request);
        break;
    case 34:
        AudioSetCloseDownMode(buf);
        break;
    case 35:
        AudioKillClient(buf);
        break;
    case 36:
        AudioGetServerTime(buf);
        ReplyExpected(fd, Request);
        break;
    case 37:
        AudioNoOperation(buf);
        break;
    default:
        warn("Unimplemented request opcode");
        break;
    }
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

void
DecodeAudioReply(FD fd, const unsigned char *buf, long n)
{
    short SequenceNumber = IShort(&buf[2]);
    short RequestMinor;
    short Request = CheckReplyTable(fd, SequenceNumber, &RequestMinor);

    if (NasVerbose == 0) {
        AudioSimpleDump(DUMP_REPLY, fd, Request, RequestMinor, n);
        return;
    }

    SetIndentLevel(PRINTSERVER);
    RBf[0] = Request; /* for the PrintField in the Reply procedure */
    RBf[1] = RequestMinor;
    if (NasVerbose > 3)
        DumpItem("Reply", fd, buf, n);
    switch (Request) {
    case 0:
        UnknownAudioReply(buf);
        break;
    case 1:
        AudioListDevicesReply(buf);
        break;
    case 2:
        AudioGetDeviceAttributesReply(buf);
        break;
    case 6:
        AudioListBucketsReply(buf);
        break;
    case 7:
        AudioGetBucketAttributesReply(buf);
        break;
    case 11:
        AudioListRadiosReply(buf);
        break;
    case 12:
        AudioGetRadioAttributesReply(buf);
        break;
    case 16:
        AudioGetFlowAttributesReply(buf);
        break;
    case 18:
        AudioGetElementsReply(buf);
        break;
    case 20:
        AudioGetElementStatesReply(buf);
        break;
    case 22:
        AudioGetElementParametersReply(buf);
        break;
    case 25:
        AudioReadElementReply(buf);
        break;
    case 29:
        AudioGetAllowedUsersReply(buf);
        break;
    case 31:
        AudioListExtensionsReply(buf);
        break;
    case 32:
        AudioQueryExtensionReply(buf);
        break;
    case 33:
        AudioGetCloseDownModeReply(buf);
        break;
    case 36:
        AudioGetServerTimeReply(buf);
        break;
    default:
        warn("Unimplemented reply opcode");
        break;
    }
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

void
DecodeAudioError(FD fd, const unsigned char *buf, long n)
{
    // short Error = IByte(&buf[1]);
    short Request = 0;
    short RequestMinor = 0;

    Request = CheckReplyTable(fd, (short) IShort(&buf[2]), &RequestMinor);

    if (NasVerbose == 0) {
        AudioSimpleDump(DUMP_ERROR, fd, Request, RequestMinor, n);
        return;
    }

    SetIndentLevel(PRINTSERVER);
    if (NasVerbose > 3)
        DumpItem("Error", fd, buf, n);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

void
DecodeAudioEvent(FD fd, const unsigned char *buf, long n)
{
    short Event = IByte(&buf[0]);
    short EventMinor = 0;

    if (NasVerbose == 0) {
        AudioSimpleDump(DUMP_EVENT, fd, Event, EventMinor, n);
        return;
    }

    SetIndentLevel(PRINTSERVER);
    if (NasVerbose > 3)
        DumpItem("Event", fd, buf, n);
    /* high-order bit means SendEvent generated */
    if (Event & 0x80) {
        debug(8, (stderr, "SendEvent generated event 0x%x\n", Event));
        Event = Event & 0x7F;
    }
    switch (Event) {
    case 2:
        AudioElementNotify(buf);
        break;
    case 3:
        AudioGrabNotify(buf);
        break;
    case 4:
        AudioMonitorNotify(buf);
        break;
    case 5:
        AudioBucketNotify(buf);
        break;
    case 6:
        AudioDeviceNotify(buf);
        break;
    default:
        warn("Unimplemented event code");
        break;
    }
}

void
InitializeAudioDecode(void)
{
    TYPE p;

    p = DefineType(NASREQUEST, ENUMERATED, "NASREQUEST",
                   (PrintProcType) PrintENUMERATED);
    DefineEValue(p, 1L, "ListDevices");
    DefineEValue(p, 2L, "GetDeviceAttributes");
    DefineEValue(p, 3L, "SetDeviceAttributes");
    DefineEValue(p, 4L, "CreateBucket");
    DefineEValue(p, 5L, "DestroyBucket");
    DefineEValue(p, 6L, "ListBuckets");
    DefineEValue(p, 7L, "GetBucketAttributes");
    DefineEValue(p, 8L, "SetBucketAttributes");
    DefineEValue(p, 9L, "CreateRadio");
    DefineEValue(p, 10L, "DestroyRadio");
    DefineEValue(p, 11L, "ListRadios");
    DefineEValue(p, 12L, "GetRadioAttributes");
    DefineEValue(p, 13L, "SetRadioAttributes");
    DefineEValue(p, 14L, "CreateFlow");
    DefineEValue(p, 15L, "DestroyFlow");
    DefineEValue(p, 16L, "GetFlowAttributes");
    DefineEValue(p, 17L, "SetFlowAttributes");
    DefineEValue(p, 18L, "GetElements");
    DefineEValue(p, 19L, "SetElements");
    DefineEValue(p, 20L, "GetElementStates");
    DefineEValue(p, 21L, "SetElementStates");
    DefineEValue(p, 22L, "GetElementParameters");
    DefineEValue(p, 23L, "SetElementParameters");
    DefineEValue(p, 24L, "WriteElement");
    DefineEValue(p, 25L, "ReadElement");
    DefineEValue(p, 26L, "GrabComponent");
    DefineEValue(p, 27L, "UngrabComponent");
    DefineEValue(p, 28L, "SendEvent");
    DefineEValue(p, 29L, "GetAllowedUsers");
    DefineEValue(p, 30L, "SetAllowedUsers");
    DefineEValue(p, 31L, "ListExtensions");
    DefineEValue(p, 32L, "QuerExtension");
    DefineEValue(p, 33L, "GetCloseDownMode");
    DefineEValue(p, 34L, "SetCloseDownMode");
    DefineEValue(p, 35L, "KillClient");
    DefineEValue(p, 36L, "GetServerTime");
    DefineEValue(p, 37L, "NoOperation");
    p = DefineType(NASREPLY, ENUMERATED, "NASREPLY",
                   (PrintProcType) PrintENUMERATED);
    DefineEValue(p, 1L, "ListDevices");
    DefineEValue(p, 2L, "GetDeviceAttributes");
    DefineEValue(p, 6L, "ListBuckets");
    DefineEValue(p, 7L, "GetBucketAttributes");
    DefineEValue(p, 11L, "ListRadios");
    DefineEValue(p, 12L, "GetRadioAttributes");
    DefineEValue(p, 16L, "GetFlowAttributes");
    DefineEValue(p, 18L, "GetElements");
    DefineEValue(p, 20L, "GetElementStates");
    DefineEValue(p, 22L, "GetElementParameters");
    DefineEValue(p, 25L, "ReadElement");
    DefineEValue(p, 29L, "GetAllowedUsers");
    DefineEValue(p, 31L, "ListExtensions");
    DefineEValue(p, 32L, "QueryExtension");
    DefineEValue(p, 33L, "GetCloseDownMode");
    DefineEValue(p, 36L, "GetServerTime");
    p = DefineType(NASEVENT, ENUMERATED, "NASEVENT",
                   (PrintProcType) PrintENUMERATED);
    DefineEValue(p, 2L, "ElementNotify");
    DefineEValue(p, 3L, "GrabNotify");
    DefineEValue(p, 4L, "MonitorNotify");
    DefineEValue(p, 5L, "BucketNotify");
    DefineEValue(p, 6L, "DeviceNotify");

}
