/* $XConsortium: options.c /main/13 1996/11/15 21:29:05 rws $ */
/*
 * Copyright 1994 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.
 *
 */
/* $XFree86: xc/programs/lbxproxy/di/options.c,v 1.1.1.1.2.2 1998/10/04 15:23:07 hohndel Exp $ */

#include <stdio.h>
#include "X.h"
#include "misc.h"
#include "XLbx.h"
#include "lbxopts.h"
#include "proxyopts.h"
#include "lbximage.h"
#include "util.h"
#include "colormap.h"
#ifndef NO_ZLIB
#include "lbxzlib.h"
#endif /* NO_ZLIB */

static int LbxDeltaReply();
static int LbxProxyDeltaReq();
static int LbxServerDeltaReq();
static int LbxStreamCompReq();
static int LbxBitmapCompReq();
static int LbxPixmapCompReq();
static int LbxSquishReq();
static int LbxUseTagsReq();
static int LbxCmapAllReq();

static int LbxProxyDeltaReply();
static int LbxServerDeltaReply();
static int LbxStreamCompReply();
static int LbxBitmapCompReply();
static int LbxPixmapCompReply();
static int LbxSquishReply();
static int LbxUseTagsReply();
static int LbxCmapAllReply();

#define OPT_INDEX_STREAM_COMP		2

/*
 * List of LBX options we recognize and may negotiate
 */
static struct _LbxOption {
    CARD8	optcode;
    Bool	negotiate;
    int		(*req)();
    int		(*reply)();
} LbxOptions[] = {
    { LBX_OPT_DELTA_PROXY,  TRUE,  LbxProxyDeltaReq,	LbxProxyDeltaReply },
    { LBX_OPT_DELTA_SERVER, TRUE,  LbxServerDeltaReq,	LbxServerDeltaReply },
    { LBX_OPT_STREAM_COMP,  TRUE,  LbxStreamCompReq,	LbxStreamCompReply },
    { LBX_OPT_BITMAP_COMP,  TRUE,  LbxBitmapCompReq,	LbxBitmapCompReply },
    { LBX_OPT_PIXMAP_COMP,  TRUE,  LbxPixmapCompReq,	LbxPixmapCompReply },
    { LBX_OPT_MSG_COMP,     TRUE,  LbxSquishReq,	LbxSquishReply },
    { LBX_OPT_USE_TAGS,     TRUE,  LbxUseTagsReq,	LbxUseTagsReply },
    { LBX_OPT_CMAP_ALL,	    TRUE,  LbxCmapAllReq,	LbxCmapAllReply },
};

#define LBX_N_OPTS	(sizeof(LbxOptions) / sizeof(struct _LbxOption))

static int optcount;
static int optlist[LBX_N_OPTS];

LbxNegOptsRec lbxNegOpt;

void
LbxOptInit()
{
    bzero((char *)&lbxNegOpt, sizeof(LbxNegOptsRec));
    lbxNegOpt.proxyDeltaN = LBX_OPT_DELTA_NCACHE_DFLT;
    lbxNegOpt.proxyDeltaMaxLen = LBX_OPT_DELTA_MSGLEN_DFLT;
    lbxNegOpt.serverDeltaN = LBX_OPT_DELTA_NCACHE_DFLT;
    lbxNegOpt.serverDeltaMaxLen = LBX_OPT_DELTA_MSGLEN_DFLT;
    lbxNegOpt.numBitmapCompMethods = 0;
    lbxNegOpt.bitmapCompMethods = NULL;
    lbxNegOpt.numPixmapCompMethods = 0;
    lbxNegOpt.pixmapCompMethods = NULL;
    lbxNegOpt.squish = TRUE;
    lbxNegOpt.useTags = TRUE;
}

int
LbxOptBuildReq(buf)
    register char *buf;
{
    int		  i;
    char *bufstart = buf;
    char *pnopts = buf++;

    optcount = 0;

    for (i = 0; i < LBX_N_OPTS; i++) {
	int reqlen;

	if (LbxOptions[i].negotiate) {
	    reqlen = (*LbxOptions[i].req)(buf + LBX_OPT_SMALLHDR_LEN);
	    *buf++ = LbxOptions[i].optcode;
	    *buf++ = LBX_OPT_SMALLHDR_LEN + reqlen;
	    buf += reqlen;
	    optlist[optcount++] = i;
	}
    }

    *pnopts = optcount;
    return (buf - bufstart);
}

