/*
   Copyright (c) 2002  XFree86 Inc
*/

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xproto.h>
#include "misc.h"
#include "os.h"
#include "dixstruct.h"
#include "extnsionst.h"
#include "swaprep.h"
#include "registry.h"
#include <X11/extensions/XResproto.h>
#include "pixmapstr.h"
#include "windowstr.h"
#include "gcstruct.h"
#include "modinit.h"
#include "protocol-versions.h"

static int
ProcXResQueryVersion (ClientPtr client)
{
    REQUEST(xXResQueryVersionReq);
    xXResQueryVersionReply rep;
    CARD16 client_major, client_minor;  /* not used */

    REQUEST_SIZE_MATCH (xXResQueryVersionReq);

    client_major = stuff->client_major;
    client_minor = stuff->client_minor;
    (void) client_major;
    (void) client_minor;

    rep.type = X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.server_major = SERVER_XRES_MAJOR_VERSION;
    rep.server_minor = SERVER_XRES_MINOR_VERSION;
    if (client->swapped) { 
        int n;
        swaps(&rep.sequenceNumber, n);
        swapl(&rep.length, n);     
        swaps(&rep.server_major, n);
        swaps(&rep.server_minor, n);
    }
    WriteToClient(client, sizeof (xXResQueryVersionReply), (char *)&rep);
    return Success;
}

static int
ProcXResQueryClients (ClientPtr client)
{
    /* REQUEST(xXResQueryClientsReq); */
    xXResQueryClientsReply rep;
    int *current_clients;
    int i, num_clients;

    REQUEST_SIZE_MATCH(xXResQueryClientsReq);

    current_clients = malloc(currentMaxClients * sizeof(int));

    num_clients = 0;
    for(i = 0; i < currentMaxClients; i++) {
       if(clients[i]) {
           current_clients[num_clients] = i;
           num_clients++;   
       }
    }

    rep.type = X_Reply;
    rep.sequenceNumber = client->sequence;
    rep.num_clients = num_clients;
    rep.length = bytes_to_int32(rep.num_clients * sz_xXResClient);
    if (client->swapped) {
        int n;
        swaps (&rep.sequenceNumber, n);
        swapl (&rep.length, n);
        swapl (&rep.num_clients, n);
    }   
    WriteToClient (client, sizeof (xXResQueryClientsReply), (char *) &rep);

    if(num_clients) {
        xXResClient scratch;

        for(i = 0; i < num_clients; i++) {
            scratch.resource_base = clients[current_clients[i]]->clientAsMask;
            scratch.resource_mask = RESOURCE_ID_MASK;
        
            if(client->swapped) {
                int n;
                swapl (&scratch.resource_base, n);
                swapl (&scratch.resource_mask, n);
            }
            WriteToClient (client, sz_xXResClient, (char *) &scratch);
        }
    }

    free(current_clients);

    return Success;
}


static void
ResFindAllRes (pointer value, XID id, RESTYPE type, pointer cdata)
{
    int *counts = (int *)cdata;

    counts[(type & TypeMask) - 1]++;
}

static int
ProcXResQueryClientResources (ClientPtr client)
{
    REQUEST(xXResQueryClientResourcesReq);
    xXResQueryClientResourcesReply rep;
    int i, clientID, num_types;
    int *counts;

    REQUEST_SIZE_MATCH(xXResQueryClientResourcesReq);

    clientID = CLIENT_ID(stuff->xid);

    if((clientID >= currentMaxClients) || !clients[clientID]) {
        client->errorValue = stuff->xid;
        return BadValue;
    }

    counts = calloc(lastResourceType + 1, sizeof(int));

    FindAllClientResources(clients[clientID], ResFindAllRes, counts);

    num_types = 0;

    for(i = 0; i <= lastResourceType; i++) {
       if(counts[i]) num_types++;
    }

    rep.type = X_Reply;
    rep.sequenceNumber = client->sequence;
    rep.num_types = num_types;
    rep.length = bytes_to_int32(rep.num_types * sz_xXResType);
    if (client->swapped) {
        int n;
        swaps (&rep.sequenceNumber, n);
        swapl (&rep.length, n);
        swapl (&rep.num_types, n);
    }   

    WriteToClient (client,sizeof(xXResQueryClientResourcesReply),(char*)&rep);

    if(num_types) {
        xXResType scratch;
	char *name;

        for(i = 0; i < lastResourceType; i++) {
            if(!counts[i]) continue;

	    name = (char *)LookupResourceName(i + 1);
            if (strcmp(name, XREGISTRY_UNKNOWN))
		scratch.resource_type = MakeAtom(name, strlen(name), TRUE);
	    else {
                char buf[40];
                snprintf(buf, sizeof(buf), "Unregistered resource %i", i + 1);
		scratch.resource_type = MakeAtom(buf, strlen(buf), TRUE);
            }

            scratch.count = counts[i];

            if(client->swapped) {
                int n;
                swapl (&scratch.resource_type, n);
                swapl (&scratch.count, n);
            }
            WriteToClient (client, sz_xXResType, (char *) &scratch);
        }
    }

    free(counts);
    
    return Success;
}

static unsigned long
ResGetApproxPixmapBytes (PixmapPtr pix)
{
   unsigned long nPixels;
   int           bytesPerPixel; 

   bytesPerPixel = pix->drawable.bitsPerPixel>>3;
   nPixels       = pix->drawable.width * pix->drawable.height;

   /* Divide by refcnt as pixmap could be shared between clients,  
    * so total pixmap mem is shared between these. 
   */
   return ( nPixels * bytesPerPixel ) / pix->refcnt;
}

