#ifdef _plan9_
#include <u.h>
#include <libc.h>
#include <stdio.h>
#else
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fconfig.h>
#include <config.h>
#include <assert.h>
#include <stdio.h>
#ifndef NO_MALLOC_H
#include <malloc.h>
#endif
#include <limits.h>
#endif
#include <fconfig.h>
#include <filter.h>
#include <misc-f.h>
#include <xerror.h>

#define nprecells (context->type & (LARGEITER|SMALLITER|TRUECOLOR|TRUECOLOR16|TRUECOLOR24)?0:(context)->ncells)
#define PREALLOCATED(palette) ((palette)->type & (LARGEITER|SMALLITER|TRUECOLOR|TRUECOLOR16|TRUECOLOR24)?0:(palette)->npreallocated)
/*emulate allocation routines using setpalette */
unsigned col_diff[3][512];
static struct palette *context;
static int maxentries;
static int needupdate;
static void
genertruecolorinfo(struct truec *t, unsigned int r, unsigned int g,
		   unsigned int b)
{
    int n;

    for (n = 0; !((r >> n) & 1); n++);
    t->rshift = n;
    for (; ((r >> n) & 1); n++);
    t->rprec = 8 - (n - t->rshift);
    t->rmask = r;

    for (n = 0; !((g >> n) & 1); n++);
    t->gshift = n;
    for (; ((g >> n) & 1); n++);
    t->gprec = 8 - (n - t->gshift);
    t->gmask = g;

    for (n = 0; !((b >> n) & 1); n++);
    t->bshift = n;
    for (; ((b >> n) & 1); n++);
    t->bprec = 8 - (n - t->bshift);
    t->bmask = b;

    t->allmask = r | g | b;
    if ((r & b) || (r & g) || (b & g)) {
	x_fatalerror("Internal error:Invalid color masks 1 %x %x %x!\n", r,
		     g, b);
    }
    if ((r < g && g < b) || (b < g && g < r)) {
	t->mask1 = r | b;
	t->mask2 = g;
    } else if ((g < r && r < b) || (b < r && r < g)) {
	t->mask1 = g | b;
	t->mask2 = r;
    } else if ((g < b && b < r) || (r < b && b < g)) {
	t->mask1 = g | r;
	t->mask2 = b;
    }
    t->byteexact = 0;
    t->missingbyte = -1;
    if (!(t->rshift % 8) &&
	!(t->gshift % 8) &&
	!(t->bshift % 8) && !t->rprec && !t->gprec && !t->bprec) {
	t->byteexact = 1;
	{
	    unsigned char ch[4];
	    *(unsigned int *) ch = t->allmask;
	    if (!ch[0])
		t->missingbyte = 0;
	    if (!ch[1])
		t->missingbyte = 1;
	    if (!ch[2])
		t->missingbyte = 2;
	    if (!ch[3])
		t->missingbyte = 3;
	}
    }
#ifdef DEBUG
    printf("Image:\n");
    printf("rshift:%i gshift:%i bshift:%i\n", t->rshift, t->gshift,
	   t->bshift);
    printf("rprec:%i gprec:%i bprec:%i\n", t->rprec, t->gprec, t->bprec);
    printf("rmask:%x gmask:%x bmask:%x\n", t->rmask, t->gmask, t->bmask);
    printf("mask1:%x mask2:%x allmask:%x\n", t->mask1, t->mask2,
	   t->allmask);
    printf("byteexact:%x missingbyte:%i\n", t->byteexact, t->missingbyte);
#endif
}

void bestfit_init(void)
{
    int i;

    for (i = 1; i < 256; i++) {
	int k = i * i;
	col_diff[0][i] = col_diff[0][512 - i] = k * (59 * 59) / 256;
	col_diff[1][i] = col_diff[1][512 - i] = k * (30 * 30) / 256;
	col_diff[2][i] = col_diff[2][512 - i] = k * (11 * 11) / 256;
    }
}

