/*
 * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "extensions.h"
#include "bigreqscope.h"
#include "lbxscope.h"
#include "randrscope.h"
#include "renderscope.h"
#include "shmscope.h"
#include "wcpscope.h"

/* Extensions we know how to decode */
struct extension_decoders {
    const char *name;
    void (*init_func) (const unsigned char *buf);
};

static const struct extension_decoders decodable_extensions[] = {
    {"BIG-REQUESTS", InitializeBIGREQ},
    {"LBX", InitializeLBX},
    {"MIT-SHM", InitializeMITSHM},
    {"NCD-WinCenterPro", InitializeWCP},
    {"RANDR", InitializeRANDR},
    {"RENDER", InitializeRENDER},
    {"GLX", InitializeGLX},
    {NULL, NULL}                /* List terminator - keep last */
};

/* all extensions we know about */
struct extension_info {
    const char *name;
    unsigned char request;
    unsigned char event;
    unsigned char error;
    long query_seq;             /* sequence id of QueryExtension request */
    struct extension_info *next;
};

struct extension_info *query_list;

struct extension_info *ext_by_request[NUM_EXTENSIONS];

static void
DefineExtNameValue(int type, unsigned char value, const char *extname)
{
    int namelen = strlen(extname) + 1;
    const char *typename = NULL;
    char *exttypename;

    switch (type) {
    case REQUEST:
        typename = "-Request";
        break;
    case REPLY:
        typename = "-Reply";
        break;
    case EVENT:
        typename = "-Event";
        break;
    case ERROR:
        typename = "-Error";
        break;
    case EXTENSION:
        typename = "";
        break;
    default:
        panic("Impossible argument to DefineExtNameValue");
    }
    namelen += strlen(typename);
    exttypename = malloc(namelen);
    if (exttypename == NULL)
        panic("Can't allocate memory for ExtNameValue");
    snprintf(exttypename, namelen, "%s%s", extname, typename);
    DefineEValue(&TD[type], (unsigned long) value, exttypename);
}

void
ProcessQueryExtensionRequest(long seq, const unsigned char *buf)
{
    int namelen = IShort(&buf[4]);
    char *extname = malloc(namelen + 1);
    struct extension_info *qe;

    if (extname == NULL)
        panic("Can't allocate memory for ExtensionRequest name");
    memcpy(extname, &buf[8], namelen);
    extname[namelen] = '\0';

    for (qe = query_list; qe != NULL; qe = qe->next) {
        if (strcmp(extname, qe->name) == 0) {
            /* already in list, no need to add */
            free(extname);
            return;
        }
    }

    /* add to list */
    qe = malloc(sizeof(struct extension_info));
    if (qe == NULL)
        panic("Can't allocate memory for extension_info");
    qe->name = extname;
    qe->request = 0;
    qe->event = 0;
    qe->error = 0;
    qe->query_seq = seq;
    qe->next = query_list;
    query_list = qe;
}

void
ProcessQueryExtensionReply(long seq, const unsigned char *buf)
{
    struct extension_info *qe;
    int i;

    if (buf[8] == 0) {
        /* Extension not present, nothing to record */
        return;
    }

    for (qe = query_list; qe != NULL; qe = qe->next) {
        if (qe->query_seq == seq) {
            qe->request = buf[9];
            qe->event = buf[10];
            qe->error = buf[11];

            ext_by_request[qe->request - EXTENSION_MIN_REQ] = qe;

            DefineExtNameValue(EXTENSION, qe->request, qe->name);

            for (i = 0; decodable_extensions[i].name != NULL; i++) {
                if (strcmp(qe->name, decodable_extensions[i].name) == 0) {
                    decodable_extensions[i].init_func(buf);
                    break;
                }
            }
            if (decodable_extensions[i].name == NULL) {
                /* Not found - initialize values as best we can generically */
                DefineExtNameValue(REQUEST, qe->request, qe->name);
                DefineExtNameValue(REPLY, qe->request, qe->name);
                if (qe->event != 0) {
                    DefineExtNameValue(EVENT, qe->event, qe->name);
                }
                if (qe->error != 0) {
                    DefineExtNameValue(ERROR, qe->error, qe->name);
                }
            }

            return;
        }
    }
}

/* Decoding for specific/known extensions */

static extension_decode_req_ptr ExtensionRequestDecoder[NUM_EXTENSIONS];
static extension_decode_reply_ptr ExtensionReplyDecoder[NUM_EXTENSIONS];
static extension_decode_error_ptr ExtensionErrorDecoder[NUM_EXTENSIONS];
static extension_decode_event_ptr ExtensionEventDecoder[NUM_EXT_EVENTS];
static extension_decode_event_ptr GenericEventDecoder[NUM_EXTENSIONS];