static void 
ResFindPixmaps (pointer value, XID id, pointer cdata)
{
   unsigned long *bytes = (unsigned long *)cdata;
   PixmapPtr pix = (PixmapPtr)value;

   *bytes += ResGetApproxPixmapBytes(pix);
}

static void
ResFindWindowPixmaps (pointer value, XID id, pointer cdata)
{
   unsigned long *bytes = (unsigned long *)cdata;
   WindowPtr pWin = (WindowPtr)value;

   if (pWin->backgroundState == BackgroundPixmap)
     *bytes += ResGetApproxPixmapBytes(pWin->background.pixmap);

   if (pWin->border.pixmap != NULL && !pWin->borderIsPixel)
     *bytes += ResGetApproxPixmapBytes(pWin->border.pixmap);
}

static void
ResFindGCPixmaps (pointer value, XID id, pointer cdata)
{
   unsigned long *bytes = (unsigned long *)cdata;
   GCPtr pGC = (GCPtr)value;

   if (pGC->stipple != NULL)
     *bytes += ResGetApproxPixmapBytes(pGC->stipple);

   if (pGC->tile.pixmap != NULL && !pGC->tileIsPixel)
     *bytes += ResGetApproxPixmapBytes(pGC->tile.pixmap);
}

static int
ProcXResQueryClientPixmapBytes (ClientPtr client)
{
    REQUEST(xXResQueryClientPixmapBytesReq);
    xXResQueryClientPixmapBytesReply rep;
    int clientID;
    unsigned long bytes;

    REQUEST_SIZE_MATCH(xXResQueryClientPixmapBytesReq);

    clientID = CLIENT_ID(stuff->xid);

    if((clientID >= currentMaxClients) || !clients[clientID]) {
        client->errorValue = stuff->xid;
        return BadValue;
    }

    bytes = 0;

    FindClientResourcesByType(clients[clientID], RT_PIXMAP, ResFindPixmaps, 
                              (pointer)(&bytes));

    /* 
     * Make sure win background pixmaps also held to account. 
     */
    FindClientResourcesByType(clients[clientID], RT_WINDOW, 
			      ResFindWindowPixmaps, 
                              (pointer)(&bytes));

    /* 
     * GC Tile & Stipple pixmaps too.
    */
    FindClientResourcesByType(clients[clientID], RT_GC, 
			      ResFindGCPixmaps, 
                              (pointer)(&bytes));

#ifdef COMPOSITE
    /* FIXME: include composite pixmaps too */
#endif

    rep.type = X_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = 0;
    rep.bytes = bytes;
#ifdef _XSERVER64
    rep.bytes_overflow = bytes >> 32;
#else
    rep.bytes_overflow = 0;
#endif
    if (client->swapped) {
        int n;
        swaps (&rep.sequenceNumber, n);
        swapl (&rep.length, n);
        swapl (&rep.bytes, n);
        swapl (&rep.bytes_overflow, n);
    }
    WriteToClient (client,sizeof(xXResQueryClientPixmapBytesReply),(char*)&rep);

    return Success;
}

static int
ProcResDispatch (ClientPtr client)
{
    REQUEST(xReq);
    switch (stuff->data) {
    case X_XResQueryVersion:
        return ProcXResQueryVersion(client);
    case X_XResQueryClients:
        return ProcXResQueryClients(client);
    case X_XResQueryClientResources:
        return ProcXResQueryClientResources(client);
    case X_XResQueryClientPixmapBytes:
        return ProcXResQueryClientPixmapBytes(client);
    default: break;
    }

    return BadRequest;
}

static int
SProcXResQueryVersion (ClientPtr client)
{
    REQUEST(xXResQueryVersionReq);
    int n;

    REQUEST_SIZE_MATCH (xXResQueryVersionReq);
    swaps(&stuff->client_major,n);
    swaps(&stuff->client_minor,n);
    return ProcXResQueryVersion(client);
}

static int
SProcXResQueryClientResources (ClientPtr client)
{
    REQUEST(xXResQueryClientResourcesReq);
    int n;

    REQUEST_SIZE_MATCH (xXResQueryClientResourcesReq);
    swaps(&stuff->xid,n);
    return ProcXResQueryClientResources(client);
}

static int
SProcXResQueryClientPixmapBytes (ClientPtr client)
{
    REQUEST(xXResQueryClientPixmapBytesReq);
    int n;

    REQUEST_SIZE_MATCH (xXResQueryClientPixmapBytesReq);
    swaps(&stuff->xid,n);
    return ProcXResQueryClientPixmapBytes(client);
}

static int
SProcResDispatch (ClientPtr client)
{
    REQUEST(xReq);
    int n;

    swaps(&stuff->length,n);

    switch (stuff->data) {
    case X_XResQueryVersion:
        return SProcXResQueryVersion(client);
    case X_XResQueryClients:  /* nothing to swap */
        return ProcXResQueryClients(client);
    case X_XResQueryClientResources:
        return SProcXResQueryClientResources(client);
    case X_XResQueryClientPixmapBytes:
        return SProcXResQueryClientPixmapBytes(client);
    default: break;
    }

    return BadRequest;
}

void
ResExtensionInit(INITARGS)
{
    (void) AddExtension(XRES_NAME, 0, 0,
                            ProcResDispatch, SProcResDispatch,
                            NULL, StandardMinorOpcode);
}