static int
allocgenerictruecolor(struct palette *palette, int init, int r, int g,
		      int b)
{
    unsigned int n;
    switch (palette->type) {
    case LARGEITER:
    case SMALLITER:
#ifdef _UNDEFINED_
	if (init)
	    n = 0;
	else
	    n = palette->size;
#endif
	return 1;
    case TRUECOLOR:
    case TRUECOLOR16:
    case TRUECOLOR24:
    default:
	n = ((r >> palette->info.truec.rprec) << palette->info.truec.
	     rshift) | ((g >> palette->info.truec.gprec) << palette->info.
			truec.gshift) | ((b >> palette->info.truec.
					  bprec) << palette->info.truec.
					 bshift);
	break;
    }
    if (init)
	palette->size = 0;
    else if (palette->size >= palette->maxentries)
	return -1;
    palette->pixels[palette->size] = n;
    palette->size++;
    return palette->size;
}

static int
allocgeneric(struct palette *palette, int init, int r, int g, int b)
{
    int start = palette->npreallocated + palette->start;
    if (init)
	palette->size = 0;
    else if (palette->size >= palette->end - start)
	return -1;
    palette->pixels[palette->size] = palette->size + start;
    palette->rgb[palette->size + start][0] = r;
    palette->rgb[palette->size + start][1] = g;
    palette->rgb[palette->size + start][2] = b;
    palette->size++;
    return (palette->size - 1);
}

int fixedalloccolor(struct palette *palette, int init, int r, int g, int b)
{
    int i, coldif, lowest, bestfit;
    if (init)
	palette->size = 0;
    else if (palette->size >= palette->maxentries)
	return -1;
    lowest = INT_MAX;
    bestfit = 1;
    if (palette->type == FIXEDCOLOR || (palette->type & BITMAPS)) {
	for (i = palette->start; i < palette->end; i++) {
	    coldif = col_diff[0][(g - palette->rgb[i][1]) & 0x1ff];
	    if (coldif < lowest) {
		coldif += col_diff[1][(r - palette->rgb[i][0]) & 0x1ff];
		if (coldif < lowest) {
		    coldif +=
			col_diff[2][(b - palette->rgb[i][2]) & 0x1ff];
		    if (coldif < lowest) {
			bestfit = i;
			if (!coldif)
			    break;
			lowest = coldif;
		    }
		}
	    }
	}
    } else {
	bestfit =
	    (r * 30 + g * 59 + b * 11) * (palette->end -
					  palette->start) / 256 / 100 +
	    palette->start;
    }
    palette->pixels[palette->size] = bestfit;
    palette->size++;
    return (palette->size - 1);
}

static void
setcolorgeneric(struct palette *palette, int start, int end, rgb_t * rgb)
{
}

static void allocfinishedgeneric(struct palette *palette)
{
    palette->setpalette(palette, palette->start,
			palette->size + palette->start +
			palette->npreallocated,
			palette->rgb + palette->start);
}

static void cycle_entries(struct palette *c, int direction)
{
    int i;
    int i1, i2, i3;
    rgb_t *co;
    if (direction > 0)
	direction %= c->size - 1;
    else
	direction = -((-direction) % (c->size - 1));
    if (!direction)
	return;
    co = (rgb_t *) malloc(c->end * sizeof(rgb_t));
    memcpy(co, c->rgb, sizeof(*co) * c->end);
    i3 = (c->size - 1 + direction) % (c->size - 1) + 1;
    for (i = 1; i < c->size; i++) {
	i1 = c->pixels[i];
	i2 = c->pixels[i3];
	c->rgb[i1][0] = co[i2][0];
	c->rgb[i1][1] = co[i2][1];
	c->rgb[i1][2] = co[i2][2];
	i3++;
	if (i3 >= c->size)
	    i3 = 1;
    }
    free(co);
}

static void cyclecolorsgeneric(struct palette *pal, int direction)
{
    cycle_entries(pal, direction);
    pal->setpalette(pal, pal->pixels[0], pal->size + pal->pixels[0],
		    pal->rgb + pal->pixels[0]);
}

#define TRUECOLORPALETTE 65536
struct palette *createpalette(int start, int end, int type, int flags,
			      int maxentries,
			      int (*alloccolor) (struct palette * pal,
						 int init, int r, int g,
						 int b),
			      void (*setcolor) (struct palette * pal,
						int start, int end,
						rgb_t * rgb),
			      void (*allocfinished) (struct palette * pal),
			      void (*cyclecolors) (struct palette * pal,
						   int direction),
			      union paletteinfo *info)
{
    static int versioncount;
    struct palette *palette =
	(struct palette *) calloc(1, sizeof(struct palette));

