#include "xmap.h"
#include "misc.h"
#include "sect.h"

typedef int boolean;

#define NEXTBYTE (*ptr++)
#define IMAGESEP 0x2c
#define COLORMAPMASK 0x80

#define DCH_BLANK	' '

#define bitmap(x,y)	*(Raster + (worldx>>1)*(y) + (x)/2)
#define ownmap(x,y)     *(Owner  + (worldx>>1)*(y) + (x)/2)

struct sectcolor {
    byte mnem, red, green, blue;
} secttype[] = {
/* sectortype   red     green   blue */
 DCH_WATER,	50,	90,	200, /* build */
 DCH_MOUNT,	128,	42,	42,  /* brown */
 DCH_SANCT,	227,	207,	87,  /* banana */
 DCH_WASTE,	255,	0,	0,
 DCH_RUIN,	0,	0,	0, 
 DCH_DESERT,	220,	150,	150,
 DCH_FOREST,	0,	255,	0,
 DCH_ARCTIC,	250,	250,	250,
 DCH_SWAMP,	10,	30,	0,
 DCH_RURAL,	10,	127,	0,
 DCH_CAPIT,	10,	255,	255,
 DCH_HARBR,	50,	200,	0,
 DCH_WAREH,	255,	200,	0,
 DCH_TRADE,	255,	200,	0,
 DCH_AIRPT,	50,	200,	0,
 DCH_FORTR,	0,	200,	0,
 DCH_NUKE,	0,	200,	0,
 DCH_RADAR,	0,	200,	0,
 DCH_ENLIST,	0,	200,	0,
 DCH_TANK,	0,	200,	0,
 DCH_URAN,	0,	200,	0,
 DCH_PARK,	0,	200,	0,
 DCH_ARMSF,	0,	200,	0,
 DCH_AMMOF,	0,	200,	0,
 DCH_MINE,	0,	200,	0,
 DCH_GMINE,	0,	200,	0,
 DCH_AGRI,	0,	200,	0,
 DCH_OIL,	0,	200,	0,
 DCH_LIGHT,	0,	200,	0,
 DCH_HEAVY,	0,	200,	0,
 DCH_TECH,	0,	200,	0,
 DCH_RSRCH,	0,	200,	0,
 DCH_LIBR,	0,	200,	0,
 DCH_BANK,	0,	200,	0,
 DCH_REFINE,	0,	200,	0,
 DCH_CHEM,	0,	200,	0,
 DCH_MILL,	0,	250,	200,
 DCH_BSPAN,	0,	200,	100,
 DCH_HIWAY,	0,	200,	100,
 DCH_BHEAD,	0,	200,	100,
 DCH_BLANK,     0,        0,    0,
};

struct policolor {
    byte red, green, blue;
} politype[] = {
/*  red     green   blue */
     30,     90,    200,        /* water */
     10,     10,     10,        /* unspecific land */

     20,     40,     25,
     60,     40,     25,
    100,     40,     25,
    140,     40,     25,
    180,     40,     25,
    220,     40,     25,
     20,     80,     25,
     60,     80,     25,
    100,     80,     25,
    140,     80,     25,
    180,     80,     25,
    220,     80,     25,
     20,    120,     25,
     60,    120,     25,
    100,    120,     25,
    140,    120,     25,
    180,    120,     25,
    220,    120,     25,
     20,    160,     25,
     60,    160,     25,
    100,    160,     25,
    140,    160,     25,
    180,    160,     25,
    220,    160,     25,
     20,    200,     25,
     60,    200,     25,
    100,    200,     25,
    140,    200,     25,
    180,    200,     25,
    220,    200,     25,
     20,     40,     75,
     60,     40,     75,
    100,     40,     75,
    140,     40,     75,
    180,     40,     75,
    220,     40,     75,
     20,     80,     75,
     60,     80,     75,
    100,     80,     75,
    140,     80,     75,
    180,     80,     75,
    220,     80,     75,
     20,    120,     75,
     60,    120,     75,
    100,    120,     75,
    140,    120,     75,
    180,    120,     75,
    220,    120,     75,
     20,    160,     75,
     60,    160,     75,
    100,    160,     75,
    140,    160,     75,
    180,    160,     75,
    220,    160,     75,
     20,     40,     125,
     60,     40,     125,
    100,     40,     125,
    140,     40,     125,
    180,     40,     125,
    220,     40,     125,
     20,     80,     125,
     60,     80,     125,
    100,     80,     125,
    140,     80,     125,
    180,     80,     125,
    220,     80,     125,
     20,    120,     125,
     60,    120,     125,
    100,    120,     125,
    140,    120,     125,
    180,    120,     125,
    220,    120,     125,
     20,    160,     125,
     60,    160,     125,
    100,    160,     125,
    140,    160,     125,
    180,    160,     125,
    220,    160,     125,
};


