#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "aalib.h"
#include "aaint.h"
#include "aamktabl.h"
#include "config.h"
#define TABLESIZE 65536

#define pow2(i) ((i)*(i))
/*#define pow2(i) (abs(i)) */
#define add(i) if(next[(i)]==(i)&&last!=(i)) {if(last!=-1) next[last]=(i),last=(i); else last=first=(i);}
#define dist(i1,i2,i3,i4,i5,y1,y2,y3,y4,y5) (2*(pow2((int)(i1)-(int)(y1))+pow2((int)(i2)-(int)(y2))+(pow2((int)(i3)-(int)(y3))+pow2((int)(i4)-(int)(y4))))+1*pow2((int)(i5)-(int)(y5)))
#define dist1(i1,i2,i3,i4,i5,y1,y2,y3,y4,y5) ((pow2((int)(i1)-(int)(y1))+pow2((int)(i2)-(int)(y2))+(pow2((int)(i3)-(int)(y3))+pow2((int)(i4)-(int)(y4))))+2*pow2((int)(i5)-(int)(y5)))
#define pos(i1,i2,i3,i4) (((int)(i1)<<12)+((int)(i2)<<8)+((int)(i3)<<4)+(int)(i4))

#define postoparams(pos,i1,i2,i3,i4) \
  ((i1)=(pos)>>12),((i2)=((pos)>>8)&15),((i3)=((pos)>>4)&15),((i4)=((pos))&15)

static __AA_CONST int priority[]={4,5,3,2,1};

unsigned short *aa_mktable(aa_context * c)
{
    int i;
    int i1, i2, i3, i4;
    int sum, pos;
    __AA_CONST struct aa_font *currfont = c->params.font;
    int supported = c->params.supported;
    unsigned short *next;
    int first = -1;
    int last = -1;
    unsigned short *table;
    unsigned short *filltable;
    static struct parameters *parameters;
    next = (unsigned short *) malloc(sizeof(*next) * TABLESIZE);
    parameters = (struct parameters *) calloc(1, sizeof(struct parameters) * (NCHARS + 1));
    table = (unsigned short *) calloc(1, TABLESIZE * sizeof(*table));
    filltable = (unsigned short *) calloc(1, 256 * sizeof(*filltable));
    first = -1;
    last = -1;
    for (i = 0; i < TABLESIZE; i++)
	next[i] = i, table[i] = 0;
    __aa_calcparams(currfont, parameters, supported,c->params.dimmul, c->params.boldmul);

    for (i = 0; i < NCHARS; i++) {
	if (ALOWED(i, supported)) {
	    int p1, p2, p3, p4;
	    i1 = parameters[i].p[0];
	    i2 = parameters[i].p[1];
	    i3 = parameters[i].p[2];
	    i4 = parameters[i].p[3];
	    p1 = i1 >> 4;
	    p2 = i2 >> 4;
	    p3 = i3 >> 4;
	    p4 = i4 >> 4;
	    /*
	       if(p1>15) p1=15;
	       if(p2>15) p2=15;
	       if(p3>15) p3=15;
	       if(p4>15) p4=15; */
	    sum = parameters[i].p[4];
	    pos = pos(p1, p2, p3, p4);
	    if (table[pos]) {
		int sum;
		p1 = (p1 << 4) + p1;
		p2 = (p2 << 4) + p2;
		p3 = (p3 << 4) + p3;
		p4 = (p4 << 4) + p4;
		sum = p1 + p2 + p3 + p4;
		if ((p1=dist(parameters[i].p[0],
			 parameters[i].p[1],
			 parameters[i].p[2],
			 parameters[i].p[3],
			 parameters[i].p[4],
			 p1, p2, p3, p4, sum)) >=
		    (p1=dist(parameters[table[pos]].p[0],
			 parameters[table[pos]].p[1],
			 parameters[table[pos]].p[2],
			 parameters[table[pos]].p[3],
			 parameters[table[pos]].p[4],
			 p1, p2, p3, p4, sum))&&(p1!=p2||priority[i/256]<=priority[table[pos]/256]))
		    goto skip;
	    }
	    table[pos] = i;
	    add(pos);
	  skip:;

	}
    }
    for (pos = 0; pos < 256; pos++) {
	int mindist = INT_MAX, d1;
	for (i = 0; i < NCHARS; i++) {
	    if (ALOWED(i, supported)) {
		if ((d1 = dist1(parameters[i].p[0],
				parameters[i].p[1],
				parameters[i].p[2],
				parameters[i].p[3],
				parameters[i].p[4],
				pos, pos, pos, pos, pos*4)) <= mindist&&(d1!=mindist||priority[i/256]>priority[filltable[pos]/256]))
		    filltable[pos] = i, mindist = d1;
	    }
	}
    }
    do {
	int blocked;
	if (last != -1)
	    next[last] = last;
	else
	    break;
	blocked = last;
	i = first;
	if (i == -1)
	    break;
	first = last = -1;
	do {
	    int m1, m2, m3, m4, ii, dm;
	    unsigned short c = table[i];
	    /* printf ("%c\n", c); */
	    postoparams(i, m1, m2, m3, m4);
	    for (dm = 0; dm < 4; dm++)
		for (ii = -1; ii <= 1; ii += 2) {
		    int dist, dist1, index;
		    unsigned short ch;
		    i1 = m1;
		    i2 = m2;
		    i3 = m3;
		    i4 = m4;
		    switch (dm) {
		    case 0:
			i1 += ii;
			if (i1 < 0 || i1 >= 16)
			    continue;
			break;
		    case 1:
			i2 += ii;
			if (i2 < 0 || i2 >= 16)
			    continue;
			break;
		    case 2:
			i3 += ii;
			if (i3 < 0 || i3 >= 16)
			    continue;
			break;
		    case 3:
			i4 += ii;
			if (i4 < 0 || i4 >= 16)
			    continue;
			break;

		    }
		    index = pos(i1, i2, i3, i4);
		    ch = table[index];
		    if (ch == c || index == blocked)
			continue;
		    if (ch) {
			int ii1 = (i1 << 4) + i1;
			int ii2 = (i2 << 4) + i2;
			int ii3 = (i3 << 4) + i3;
			int ii4 = (i4 << 4) + i4;
			int iisum = ii1 + ii2 + ii3 + ii4;
			dist = dist(
				       ii1, ii2, ii3, ii4, iisum,
				       parameters[c].p[0],
				       parameters[c].p[1],
				       parameters[c].p[2],
				       parameters[c].p[3],
				       parameters[c].p[4]);
			dist1 = dist(
					ii1, ii2, ii3, ii4, iisum,
					parameters[ch].p[0],
					parameters[ch].p[1],
					parameters[ch].p[2],
					parameters[ch].p[3],
					parameters[ch].p[4]);
		    }
		    if (!ch || dist < dist1) {
			table[index] = c;
			add(index);
		    }
		}
	    i1 = i;
	    i = next[i];
	    next[i1] = i1;
	}
	while (i != i1);
    }
    while (last != -1);
    c->table = table;
    c->filltable = filltable;
    c->parameters = parameters;
    free(next);
    return (table);
}