    if (col_diff[0][1] == 0)
	bestfit_init();
    palette->ncells = 0;
    palette->index = NULL;
    palette->size = 0;
    palette->rgb = NULL;
    if (palette == NULL)
	return NULL;

    switch (type) {
    case LBITMAP:
    case MBITMAP:
    case LIBITMAP:
    case MIBITMAP:
	end = 2;
	start = 0;
	maxentries = 256;
	palette->rgb = (rgb_t *) calloc(end, sizeof(*palette->rgb));
	if (palette->rgb == NULL) {
	    free(palette);
	    return NULL;
	}
	if (type & (LIBITMAP | MIBITMAP)) {
	    palette->rgb[0][0] = 255;
	    palette->rgb[0][1] = 255;
	    palette->rgb[0][2] = 255;
	    palette->rgb[1][0] = 0;
	    palette->rgb[1][1] = 0;
	    palette->rgb[1][2] = 0;
	} else {
	    palette->rgb[0][0] = 0;
	    palette->rgb[0][1] = 0;
	    palette->rgb[0][2] = 0;
	    palette->rgb[1][0] = 255;
	    palette->rgb[1][1] = 255;
	    palette->rgb[1][2] = 255;
	}
	palette->maxentries = maxentries;
	palette->alloccolor = fixedalloccolor;
	palette->setpalette = NULL;
	palette->allocfinished = NULL;
	palette->cyclecolors = NULL;
	break;
    case FIXEDCOLOR:
	if (!end)
	    end = 256;
	if (!maxentries)
	    maxentries = 256;
	palette->rgb = (rgb_t *) calloc(end, sizeof(*palette->rgb));
	if (palette->rgb == NULL) {
	    free(palette);
	    return NULL;
	}
	palette->maxentries = maxentries;
	palette->alloccolor = fixedalloccolor;
	palette->setpalette = NULL;
	palette->allocfinished = NULL;
	palette->cyclecolors = NULL;
	break;
    case GRAYSCALE:
	if (!end)
	    end = 256;
	if (!maxentries)
	    maxentries = end - start;
	palette->maxentries = 65536;
	palette->alloccolor = fixedalloccolor;
	palette->setpalette = NULL;
	palette->allocfinished = NULL;
	palette->cyclecolors = NULL;
	palette->size = end - start;
	break;
    case C256:

	if (!end)
	    end = 256;
	if (!maxentries)
	    maxentries = end - start;
	if (cyclecolors == NULL && setcolor != NULL)
	    cyclecolors = cyclecolorsgeneric;

	if (alloccolor == NULL) {
	    alloccolor = allocgeneric, allocfinished =
		allocfinishedgeneric;
	    if (setcolor == NULL && type == C256)	/*non hardware palette */
		setcolor = setcolorgeneric, cyclecolors =
		    cyclecolorsgeneric;
	}
	palette->rgb = (rgb_t *) calloc(end, sizeof(*palette->rgb));

	if (palette->rgb == NULL) {
	    free(palette);
	    return NULL;
	}

	palette->maxentries = maxentries;
	palette->alloccolor = alloccolor;
	palette->setpalette = setcolor;
	palette->allocfinished = allocfinished;
	palette->cyclecolors = cyclecolors;

	break;
    default:
	end = TRUECOLORPALETTE;
	start = 0;
	if (type == SMALLITER)
	    end = 256;
	start = 0;
	palette->maxentries = end;
	palette->alloccolor = allocgenerictruecolor;
	palette->cyclecolors = NULL;
	palette->setpalette = NULL;
	palette->allocfinished = NULL;
    }
    {
	int ee = palette->maxentries;
	/*if(end>palette->maxentries) ee=end; */
	if (ee < 256)
	    palette->pixels =
		(unsigned int *) calloc(256, sizeof(*palette->pixels));
	else
	    palette->pixels =
		(unsigned int *) calloc(ee, sizeof(*palette->pixels));
    }
    if (palette->pixels == NULL) {
	free(palette);
	return NULL;
    }
    if (type & (LARGEITER | SMALLITER)) {
	int i;
	palette->size = end;
	palette->flags |= DONOTCHANGE;
	for (i = 0; i < end; i++)
	    palette->pixels[i] = i;
    }
    palette->start = start;
    palette->end = end;
    palette->type = type;
    palette->flags |= flags;
    palette->version = (versioncount += 65536);
    if (type == FIXEDCOLOR) {
	int i;
	if (setcolor != NULL)
	    setcolor(palette, start, end, palette->rgb);
	for (i = 0; i < end - start; i++)
	    palette->pixels[i] = i + start;
    }
    if (type == GRAYSCALE) {
	int i;
	for (i = palette->start; i < end - start; i++)
	    palette->pixels[i] = i + start;
    }
    if (info != NULL
	&& (type == TRUECOLOR || type == TRUECOLOR24
	    || type == TRUECOLOR16)) {
	genertruecolorinfo(&palette->info.truec, info->truec.rmask,
			   info->truec.gmask, info->truec.bmask);
    } else {
	if (type == TRUECOLOR)
	    genertruecolorinfo(&palette->info.truec, 255 << 16, 255 << 8,
			       255);
	if (type == TRUECOLOR24)
	    genertruecolorinfo(&palette->info.truec, 255 << 16, 255 << 8,
			       255);
	if (type == TRUECOLOR16)
	    genertruecolorinfo(&palette->info.truec, (255 >> 3) << 11,
			       (255 >> 2) << 5, (255 >> 3));
    }
    return (palette);
}

