#include <string.h>
#include "alloc.h"
#include "dos.h"
#include "quant.h"
#include "octree.h"

#define TESTBIT(a,i) ( ((a) >> (i)) & 1)
#define MAXDEPTH 8
#define BYTE    unsigned char

UCHAR palette [MAXCOLORS][3];

static UINT size;
static UINT reducelevel;
static UINT leaflevel;
static OCTREE tree;
static BYTE rgb_mio[3];
static OCTREE reducelist[MAXDEPTH + 1];

static unsigned char quant_r,
                     quant_g,
                     quant_b;

    /* Quantiza seg£n TESTBIT, pero solamente de retorno */

static char quant2(OCTREE tree)
{
	if (tree->leaf)   return(tree->colorindex);
	else					return(quant2(tree->next[
								TESTBIT(quant_r, MAXDEPTH - tree->level) * 4 +
								TESTBIT(quant_g, MAXDEPTH - tree->level) * 2 +
								TESTBIT(quant_b, MAXDEPTH - tree->level)]));
}

    /* devuelve el indice a la paleta quantizada de acuerdo con RGB apuntado */

int pal_index(UCHAR *p)
{
	quant_r = p[RED];
	quant_g = p[GREEN];
	quant_b = p[BLUE];
	return quant2(tree);
}

static double init_Cfactor;
static UINT init_col_num;

static void initpalette(OCTREE tree)
{
	UINT j;

	if (tree == NULL) return;
	if (tree->leaf || tree->level == leaflevel) {
        palette[init_col_num][RED]   = (char) ((init_Cfactor * tree->rgbsum.r) / tree->colorcount + .5);
        palette[init_col_num][GREEN] = (char) ((init_Cfactor * tree->rgbsum.g) / tree->colorcount + .5);
        palette[init_col_num][BLUE]  = (char) ((init_Cfactor * tree->rgbsum.b) / tree->colorcount + .5);
		tree->colorindex = init_col_num;
		tree->leaf = TRUE;
		init_col_num++;
	} else {
		for (j = 0; j < 8; j++)
			initpalette(tree->next[j]);
	}
}

    /* calcula la paleta de acuerdo con un factor introducido */

UINT calc_palette(UINT i, double Cfactor)
{
	init_Cfactor = Cfactor;
	init_col_num = i;
	initpalette(tree);
	return init_col_num;
}


static void newandinit(OCTREE *tree, UINT depth)
    {
    unsigned long rest;
	*tree = (OCTREE)calloc(1,sizeof(struct node));
	if (*tree == NULL) {
	rest=coreleft();
	printf("No hay bastante Memoria");
        exit(1);
        }
    (*tree)->level = depth;
    (*tree)->leaf = (depth >= leaflevel);
    if ((*tree)->leaf)
        size++;
    }

static void getreduceable(OCTREE *node)
    {
    UINT newreducelevel;

    newreducelevel = reducelevel;
    while (reducelist[newreducelevel] == NULL)
        newreducelevel--;
    *node = reducelist[newreducelevel];
    reducelist[newreducelevel] =
                reducelist[newreducelevel]->nextreduceable;
    }

static void makereduceable(UINT level,OCTREE node)
{
	node->nextreduceable = reducelist[level];
	reducelist[level] = node;
}

    /* reduzcamos el arbol, pues K+1>size */

static void reducetree(void)
{
	OCTREE node;
	UINT depth;

	getreduceable(&node);
	node->leaf = 1;
	size = size - node->children + 1;
	depth = node->level;
	if (depth < reducelevel) {
		reducelevel = depth;
		leaflevel = reducelevel + 1;
	}
}

static UCHAR insert_rgb[3];

    /* para insertar cada color dentro del arbol */

static void inserttree(OCTREE *tree, UINT depth)
{
	UINT branch;

	if (*tree == NULL)
		newandinit(tree,depth);
	(*tree)->colorcount++;
	(*tree)->rgbsum.r += insert_rgb[RED];
	(*tree)->rgbsum.g += insert_rgb[GREEN];
	(*tree)->rgbsum.b += insert_rgb[BLUE];
	if ((*tree)->leaf == FALSE && depth < leaflevel) {
		branch = TESTBIT(insert_rgb[RED],MAXDEPTH - depth) * 4 +
					TESTBIT(insert_rgb[GREEN],MAXDEPTH - depth) * 2 +
					TESTBIT(insert_rgb[BLUE],MAXDEPTH - depth);
		if ((*tree)->next[branch] == NULL) {
			(*tree)->children++;
			if ((*tree)->children == 2)
				makereduceable(depth,*tree);
		}
		inserttree(&((*tree)->next[branch]), depth + 1);
	}
}


void lee(FILE *uno,double gamm)     /* funci¢n que leer  la 1¦ vez el TGA */
{
	union REGS regset;
	struct SREGS sregset;

	reducelevel = MAXDEPTH;
	leaflevel = reducelevel + 1;

	while (!feof(uno)) {
	fread(&rgb_mio,3,1,uno);
	insert_rgb[0]=rgb_mio[2];	// CUIDADO, valores de TGA tipo 2
	insert_rgb[1]=rgb_mio[1];	// Estan BGR no RGB
	insert_rgb[2]=rgb_mio[0];
		inserttree(&tree, 0);
	if (size > MAXCOLORS - 1)       /* > K+1? (colores) */
			reducetree();
	}
	calc_palette((unsigned int)0,gamm);
	regset.x.ax=0x1012;
	regset.x.bx=0;
	regset.x.cx=256;
	regset.x.dx=FP_OFF(palette);
	sregset.es=FP_SEG(palette);
    int86x(0x10,&regset,&regset,&sregset);  /* interrupci¢n que carga y */
			/* activa la paleta nueva */
}