void
InitializeExtensionDecoder(int Request, extension_decode_req_ptr reqd,
                           extension_decode_reply_ptr repd)
{
    if ((Request > EXTENSION_MAX_REQ) || (Request < EXTENSION_MIN_REQ)) {
        char errmsg[128];

        snprintf(errmsg, sizeof(errmsg), "Failed to register decoder"
                 " for invalid extension request code %d.", Request);
        warn(errmsg);
        return;
    }
    ExtensionRequestDecoder[Request - EXTENSION_MIN_REQ] = reqd;
    ExtensionReplyDecoder[Request - EXTENSION_MIN_REQ] = repd;
}

void
InitializeExtensionErrorDecoder(int Error, extension_decode_error_ptr errd)
{
    if ((Error > EXTENSION_MAX_ERR) || (Error < EXTENSION_MIN_ERR)) {
        char errmsg[128];

        snprintf(errmsg, sizeof(errmsg), "Failed to register decoder"
                 " for invalid extension error code %d.", Error);
        warn(errmsg);
        return;
    }
    ExtensionErrorDecoder[Error - EXTENSION_MIN_ERR] = errd;
}

void
InitializeExtensionEventDecoder(int Event, extension_decode_event_ptr evd)
{
    if ((Event > EXTENSION_MAX_EV) || (Event < EXTENSION_MIN_EV)) {
        char errmsg[128];

        snprintf(errmsg, sizeof(errmsg), "Failed to register decoder"
                 " for invalid extension event code %d.", Event);
        warn(errmsg);
        return;
    }
    ExtensionEventDecoder[Event - EXTENSION_MIN_EV] = evd;
}

void
InitializeGenericEventDecoder(int Request, extension_decode_event_ptr evd)
{
    if ((Request > EXTENSION_MAX_REQ) || (Request < EXTENSION_MIN_REQ)) {
        char errmsg[128];

        snprintf(errmsg, sizeof(errmsg), "Failed to register decoder"
                 " for invalid generic extension event code %d.", Request);
        warn(errmsg);
        return;
    }
    GenericEventDecoder[Request - EXTENSION_MIN_REQ] = evd;
}

void
ExtensionRequest(FD fd, const unsigned char *buf, short Request)
{
    extension_decode_req_ptr decode_req = NULL;

    if ((Request <= EXTENSION_MAX_REQ) && (Request >= EXTENSION_MIN_REQ)) {
        decode_req = ExtensionRequestDecoder[Request - EXTENSION_MIN_REQ];
    }

    if (decode_req != NULL) {
        decode_req(fd, buf);
    }
    else {
        ExtendedRequest(fd, buf);
        ReplyExpected(fd, Request);
    }
}

void
ExtensionReply(FD fd, const unsigned char *buf,
               short Request, short RequestMinor)
{
    extension_decode_reply_ptr decode_reply = NULL;

    if ((Request <= EXTENSION_MAX_REQ) && (Request >= EXTENSION_MIN_REQ)) {
        decode_reply = ExtensionReplyDecoder[Request - EXTENSION_MIN_REQ];
    }

    if (decode_reply != NULL) {
        decode_reply(fd, buf, RequestMinor);
    }
    else {
        UnknownReply(buf);
    }
}

void
ExtensionError(FD fd, const unsigned char *buf, short Error)
{
    extension_decode_error_ptr decode_error = NULL;

    if ((Error <= EXTENSION_MAX_ERR) && (Error >= EXTENSION_MIN_ERR)) {
        decode_error = ExtensionErrorDecoder[Error - EXTENSION_MIN_ERR];
    }

    if (decode_error != NULL) {
        decode_error(fd, buf);
    }
    else {
        UnknownError(buf);
    }
}

void
ExtensionEvent(FD fd, const unsigned char *buf, short Event)
{
    extension_decode_event_ptr decode_event = NULL;

    if ((Event <= EXTENSION_MAX_EV) && (Event >= EXTENSION_MIN_EV)) {
        decode_event = ExtensionEventDecoder[Event - EXTENSION_MIN_EV];
    }
    else if (Event == Event_Type_Generic) {
        int Request = IByte(&buf[1]);

        if ((Request <= EXTENSION_MAX_REQ) && (Request >= EXTENSION_MIN_REQ)) {
            decode_event = GenericEventDecoder[Request - EXTENSION_MIN_REQ];
        }
    }

    if (decode_event != NULL) {
        decode_event(fd, buf);
    }
    else if (Event == Event_Type_Generic) {
        UnknownGenericEvent(buf);
    }
    else {
        UnknownEvent(buf);
    }
}