void destroypalette(struct palette *palette)
{
    free(palette->pixels);
    if (palette->rgb != NULL)
	free(palette->rgb);
    if (palette->index != NULL)
	free(palette->index);
    free(palette);
}

#define MYMIN(x,y) ((x)<(y)?(x):(y))
struct palette *clonepalette(struct palette *palette)
{
    struct palette *pal =
	createpalette(palette->start, palette->end, palette->type,
		      palette->flags, palette->maxentries,
		      /*palette->alloccolor, palette->setpalette, palette->allocfinished, palette->cyclecolors */
		      NULL, NULL, NULL, NULL, &palette->info);
    memcpy(pal->pixels, palette->pixels,
	   sizeof(*pal->pixels) * MYMIN(palette->end, pal->end));
    if (pal->rgb != NULL) {
	memcpy(pal->rgb, palette->rgb,
	       sizeof(*pal->rgb) * MYMIN(palette->end, pal->end));
    }
    pal->size = palette->size;
    return (pal);
}

void preallocpalette(struct palette *pal)
{
    int i;
    int p;
    if (pal->index != NULL)
	free(pal->index), pal->index = NULL;
    pal->npreallocated = 0;
    if (!pal->ncells)
	return;
    pal->index = (unsigned int *) malloc(sizeof(int) * (pal->ncells + 1));
    for (i = 0; i < pal->ncells; i++) {
	if (!i)
	    p = pal->pixels[0];
	else
	    p = pal->pixels[pal->size];
	pal->alloccolor(pal, i == 0, pal->prergb[i][0], pal->prergb[i][1],
			pal->prergb[i][2]);
	if (pal->size) {
	    pal->index[i] = pal->pixels[pal->size - 1];
	    pal->pixels[pal->size - 1] = p;
	}
    }
    pal->npreallocated = pal->size;
    pal->size = 0;
    needupdate = 0;
}

void restorepalette(struct palette *dest, struct palette *src)
{
    int i;
    preallocpalette(dest);
    for (i = 0; i < src->size; i++) {
	int r = 0, g = 0, b = 0;
	switch (src->type) {
	case SMALLITER:
	    r = g = b = i;
	    break;
	case LARGEITER:
	    r = g = b = i / 256;
	    break;
	case GRAYSCALE:
	    r = g = b = src->pixels[i];
	    break;
	case C256:
	case FIXEDCOLOR:
	case MBITMAP:
	case LBITMAP:
	case MIBITMAP:
	case LIBITMAP:
	    r = src->rgb[src->pixels[i]][0];
	    g = src->rgb[src->pixels[i]][1];
	    b = src->rgb[src->pixels[i]][2];
	    break;
	case TRUECOLOR:
	case TRUECOLOR16:
	case TRUECOLOR24:
	    r = (((src->pixels[i] & src->info.truec.rmask) >> src->
		  info.truec.rshift)) << src->info.truec.rprec;
	    g = (((src->pixels[i] & src->info.truec.gmask) >> src->
		  info.truec.gshift)) << src->info.truec.gprec;
	    b = (((src->pixels[i] & src->info.truec.bmask) >> src->
		  info.truec.bshift)) << src->info.truec.bprec;
	    break;
	}
	if (dest->size >= dest->maxentries - PREALLOCATED(dest))
	    break;
	if (dest->
	    alloccolor(dest, (i + dest->npreallocated) == 0, r, g,
		       b) == -1)
	    break;
    }
    if (!(dest->flags & FINISHLATER)) {
	if (dest->allocfinished != NULL)
	    dest->allocfinished(dest);
    } else
	dest->flags |= UNFINISHED;
    dest->version++;
}