int
LbxOptParseReply(nopts, preply, replylen)
    int		  nopts;
    unsigned char *preply;
    int		  replylen;
{
    int		  i;

    for (i = 0; i < nopts; i++) {
	int len;
	int hdrlen;
	int result;
	int optindex = *preply;

	if (optindex >= optcount)
	    return -1;
	LBX_OPT_DECODE_LEN(preply + 1, len, hdrlen);
	if (len < ++hdrlen || len > replylen) {
#ifdef OPTDEBUG
	    fprintf(stderr, "bad length, len = %d, hdrlen = %d, optlen = %d\n",
			    len, hdrlen, replylen);
#endif
	    return -1;
	}

	result = (*LbxOptions[optlist[optindex]].reply)(preply + hdrlen,
							len - hdrlen);
	if (result < 0)
	    return -1;

	preply += len;
	replylen -= len;
    }
    return 0;
}

struct deltaOpt {
    CARD8	minN;			/* min cache size */
    CARD8	maxN;			/* max cache size */
    CARD8	prefN;			/* preferred cache size */
    CARD8	minMaxMsgLen;		/* min max-message-len / 4 */
    CARD8	maxMaxMsgLen;		/* max max-message-len / 4 */
    CARD8	prefMaxMsgLen;		/* preferred max-message-len / 4 */
};

static struct deltaOpt proxyDeltaOpt = {
    0,
    0xff,
    LBX_OPT_DELTA_NCACHE_DFLT,
    LBX_OPT_DELTA_MSGLEN_MIN >> 2,
    0xff,
    LBX_OPT_DELTA_MSGLEN_DFLT >> 2
};

static struct deltaOpt serverDeltaOpt = {
    0,
    0xff,
    LBX_OPT_DELTA_NCACHE_DFLT,
    LBX_OPT_DELTA_MSGLEN_MIN >> 2,
    0xff,
    LBX_OPT_DELTA_MSGLEN_DFLT >> 2
};

void
LbxNoDelta()
{
    proxyDeltaOpt.prefN = 0;
    serverDeltaOpt.prefN = 0;
}

static int
LbxProxyDeltaReq(buf)
    unsigned char *buf;
{
    memcpy(buf, (char *)&proxyDeltaOpt, sizeof(struct deltaOpt));
    return LBX_OPT_DELTA_REQLEN;
}

static int
LbxServerDeltaReq(buf)
    unsigned char *buf;
{
    memcpy(buf, (char *)&serverDeltaOpt, sizeof(struct deltaOpt));
    return LBX_OPT_DELTA_REQLEN;
}

static int
LbxProxyDeltaReply(preply, replylen)
    unsigned char *preply;
    int		  replylen;
{
    return LbxDeltaReply(preply, 
			 replylen,
			 &lbxNegOpt.proxyDeltaN,
			 &lbxNegOpt.proxyDeltaMaxLen);
}

static int
LbxServerDeltaReply(preply, replylen)
    unsigned char *preply;
    int		  replylen;
{
    return LbxDeltaReply(preply, 
			 replylen,
			 &lbxNegOpt.serverDeltaN,
			 &lbxNegOpt.serverDeltaMaxLen);
}

static int
LbxDeltaReply(preply, replylen, pn, pmaxlen)
    unsigned char *preply;
    int		  replylen;
    short	  *pn;
    short	  *pmaxlen;
{
    if (replylen < 2)
	return -1;
    *pn = *preply++;
    *pmaxlen = *preply;
    if ((*pmaxlen <<= 2) == 0)
	*pn = 0;
    else if (*pmaxlen < 32) {
#ifdef DEBUG
	printf("bad delta max msg length %d\n", *pmaxlen);
#endif
	return -1;
     }
    return 0;
}

static int OptZlibReq();
static int OptZlibReply();