#define NumSectTypes	(sizeof(secttype) / sizeof(struct sectcolor))
#define NumPoliTypes    (sizeof(politype) / sizeof(struct policolor))
#define GeoColorMapSize	NumSectTypes
#define PolColorMapSize NumPoliTypes

FILE *fp;

static int XC=0, YC=0;		/* Output X and Y coords of current pixel */
static int numused = 0;

struct sctstr sect;

int Width, Height;		/* image dimensions */
int BytesPerScanline;		/* bytes per scanline in output raster */

byte *Image;			/* The result array */
byte *Raster;			/* The raster data stream, unblocked */
byte *RawData;
byte *Owner;

byte Red[256], Green[256], Blue[256];
int used[256];

LoadMAP(fname,worldsize,maptype)
char *fname,*worldsize;
int maptype;
{
    int rawsize;
    register byte  ch, ch1;
    register byte *ptr, *ptr1, *own;
    int i,j,k;
    int worldx,worldy,x,y,z;

    worldx = worldy = -1;
    if (worldsize) {
	worldx = atoi(worldsize);
	while (*worldsize && *worldsize!='x') worldsize++;
	if (*worldsize=='x') worldsize++;
	worldy = atoi(worldsize);
    }

    if (worldx*worldy <= 0)
	FatalError("bad world size option");

    if (worldx < 0) {
	worldx = WORLD_X;
	worldy = WORLD_Y;
    }

    rawsize = (worldx>>1) * worldy;

    if (strcmp(fname,"-")==0) { fp = stdin;  fname = "<stdin>"; }
    else fp = fopen(fname,"r");
    if (!fp) FatalError("file not found");

    /*
    ** Allocate raster buffer
    */

    if (!(Raster = (byte *) malloc(rawsize)))
	FatalError("insufficient memory");
    
    if (!(Owner = (byte *) malloc(rawsize))) {
	FatalError("insufficient memory");
    }
    
    fprintf(stderr,"Reading %s\n",fname);

    switch (maptype) {

    case 2: /* `map' command output */

	;;;

    case 1: /* bigmap file */

        political = 0;
	if (!(ptr = RawData = (byte *) malloc(rawsize)))
	    FatalError("insufficient memory");
	if ((j=fread(ptr, 1, rawsize, fp)) != rawsize)
	    FatalError("bigmap data read failed");
	for (ptr=RawData, i=0; i<rawsize; i++, ptr++) {
	    for (j=0; j<NumSectTypes; j++)
		if (secttype[j].mnem == *ptr) {
		    Raster[i] = j;
		    break;
		}
	    if (j==NumSectTypes)
		Raster[i] = NumSectTypes-1;
	}
	free(RawData);
	break;

    default: /* sector database */

	ptr = Raster;
	own = Owner;
	for (i=0; i<rawsize; i++) {
	    if ((j=fread(&sect, sizeof(sect), 1, fp)) != 1)
		FatalError("sector database read failed\n");
            *own = 0;
	    if (sect.sct_type < 0 || sect.sct_type >= NumSectTypes) {
		*ptr++ = (byte)(NumSectTypes-1);
    	        own++;
	    } else {
		*ptr++ = (byte) sect.sct_type;
                if (sect.sct_type == 0) *own++ = 0;
                else {
                    *own = 1;
                    for (j=0; j<(NumPoliTypes-2); j++) {
                        if (polizone[j] == sect.sct_own) {
                            *own = j+2;
                            break;
                        }
                    }
                    own++;
                }
	    }
	}
	break;

    }

    if (fp != stdin) fclose(fp);
    
    newCmap = DefaultColormap(theDisp, theScreen);
    numcols = political ? PolColorMapSize+1 : GeoColorMapSize;
    for (i = 0; i < numcols; i++) {
	used[i]  = 0;
        if (political) {
            Red[i]   = politype[i].red;
    	    Green[i] = politype[i].green;
	    Blue[i]  = politype[i].blue;
        } else {
    	    Red[i]   = secttype[i].red;
    	    Green[i] = secttype[i].green;
	    Blue[i]  = secttype[i].blue;
        }
    }

    /*
    **  We want to build up a patchwork that looks something like this.
    **  Every sector is a 4-pixel square; odd y-rows are indented one pixel
    **  to the right.  Thus, the image will be (worldx+1) pixels wide, and
    **  (worldy*2) pixels high.
    **   _________________
    **  | XX**YY**
    **  | XX**YY**
    **  |OO..OO..OO
    **  |OO..OO..OO
    **  | XX**YY**   ...
    **  | XX**YY**
    **  |OO..OO..OO
    **  |OO..OO..OO
    **
    **   00000000001111111111
    **   01234567890123456789 ...
    */

    Width  = worldx + 1;
    Height = worldy * 2;
    BytesPerScanline = Width;

    /* Allocate the X Image */
    Image = (byte *) malloc(Width*Height);
    if (!Image) FatalError("not enough memory for XImage");
    
    theImage = XCreateImage(theDisp,theVisual,8,ZPixmap,0,Image,
			    Width,Height,8,BytesPerScanline);

    if (!theImage) FatalError("unable to create XImage");

    for (y=0; y<worldy; y++) {
	for (i=0; i<2; i++) {
	    if ((y&1)==1)
		AddToPixel(NumSectTypes-1);
	    for (x=y&1; x<worldx; x+=2) {
		j = political ? ownmap(x,y) : bitmap(x,y);
		AddToPixel(j);
		AddToPixel(j);
	    }
	    if ((y&1)==0)
		AddToPixel(NumSectTypes-1);
	}
    }

    free(Raster);
    free(Owner);
    ColorDicking(fname);
}