/*Generation code for various palettes */


#define DEFNSEGMENTS (255/8)
#define MAXNSEGMENTS (4096)
#define NSEGMENTS ((255/segmentsize))
static int segmentsize;


static unsigned char colors[MAXNSEGMENTS][3];
static const unsigned char colors1[DEFNSEGMENTS][3] = {
    /*{8, 14, 32}, */
    {0, 0, 0},
    {120, 119, 238},
    {24, 7, 25},
    {197, 66, 28},
    {29, 18, 11},
    {135, 46, 71},
    {24, 27, 13},
    {241, 230, 128},
    {17, 31, 24},
    {240, 162, 139},
    {11, 4, 30},
    {106, 87, 189},
    {29, 21, 14},
    {12, 140, 118},
    {10, 6, 29},
    {50, 144, 77},
    {22, 0, 24},
    {148, 188, 243},
    {4, 32, 7},
    {231, 146, 14},
    {10, 13, 20},
    {184, 147, 68},
    {13, 28, 3},
    {169, 248, 152},
    {4, 0, 34},
    {62, 83, 48},
    {7, 21, 22},
    {152, 97, 184},
    {8, 3, 12},
    {247, 92, 235},
    {31, 32, 16}
};

REGISTERS(3)
static int allocate(int r, int g, int b, int init)
{
    unsigned int n;
    if (init)
	preallocpalette(context);
    n = context->pixels[(init ? 0 : context->size) /*+ context->start */ ];
    if (!init && context->size == maxentries)
	return 0;
    if ((context->alloccolor(context, init
			     && !context->npreallocated, (int) r, (int) g,
			     (int) b)) == -1) {
	return 0;
    }
    if (context->pixels[context->size - 1 /*+ context->start */ ] != n) {
	needupdate = 1;
    }
    return (1);
}

static int mksmooth(int nsegments, int setsegments)
{
    int i, y;
    float r, g, b, rs, gs, bs;
    int segmentsize1 = segmentsize;

    for (i = 0; i < setsegments; i++) {
	if (i == setsegments - 1 && !(context->flags & UNKNOWNENTRIES)) {
	    segmentsize1 = maxentries - context->size - 2;
	}
	r = colors[i % nsegments][0];
	g = colors[i % nsegments][1];
	b = colors[i % nsegments][2];
	rs = ((int) colors[(i + 1) % setsegments % nsegments][0] -
	      r) / (unsigned int) segmentsize1;
	gs = ((int) colors[(i + 1) % setsegments % nsegments][1] -
	      g) / (unsigned int) segmentsize1;
	bs = ((int) colors[(i + 1) % setsegments % nsegments][2] -
	      b) / (unsigned int) segmentsize1;
	for (y = 0; y < segmentsize1; y++) {
	    if (!allocate((int) r, (int) g, (int) b, i == 0 && y == 0)) {
		if (!i)
		    context->size = 2;
		context->size = i * segmentsize;
		return 0;
	    }
	    r += rs;
	    g += gs;
	    b += bs;
	}
    }
    if (context->flags & UNKNOWNENTRIES)
	context->size = i * segmentsize;
    return 1;
}