static struct _LbxStreamComp {
    int		typelen;
    char	*type;
    int		(*req)();
    int		(*reply)();
} LbxStreamComp[] = {
#ifndef NO_ZLIB
    { ZLIB_STRCOMP_OPT_LEN,  ZLIB_STRCOMP_OPT,  OptZlibReq,  OptZlibReply }
#endif /* NO_ZLIB */
};

#define LBX_N_STRCOMP	\
    (sizeof(LbxStreamComp) / sizeof(struct _LbxStreamComp))

void
LbxNoComp()
{
    LbxOptions[OPT_INDEX_STREAM_COMP].negotiate = FALSE;
}

static int
LbxStreamCompReq(buf)
    unsigned char *buf;
{
    int		  i;
    int		  reqlen;

    *buf++ = LBX_N_STRCOMP;
    reqlen = 1;

    for (i = 0; i < LBX_N_STRCOMP; i++) {
	int optdatalen;
	int typelen = LbxStreamComp[i].typelen;

	*buf++ = typelen;
	memcpy(buf, LbxStreamComp[i].type, typelen);
	buf += typelen;
	reqlen += 1 + typelen;

	optdatalen = (*LbxStreamComp[i].req)(buf + 1);
	*buf = optdatalen + 1;
	buf += optdatalen + 1;
	reqlen += optdatalen + 1;
    }

    return reqlen;
}

static int
LbxStreamCompReply(preply, replylen)
    unsigned char *preply;
    int		  replylen;
{
    int		  optindex;

    if (replylen < 1 || (optindex = *preply) >= LBX_N_STRCOMP)
	return -1;
    return (*LbxStreamComp[optindex].reply)(preply + 1, replylen - 1);
	
}

extern int  zlevel;

static int
OptZlibReq(buf)
    unsigned char *buf;
{
    *buf++ = 1; /* len */
    *buf = zlevel;
    return (2);
}

extern LbxStreamCompHandle ZlibInit();
extern int ZlibStuffInput(), ZlibInputAvail(), ZlibFlush(),
	 ZlibRead(), ZlibWriteV();
extern void ZlibCompressOn(), ZlibCompressOff(), ZlibFree();

/*ARGSUSED*/
static int
OptZlibReply(preply, replylen)
    unsigned char *preply;
    int		  replylen;
{
    lbxNegOpt.streamOpts.streamCompInit = ZlibInit;
    lbxNegOpt.streamOpts.streamCompArg = (pointer)(long) zlevel;
    lbxNegOpt.streamOpts.streamCompStuffInput = ZlibStuffInput;
    lbxNegOpt.streamOpts.streamCompInputAvail = ZlibInputAvail;
    lbxNegOpt.streamOpts.streamCompFlush = ZlibFlush;
    lbxNegOpt.streamOpts.streamCompRead = ZlibRead;
    lbxNegOpt.streamOpts.streamCompWriteV = ZlibWriteV;
    lbxNegOpt.streamOpts.streamCompOn = ZlibCompressOn;
    lbxNegOpt.streamOpts.streamCompOff = ZlibCompressOff;
    lbxNegOpt.streamOpts.streamCompFreeHandle = ZlibFree;

    return 0;
}

Bool lbxDoSquishing = TRUE;

static int
LbxSquishReq(buf)
    unsigned char *buf;
{
    *buf = lbxDoSquishing;
    return 1;
}

static int
LbxSquishReply(preply, replylen)
    unsigned char *preply;
    int		  replylen;
{
    if (replylen < 1)
	return -1;
    lbxNegOpt.squish = *preply;
    return 0;
}


Bool lbxUseTags = TRUE;

static int
LbxUseTagsReq(buf)
    unsigned char *buf;
{
    *buf = lbxUseTags;
    return 1;
}


static int
LbxUseTagsReply(preply, replylen)
    unsigned char *preply;
    int		  replylen;
{
    if (replylen < 1)
	return -1;
    lbxNegOpt.useTags = *preply;
    return 0;
}


/*
 * Option negotiation for image compression
 */

LbxBitmapCompMethod
LbxBitmapCompMethods [] = {
  {
    "XC-FaxG42D",		/* compression method name */
    0,				/* inited */
    -1,				/* method opcode - to be filled on reply */
    NULL,			/* init function */
    LbxImageEncodeFaxG42D,	/* encode function */
    LbxImageDecodeFaxG42D	/* decode function */
  }
};

