#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>

#include <proto/all.h>
#include <pragmas/wb_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef struct {
	int colors;
	int depth;
    int max;
	int red[256];
	int green[256];
	int blue[256];
}
palette;

palette *pal=NULL;              /* The palette the icon will use */
char *tool=NULL;                /* The default tool of the icon */
char *tooltypes[] = {           /* Tool type list. */
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};
#define MAXTOOLTYPES 10
int typecount=0;


USHORT *imageData=NULL;

/* our functions */
void cleanup(char *,int);
BOOL makeIcon(UBYTE *, char **, char *);

int
CXBRK(void)
{
    cleanup("User Interrupt.",21);
	return (0);
}								/* Disable SAS Lattice CTRL/C handling */

struct Image iconImage1 =
{
	0, 0,						/* Top Corner */
	0,0,0,     					/* Width, Height, Depth */
	NULL,              			/* Image Data */
	0x000, 0x000,				/* PlanePick,PlaneOnOff */
	NULL						/* Next Image */
};

struct DiskObject Icon =
{
	WB_DISKMAGIC,		    		/* Magic Number */
	WB_DISKVERSION,			    	/* Version */
	{					    		/* Embedded Gadget Structure */
		NULL,					    /* Next Gadget Pointer */
		0, 0, 0, 0, 		    	/* Left,Top,Width,Height */
		GADGIMAGE | GADGHBOX,	    /* Flags */
		GADGIMMEDIATE | RELVERIFY,	/* Activation Flags */
		BOOLGADGET,				    /* Gadget Type */
		&iconImage1,               	/* Render Image */
		NULL,			    		/* Select Image */
		NULL,		    			/* Gadget Text */
		NULL,	    				/* Mutual Exclude */
		NULL,				    	/* Special Info */
		0,				    		/* Gadget ID */
		NULL				    	/* User Data */
	},
	WBPROJECT,		    			/* Icon Type */
	NULL,           			    /* Default Tool */
	NULL,   				    	/* Tool Type Array */
	NO_ICON_POSITION,	    		/* Current X */
	NO_ICON_POSITION,   			/* Current Y */
	NULL,						    /* Drawer Structure */
	NULL,						    /* Tool Window */
	4000					    	/* Stack Size */
};

/* Opens and allocations we must clean up */
struct Library *IconBase = NULL;

int
log2(int val)
{
    int c=0;
    
    while(val/=2) c++;
    
    return c;
}

#define MIN(a,b) ((a)<(b))?(a):(b);

/*
 * build palette
 * 
 * This function reads in a ppmfile and coverts it to a "palette".
 * 
 * This function assumes a few things: 1. each color in the ppmfile is
 * unique. 2. the ppmfile has no more than 256 pixels.  (Longer images
 * are truncated.)
 */
palette *
build_palette(char *fname)
{
	FILE *ppmfile;
	char buf[80];
	int wide, high;
    int i;
	palette *pal;

	pal = AllocVec(sizeof(palette), NULL);
	if (!pal) 
		cleanup("Could not allocate palette.",21);
	
	ppmfile = fopen(fname, "r");
	if (!ppmfile) 
		cleanup("Could not open palette file.\n",21);
	
	fgets(buf, 80,ppmfile);
	if (strcmp(buf, "P3\n")) 
		cleanup("Palette must be in P3 format.\n",21);

	fscanf(ppmfile, "%d %d", &wide, &high);
    pal->colors=MIN(256,wide*high);
    pal->depth=log2(pal->colors);
    if ((1<<pal->depth)<pal->colors) pal->depth++;
    
    printf("Palette will require %d planes.\n",pal->depth);
 
    if (fscanf(ppmfile,"%d",&pal->max)!=1) cleanup("Failed to read palette file.\n",21);
 
    for(i=0;i<pal->colors;i++) 
        if (fscanf(ppmfile,"%d",&pal->red[i])!=1) 
            cleanup("Failed reading the palette file.\n",21);
        else if (fscanf(ppmfile,"%d",&pal->green[i])!=1) 
            cleanup("Failed reading the palette file.\n",21);
        else if (fscanf(ppmfile,"%d",&pal->blue[i])!=1) 
            cleanup("Failed reading the palette file.\n",21);

    printf("Palette:\n");
    for(i=0;i<pal->colors;i++)
        printf("\t%d\t%d\t%d\n",pal->red[i],pal->green[i],pal->blue[i]);
           
    fclose(ppmfile);
    return pal;
}

void 
scale_palette(palette *p,int m)
{
    int i;
    
    for (i=0;i<pal->colors;i++) {
        pal->red[i]=(((float)pal->red[i]/(float)p->max)*(float)m+0.5);
        pal->green[i]=(((float)pal->green[i]/(float)p->max)*(float)m+0.5);
        pal->blue[i]=(((float)pal->blue[i]/(float)p->max)*(float)m+0.5);
    }
    printf("Scaled Palette:\n");
    for(i=0;i<pal->colors;i++)
        printf("\t%d\t%d\t%d\n",pal->red[i],pal->green[i],pal->blue[i]);

}