static INLINE void
hsv_to_rgb(int h, int s, int v,
	   unsigned char *red, unsigned char *green, unsigned char *blue)
{
    int hue;
    int f, p, q, t;
    h += 256;
    h %= 256;

    if (s == 0) {
	*red = v;
	*green = v;
	*blue = v;
    } else {
	h %= 256;
	if (h < 0)
	    h += 256;
	hue = h * 6;

	f = hue & 255;
	p = v * (256 - s) / 256;
	q = v * (256 - (s * f) / 256) >> 8;
	t = v * (256 * 256 - (s * (256 - f))) >> 16;

	switch ((int) (hue / 256)) {
	case 0:
	    *red = v;
	    *green = t;
	    *blue = p;
	    break;
	case 1:
	    *red = q;
	    *green = v;
	    *blue = p;
	    break;
	case 2:
	    *red = p;
	    *green = v;
	    *blue = t;
	    break;
	case 3:
	    *red = p;
	    *green = q;
	    *blue = v;
	    break;
	case 4:
	    *red = t;
	    *green = p;
	    *blue = v;
	    break;
	case 5:
	    *red = v;
	    *green = p;
	    *blue = q;
	    break;
	}
    }
}

static void randomize_segments3(int whitemode, int nsegments)
{
    int i = 0;
    int h, s, v;

    for (i = 0; i < nsegments; i++) {
	if (!(i % 3)) {
	    if (i % 6)
		colors[i][0] = 255, colors[i][1] = 255, colors[i][2] = 255;
	    else
		colors[i][0] = 0, colors[i][1] = 0, colors[i][2] = 0;
	} else {
	    s = (int) XaoS_random() % 256;
	    h = (int) XaoS_random() % (128 - 32);
	    v = (int) XaoS_random() % 128;
	    if (((i) % 6 > 3) ^ ((i) % 3 == 1))
		/*if(((i)%3==1)) */
		h += 42 + 16;
	    else
		h += 42 + 128 + 16, v += 128 + 64;
	    hsv_to_rgb(h, s, v, colors[i], colors[i] + 1, colors[i] + 2);
	}
    }
    colors[i - 1][0] = colors[0][0];
    colors[i - 1][1] = colors[0][1];
    colors[i - 1][2] = colors[0][2];
}

static void randomize_segments2(int whitemode, int nsegments)
{
    int i = 0;

    for (i = 0; i < nsegments; i++) {
	if (i % 3 == 2)
	    colors[i][0] = whitemode * 255,
		colors[i][1] = whitemode * 255, colors[i][2] =
		whitemode * 255;
	else if (i % 3 == 0)
	    colors[i][0] = (!whitemode) * 255,
		colors[i][1] = (!whitemode) * 255,
		colors[i][2] = (!whitemode) * 255;
	else
	    colors[i][0] = (int) XaoS_random() % 256,
		colors[i][1] = (int) XaoS_random() % 256,
		colors[i][2] = (int) XaoS_random() % 256;
    }
    colors[i - 1][0] = colors[0][0];
    colors[i - 1][1] = colors[0][1];
    colors[i - 1][2] = colors[0][2];
}

static void randomize_segments(int whitemode, int nsegments)
{
    int i = 0;
    if (whitemode) {
	colors[0][0] = 255, colors[0][1] = 255, colors[0][2] = 255;
	for (i = 0; i < nsegments; i += 2) {
	    if (i != 0) {
		colors[i][0] = (int) XaoS_random() % 256,
		    colors[i][1] = (int) XaoS_random() % 256,
		    colors[i][2] = (int) XaoS_random() % 256;
	    }
	    if (i + 1 < nsegments)
		colors[i + 1][0] = (int) XaoS_random() % 35,
		    colors[i + 1][1] = (int) XaoS_random() % 35,
		    colors[i + 1][2] = (int) XaoS_random() % 35;
	}
    } else {
	for (i = 0; i < nsegments; i += 2) {
	    colors[i][0] = (int) XaoS_random() % 35,
		colors[i][1] = (int) XaoS_random() % 35,
		colors[i][2] = (int) XaoS_random() % 35;
	    if (i + 1 < nsegments)
		colors[i + 1][0] = (int) XaoS_random() % 256,
		    colors[i + 1][1] = (int) XaoS_random() % 256,
		    colors[i + 1][2] = (int) XaoS_random() % 256;
	}
    }
    colors[i - 1][0] = colors[0][0];
    colors[i - 1][1] = colors[0][1];
    colors[i - 1][2] = colors[0][2];
}

#define MYLONG_MAX 0xffffff
#define rrandom(i) ((int)(((int)XaoS_random()/(double)MYLONG_MAX)*(i)))
/*Do not use modulo type random since it should bring very different results
 *for slightly different sizes
 */