#define NUM_BITMAP_METHODS \
	(sizeof (LbxBitmapCompMethods) / sizeof (LbxBitmapCompMethod))



#if 1
/*
 * Currently, we don't support any pixmap compression algorithms
 * because regular stream compression does much better than PackBits.
 * If we want to plug in a better pixmap image compression algorithm,
 * it would go here.
 */

#define NUM_PIXMAP_METHODS 0
LbxPixmapCompMethod LbxPixmapCompMethods [1]; /* dummy */

#else

LbxPixmapCompMethod
LbxPixmapCompMethods [] = {
  {
    "XC-PackBits",		/* compression method name */
    1 << ZPixmap,		/* formats supported */
    1, {8},			/* depths supported */
    0,				/* inited */
    -1,				/* method opcode - to be filled on reply */
    NULL,			/* init function */
    LbxImageEncodePackBits,	/* encode function */
    LbxImageDecodePackBits	/* decode function */
  }
};

#define NUM_PIXMAP_METHODS \
	(sizeof (LbxPixmapCompMethods) / sizeof (LbxPixmapCompMethod))
#endif



static int
LbxBitmapCompReq (buf)

unsigned char *buf;

{
    unsigned char *bufStart = buf;
    int i;

    *buf++ = NUM_BITMAP_METHODS;

    for (i = 0; i < NUM_BITMAP_METHODS; i++)
    {
	int len = strlen (LbxBitmapCompMethods[i].methodName);
	*buf++ = len;
	memcpy (buf, LbxBitmapCompMethods[i].methodName, len);
	buf += len;
    }

    return (buf - bufStart);
}

/*ARGSUSED*/
static int
LbxBitmapCompReply (preply, replylen)

unsigned char *preply;
int	      replylen;

{
    int count = *preply++;
    int i;

    lbxNegOpt.numBitmapCompMethods = count;

    if (count > 0)
    {
	lbxNegOpt.bitmapCompMethods = (char *) xalloc (count);
	if (lbxNegOpt.bitmapCompMethods == NULL)
	{
	    lbxNegOpt.numBitmapCompMethods = 0;
	    return -1;
	}
    }

    for (i = 0; i < count; i++)
    {
	int index = *preply++;
	lbxNegOpt.bitmapCompMethods[i] = index;
	LbxBitmapCompMethods[index].methodOpCode = *preply++;
    }

    return 0;
}


static int
LbxPixmapCompReq (buf)

unsigned char *buf;

{
    unsigned char *bufStart = buf;
    int i, j;

    *buf++ = NUM_PIXMAP_METHODS;

    for (i = 0; i < NUM_PIXMAP_METHODS; i++)
    {
	int len = strlen (LbxPixmapCompMethods[i].methodName);
	*buf++ = len;
	memcpy (buf, LbxPixmapCompMethods[i].methodName, len);
	buf += len;
	*buf++ = LbxPixmapCompMethods[i].formatMask;
	*buf++ = LbxPixmapCompMethods[i].depthCount;
	for (j = 0; j < LbxPixmapCompMethods[i].depthCount; j++)
	    *buf++ = LbxPixmapCompMethods[i].depths[j];
    }

    return (buf - bufStart);
}

/*ARGSUSED*/
static int
LbxPixmapCompReply (preply, replylen)

unsigned char *preply;
int	      replylen;

{
    int count = *preply++;
    int i, j;

    lbxNegOpt.numPixmapCompMethods = count;

    if (count > 0)
    {
	lbxNegOpt.pixmapCompMethods = (char *) xalloc (count);
	if (lbxNegOpt.pixmapCompMethods == NULL)
	{
	    lbxNegOpt.numPixmapCompMethods = 0;
	    return -1;
	}
    }

    for (i = 0; i < count; i++)
    {
	int index = *preply++;
	lbxNegOpt.pixmapCompMethods[i] = index;
	LbxPixmapCompMethods[index].methodOpCode = *preply++;
	LbxPixmapCompMethods[index].formatMask = *preply++;
	LbxPixmapCompMethods[index].depthCount = *preply++;
	for (j = 0; j < LbxPixmapCompMethods[index].depthCount; j++)
	    LbxPixmapCompMethods[index].depths[j] = *preply++;
    }

    return 0;
}