static AddToPixel(Index)
int Index;
{
    if (YC<Height)
        *(Image + YC * BytesPerScanline + XC) = (byte)Index;

    if (++XC == Width) {
	XC = 0;
	YC++;
    }

    if (!used[Index]) { 
	used[Index]=1;  
	numused++; 
    }
}


static ColorDicking(fname)
    char *fname;
{
    /* 
    ** We've got the picture loaded, we know what colors are needed. 
    ** Get 'em.
    */

    register int i,j;
    static byte lmasks[8] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
    byte lmask, *ptr, *own;

    /* 
    ** Allocate the X colors for this picture.
    */

    if (nostrip)  {   /* nostrip was set.  try REAL hard to do it */
        for (i=j=0; i<numcols; i++) {
            if (used[i]) {
                defs[i].red   = Red[i]<<8;
                defs[i].green = Green[i]<<8;
                defs[i].blue  = Blue[i]<<8;
                defs[i].flags = DoRed | DoGreen | DoBlue;
                if (!XAllocColor(theDisp,theCmap,&defs[i])) { 
                    j++;  defs[i].pixel = 0xffff;
		}
                cols[i] = defs[i].pixel;
	    }
	}
	
        if (j) { /* failed to pull it off */
            XColor ctab[256];
            int    dc;
            dc = (dispcells<256) ? dispcells : 256;
            fprintf(stderr,"Failed to allocate %d out of %d colors.\n");
	    fprintf(stderr,"Trying extra hard.\n",j,numused);

            /* 
	    ** Read in the color table.
	    */

            for (i=0; i<dc; i++) ctab[i].pixel = i;
            XQueryColors(theDisp,theCmap,ctab,dc);
	    
            /* 
	    ** Run through the used colors.  any used color that has a 
	    ** pixel value of 0xffff wasn't allocated.  For such colors, 
	    ** run through the entire X colormap and pick the closest color.
	    */
	    
            for (i=0; i<numcols; i++)
                if (used[i] && cols[i]==0xffff) {  /* an unallocated pixel */
                    int d, mdist, close;
                    unsigned long r,g,b;
                    mdist = 100000;
		    close = -1;
                    r =  Red[i];
                    g =  Green[i];
                    b =  Blue[i];
                    for (j=0; j<dc; j++) {
                        d = abs(r - (ctab[j].red>>8)) +
                            abs(g - (ctab[j].green>>8)) +
				abs(b - (ctab[j].blue>>8));
                        if (d<mdist) { mdist=d; close=j; }
		    }
                    if (close<0) FatalError("simply can't do it.  Sorry.");
                    bcopy(&defs[close],&defs[i],sizeof(XColor));
                    cols[i] = ctab[close].pixel;
		}
	}	/* end 'failed to pull it off' */
    }

    else {          /* strip wasn't set, do the best auto-strip */
        j = 0;
        while (strip<8) {
            lmask = lmasks[strip];
            for (i=0; i<numcols; i++)
                if (used[i]) {
                    defs[i].red   = (Red[i]  &lmask)<<8;
                    defs[i].green = (Green[i]&lmask)<<8;
                    defs[i].blue  = (Blue[i] &lmask)<<8;
                    defs[i].flags = DoRed | DoGreen | DoBlue;
                    if (!XAllocColor(theDisp,theCmap,&defs[i])) break;
                    cols[i] = defs[i].pixel;
		}
	    
            if (i<numcols) {		/* failed */
                strip++;  j++;
                for (i--; i>=0; i--)
                    if (used[i]) XFreeColors(theDisp,theCmap,cols+i,1,0L);
	    }
            else break;
	}
	
        if (j && strip<8)
            fprintf(stderr,"%s:  %s stripped %d bits\n",cmd,fname,strip);
	
        if (strip==8) {
            fprintf(stderr,"Impossible to allocate the desired colors.\n");
            for (i=0; i<numcols; i++) cols[i]=i;
	}
    }
    
    ptr = Image;
    for (i=0; i<Height; i++)
        for (j=0; j<Width; j++,ptr++,own++) 
            *ptr = (byte) cols[*ptr];
}