int mkpalette(struct palette *c, int seed, int algorithm)
{
    int i, ncolors = c->size;
    int whitemode;
    int i1;

    context = c;
    needupdate = 0;

    if (c->flags & DONOTCHANGE)
	return 0;
    XaoS_srandom(seed);
    seed = (int) XaoS_random();
    whitemode = (int) XaoS_random() % 2;

    if ((c->flags & UNKNOWNENTRIES) || !c->size) {
	maxentries = context->maxentries - nprecells;
	segmentsize = (rrandom(maxentries / 2)) & (~3);
	if (segmentsize < 1)
	    segmentsize = 1;
    } else {
	if (maxentries > 8) {
	    int qq = 255;

	    maxentries = context->maxentries - nprecells;
	    segmentsize = rrandom(qq / 3 + 4);
	    segmentsize += rrandom(qq / 3 + 4);
	    segmentsize += rrandom(qq / 3 + 4);
	    segmentsize += rrandom(qq / 3 + 4);	/*Make smaller segments with higher probability */

	    segmentsize = abs(segmentsize / 2 - qq / 3 + 3);
	    if (segmentsize < 8)
		segmentsize = 8;
	    if (segmentsize > maxentries / 3)
		segmentsize = maxentries / 3;
	}
    }

    if (c->flags & UNKNOWNENTRIES)
	i = rrandom(maxentries);
    else
	i = (maxentries + segmentsize - 5) / segmentsize;

    if (i < 0)
	i = 1;
    if (i > MAXNSEGMENTS)
	i1 = MAXNSEGMENTS;
    else
	i1 = i;

    XaoS_srandom(seed);

    switch (algorithm) {
    case 2:
	randomize_segments3(whitemode, i1);
	break;
    case 1:
	randomize_segments2(whitemode, i1);
	break;
    case 0:
	randomize_segments(whitemode, i1);
    }
    mksmooth(i1, i);

    if (!(c->flags & FINISHLATER)) {
	if (c->allocfinished != NULL)
	    c->allocfinished(c);
    } else
	c->flags |= UNFINISHED;
    if (context->size != ncolors || needupdate) {
	context->version++;
	return 1;
    }

    return 0;
}

int mkstereogrampalette(struct palette *c)
{
    int i, ncolors = c->size;
    context = c;
    needupdate = 0;
    for (i = 0; i < 16; i++)
	allocate(i * 4, i * 4, i * 16, i == 0);
    if (!(c->flags & FINISHLATER)) {
	if (c->allocfinished != NULL)
	    c->allocfinished(c);
    } else
	c->flags |= UNFINISHED;
    if (context->size != ncolors || needupdate) {
	context->version++;
	return 1;
    }
    return 0;
}

int mkstarfieldpalette(struct palette *c)
{
    int i, ncolors = c->size;
    context = c;
    needupdate = 0;
    for (i = 0; i < 16; i++)
	if (i % 2)
	    allocate(i * 4, i * 4, i * 16, i == 0);
	else
	    allocate(i * 16, i * 16, i * 16, i == 0);
    if (!(c->flags & FINISHLATER)) {
	if (c->allocfinished != NULL)
	    c->allocfinished(c);
    } else
	c->flags |= UNFINISHED;
    if (context->size != ncolors || needupdate) {
	context->version++;
	return 1;
    }
    return 0;
}

int mkblurpalette(struct palette *c)
{
    int i, ncolors = c->size;
    context = c;
    needupdate = 0;
    for (i = 0; i < 63; i++)
	allocate(i * 2, i * 2, i * 4, i == 0);
    allocate(i * 2, i * 2, i * 4 - 1, 0);
    if (!(c->flags & FINISHLATER)) {
	if (c->allocfinished != NULL)
	    c->allocfinished(c);
    } else
	c->flags |= UNFINISHED;
    if (context->size != ncolors || needupdate) {
	context->version++;
	return 1;
    }
    return 0;
}

int mkgraypalette(struct palette *c)
{
    int i, ncolors = c->size;
    context = c;
    needupdate = 0;
    for (i = 0; i < 64; i++)
	allocate(i * 4, i * 4, i * 4, i == 0);
    for (i = 0; i < 16; i++)
	allocate(255, 255 - i * 16, 255 - i * 16, 0);
    for (i = 0; i < 16; i++)
	allocate(255 - i * 16, 0, 0, 0);
    if (!(c->flags & FINISHLATER)) {
	if (c->allocfinished != NULL)
	    c->allocfinished(c);
    } else
	c->flags |= UNFINISHED;
    if (context->size != ncolors || needupdate) {
	context->version++;
	return 1;
    }
    return 0;
}