LbxBitmapCompMethod *
LbxLookupBitmapCompMethod (methodOpCode)

int methodOpCode;

{
    int i;

    for (i = 0; i < lbxNegOpt.numBitmapCompMethods; i++)
    {
	LbxBitmapCompMethod *method;

	method = &LbxBitmapCompMethods[lbxNegOpt.bitmapCompMethods[i]];

	if (method->methodOpCode == methodOpCode)
	    return (method);
    }

    return (NULL);
}


LbxPixmapCompMethod *
LbxLookupPixmapCompMethod (methodOpCode)

int methodOpCode;

{
    int i;

    for (i = 0; i < lbxNegOpt.numPixmapCompMethods; i++)
    {
	LbxPixmapCompMethod *method;

	method = &LbxPixmapCompMethods[lbxNegOpt.pixmapCompMethods[i]];

	if (method->methodOpCode == methodOpCode)
	    return (method);
    }

    return (NULL);
}


LbxBitmapCompMethod *
LbxFindPreferredBitmapCompMethod ()

{
    if (lbxNegOpt.numBitmapCompMethods == 0)
	return NULL;
    else
	return (&LbxBitmapCompMethods[lbxNegOpt.bitmapCompMethods[0]]);
}


LbxPixmapCompMethod *
LbxFindPreferredPixmapCompMethod (format, depth)

int format;
int depth;

{
    if (lbxNegOpt.numPixmapCompMethods == 0)
	return NULL;
    else
    {
	LbxPixmapCompMethod *method;
	int i, j;

	for (i = 0; i < lbxNegOpt.numPixmapCompMethods; i++)
	{
	    method = &LbxPixmapCompMethods[lbxNegOpt.pixmapCompMethods[i]];

	    if ((method->formatMask & (1 << format)))
	    {
		for (j = 0; j < method->depthCount; j++)
		    if (depth == method->depths[j])
			return method;
	    }
	}

	return NULL;
    }
}

typedef struct _LbxCmapAllMethod {
    char *methodName;
    void (*resolver)(
#if NeedFunctionPrototypes
	LbxVisualPtr /* pVisual */,
	CARD16* /* red */,
	CARD16* /* green */,
	CARD16* /* blue */
#endif
    );
    Pixel (*findfree)(
#if NeedFunctionPrototypes
	ColormapPtr /* pmap */,
	CARD32 	/* red */,
	CARD32	/* green */,
	CARD32	/* blue */
#endif
    );
    Entry * (* findbest)(
#if NeedFunctionPrototypes
	ColormapPtr /* pmap */,
	CARD32 	/* red */,
	CARD32	/* green */,
	CARD32	/* blue */,
	int	/* channels */
#endif
	);

} LbxCmapAllMethod;

LbxCmapAllMethod
LbxCmapAllMethods [] = {
  {
    "XC-CMAP",			/* colormap allocation method name */
    ResolveColor,		/* rgb resolver */
    FindFreePixel,		/* allocator */
    FindBestPixel,		/* matcher */
  }
};

#define NUM_CMAP_METHODS \
	(sizeof (LbxCmapAllMethods) / sizeof (LbxCmapAllMethod))

static int
LbxCmapAllReq (buf)

unsigned char *buf;

{
    unsigned char *bufStart = buf;
    int i;

    *buf++ = NUM_CMAP_METHODS;

    for (i = 0; i < NUM_CMAP_METHODS; i++)
    {
	int len = strlen (LbxCmapAllMethods[i].methodName);
	*buf++ = len;
	memcpy (buf, LbxCmapAllMethods[i].methodName, len);
	buf += len;
    }

    return (buf - bufStart);
}

static int
LbxCmapAllReply(preply, replylen)
    unsigned char *preply;
    int		  replylen;
{
    if (replylen < 1)
	return -1;
    if (*preply < NUM_CMAP_METHODS) {
	LbxResolveColor = LbxCmapAllMethods[*preply].resolver;
	LbxFindFreePixel = LbxCmapAllMethods[*preply].findfree;
	LbxFindBestPixel = LbxCmapAllMethods[*preply].findbest;
    }
    return 0;
}