int
lookup_color(palette *pal,int r, int g, int b)
{
    int i;
    
    for (i=0;i<pal->colors; i++) 
        if ((pal->red[i]==r)&&(pal->green[i]==g)&&(pal->blue[i]==b)) return i;

    return -1;
}

void 
PutPixel(USHORT *imagedata, int wwide, int high, int depth, int x, int y, int c)
{
    int windex, bindex, fsize;
    int count;    
    USHORT mask;
    
    windex=(wwide*y)+x/16;
    fsize=wwide*high;   
    bindex=x%16;

    mask=1<<(15-bindex);
    
    for (count=0;count<depth;count++) {
        if (c&1) 
            imagedata[windex]|=mask;
        c/=2;
        windex+=fsize;
    }
}
    
void
build_image(palette *pal, struct Image *image, FILE *ppmfile)
{
    int wide, high, max, wwide;
    char buf[80];
    int x,y,c,r,g,b;
    
    if (!ppmfile)
        cleanup("Could not open image file.\n",21);
    fgets(buf,80,ppmfile);
    if (strcmp(buf,"P6\n"))
        cleanup("Image must be in P6 format.\n",21);
        
    fscanf(ppmfile,"%d",&wide); 
    fscanf(ppmfile,"%d",&high);
    if (!fscanf(ppmfile,"%d",&max)) cleanup("Failed to read image file.\n",21);;
    
    if (max!=pal->max) scale_palette(pal,max);
    
    /* skip to the EOL */
    while (fgetc(ppmfile)!='\n') {}
    
    image->Width=wide; image->Height=high;
    image->Depth=pal->depth;
    image->PlanePick=pal->depth-1;
    image->PlaneOnOff=pal->depth-1;
    wwide=wide/16; if (wwide*16!=wide) wwide++;
    
    imageData=AllocVec(wwide*high*pal->depth*2,MEMF_CLEAR);
    if (!imageData) cleanup("Could not allocate memory for the image.\n",21);

    image->ImageData=imageData;
    
    for(y=0;y<high;y++)
        for(x=0;x<wide;x++) {
            r=fgetc(ppmfile); g=fgetc(ppmfile); b=fgetc(ppmfile);
            
            c=lookup_color(pal,r,g,b);
            if (c<0) {
                fprintf(stderr,"%d  %d  %d\n",r,g,b);
                cleanup("Image contains a color not in the palette.\n",21);
            }
            PutPixel(imageData,wwide,high, pal->depth,x,y,c);
        }

}  

#define USAGE "syntax: ppmtoinfo [-tool {default tool}] 0[-tooltype {tool type}]10\n\t-map {mapfile} -icon {infofile} [{ppmfile}]\n"
    
/*
 * ppmtoinfo
 * 
 * Arguments:   ppmfile mapfile iconfile
 * 
 * ppmfile - the image to turn into an icon mapfile - a ppmfile which
 * describes the palette to use iconfile - the icon to make.
 */
void 
main(int argc, char **argv)
{
    char *ppmfile=NULL;
    char *mapfile=NULL;
    char *infofile=NULL;
    FILE *In;
    int i;
        
    /* parse the arguments */
    for (i=1;(i<argc)&&(argv[i][0]=='-');i++) {
        if (!strcmp(argv[i],"-tool")) { tool=argv[i+1]; i++; }
        else if (!strcmp(argv[i],"-tooltype")) {
            if (typecount<MAXTOOLTYPES) {
                tooltypes[typecount]=argv[i+1]; 
                i++; typecount++;
            }
        } else if (!strcmp(argv[i],"-map")) {
            mapfile=argv[i+1]; i++;
        } else if (!strcmp(argv[i],"-icon")) {
            infofile=argv[i+1]; i++;
        } else cleanup(USAGE,21);
    }

    if (i>argc) cleanup(USAGE,21);
 
    if (i<argc) {
        ppmfile=argv[i]; i++;
    }
 
    if (!mapfile) cleanup(USAGE,21);
    
    if (i<argc) cleanup(USAGE,21);
    
	/* Open icon.library */
	if (!(IconBase = OpenLibrary("icon.library", 33)))
		cleanup("Could not open icon.library",21);

    pal=build_palette(mapfile);
    
    if (ppmfile) In=fopen(ppmfile,"r");
    else In=stdin;
    
    build_image(pal,&iconImage1,In);

    Icon.do_Gadget.Width=iconImage1.Width;
    Icon.do_Gadget.Height=iconImage1.Height;
    Icon.do_DefaultTool=tool;
    Icon.do_ToolTypes=tooltypes;
    
    PutDiskObject(infofile,&Icon);
    
	cleanup("Finished.",0);
}

void 
cleanup(char *s, int n)
{
    if (pal) FreeVec(pal);
    if (imageData) FreeVec(imageData);
    
	if (IconBase)
		CloseLibrary(IconBase);
    puts(s);
    exit(n);
}