int mkdefaultpalette(struct palette *c)
{
    int i, ncolors = c->size;
    context = c;
    needupdate = 0;
    segmentsize = 8;

    if (c->flags & DONOTCHANGE)
	return 0;
    memcpy(colors, colors1, sizeof(colors1));
    maxentries = context->maxentries - nprecells;
    if (c->flags & UNKNOWNENTRIES)
	i = 128 / 8;
    else
	i = (maxentries + 3) / 8;
    if (i < 0)
	i = 1;
    mksmooth(255 / 8, i);
    if (!(c->flags & FINISHLATER)) {
	if (c->allocfinished != NULL)
	    c->allocfinished(c);
    } else
	c->flags |= UNFINISHED;
    if (context->size != ncolors || needupdate) {
	context->version++;
	return 1;
    }
    return 0;
}

int shiftpalette(struct palette *c, int shift)
{
    if (!c->size)
	return 0;

    while (shift < 0)
	shift += c->size - 1;
    shift = shift % (c->size - 1);

    if (!shift)
	return 0;

    if (c->cyclecolors != NULL) {
	if (c->flags & UNFINISHED) {
	    cycle_entries(c, shift);
	} else {
	    c->cyclecolors(c, shift);
	}
	return 0;
    }

    if (c->type & (TRUECOLOR | TRUECOLOR24 | TRUECOLOR16)) {
	int i;
	int i3;
	int *co;

	if (shift > 0)
	    shift %= c->size - 1;
	else
	    shift = -((-shift) % (c->size - 1));
	if (!shift)
	    return 0;
	co = (int *) malloc(c->size * sizeof(*co));
	memcpy(co, c->pixels, sizeof(*co) * c->size);
	i3 = (c->size - 1 + shift) % (c->size - 1) + 1;
	for (i = 1; i < c->size; i++) {
	    c->pixels[i] = co[i3];
	    i3++;
	    if (i3 >= c->size)
		i3 = 1;
	}
	c->version++;
	free(co);
    }
    return 1;
}

static int allocrgb(struct palette *c, int r1, int g1, int b1)
{
    int r, g, b;
    int f = 1;
    for (g = 0; g < g1; g++)
	for (b = 0; b < b1; b++) {
	    for (r = 0; r < r1; r++) {
		if (!allocate
		    (r * 255 / (r1 - 1), g * 255 / (g1 - 1),
		     b * 255 / (b1 - 1), f))
		    return 0;
		f = 0;
	    }
	}
    return 1;
}

int mkrgb(struct palette *c)
{
    int ncolors = c->size;
    int red = 8, green = 8, blue = 4;

    context = c;
    needupdate = 0;

    if (c->flags & UNKNOWNENTRIES) {
	while (blue > 0) {
	    if (allocrgb(c, red, green, blue))
		break;
	    red--;
	    if (allocrgb(c, red, green, blue))
		break;
	    green--;
	    if (allocrgb(c, red, green, blue))
		break;
	    red--;
	    if (allocrgb(c, red, green, blue))
		break;
	    green--;
	    if (allocrgb(c, red, green, blue))
		break;
	    blue--;
	}
    } else {
	number_t n =
	    pow((c->maxentries - nprecells) / (0.5 * 0.2 * 0.3), 1.0 / 3);
	green = (int) (n * 0.5);
	blue = (int) (n * 0.2);
	red = (int) (n * 0.3);
	while ((blue + 1) * red * green < (c->maxentries - nprecells))
	    blue++;
	while ((red + 1) * blue * green < (c->maxentries - nprecells))
	    red++;
	while ((green + 1) * blue * red < (c->maxentries - nprecells))
	    green++;
	allocrgb(c, red, green, blue);
    }

    if (!(c->flags & FINISHLATER)) {
	if (c->allocfinished != NULL)
	    c->allocfinished(c);
    } else
	c->flags |= UNFINISHED;
    if (context->size != ncolors || needupdate) {
	context->version++;
    }

    return red * 256 * 256 + green * 256 + blue;
}
