#ifndef GENHAM_H
#define GENHAM_H

/*

	GenHAM.h Version 1.4
	Created 4th July 1989 by Danny Ross BSc(Hons)
	Last updated 29th August 1989

	Function :

		'Optimally' remaps a 12-Bitplane deep Rastport (4096 colours) into
		a Window on an Intuition HAM Screen

	Usage :

		error=GenHAM(DestWindowPtr, SourceRastport12Ptr);

	Another fine product from Johnny Appleseed S/W Development

	Requires support routines from extended fastpixel.o (see text)
	Returns -1 if insufficient memory.

*/

/* PERF_MON turns on performance monitoring code - remove if not desired */

#define PERF_MON 1

extern void initStreamRead();
extern int streamRead();

/* Macros for manipulating RGB elements of 12-bit colour values */
 
#define EXTRACTRGB(COL,RR,GG,BB) { RR=((COL&0xF00)>>8); GG=((COL&0xF0)>>4); BB=(COL&0xF); }
#define FMAKERGB(RR,GG,BB) ( (int)((RR<<8)|(GG<<4)|(BB)) )

/* HAM command codes */

#define FMODIFY_BLUE  (0x10)
#define FMODIFY_RED   (0x20)
#define FMODIFY_GREEN (0x30)
#define FINDEX        (0x00)
#define FHAM(CC,CL)   ((CC)|(CL))

#define CARRSIZE ((4096)*(sizeof(long)))
#define CTOTSIZE ((4096)*(sizeof(long)))

/* 'Fat' bitmap stucture */

struct extraBitMap
	{
	UWORD BytesPerRow;
	UWORD Row;
	UBYTE Flags;
	UBYTE Depth;
	UWORD pad;
	PLANEPTR Planes[12];
	} ;

/* The GenHAM function.

   This should not really be in an include file.  Rather, it should be
compiled and linked as a standalone function.  Unfortunately the method for
doingthis is very compiler specific but when you have it working, move
GenHAM() to it's own .c file and compile separately.

*/

int GenHAM(struct Window *wndo, struct RastPort *srcrp)
	{
	struct ViewPort *vp;
	struct Screen *scrn;
	struct RastPort *dstrp;

	long *arr, *tots;

	SHORT pl[16];
	SHORT maxx,maxy,minx,miny;

	int i,k,l,lx,rtc;
	int r,g,b,cr,cg,cb,xr,xg,xb;
	int adj,cham,cc,ir,ss,sc,cadj,ct,cindex;

	register int j;
	register long t;

#ifdef PERF_MON
	int tot_offhue, tot_direct;
#endif

	arr=tots=NULL; rtc=0;

	if ((arr=(long *)AllocMem(CARRSIZE,MEMF_CLEAR|MEMF_PUBLIC))==NULL) { rtc=(-1); goto cleanup; }
	if ((tots=(long *)AllocMem(CTOTSIZE,MEMF_CLEAR|MEMF_PUBLIC))==NULL) { rtc=(-1); goto cleanup; }

	minx=wndo->LeftEdge; maxx=minx+wndo->Width;
	miny=wndo->TopEdge; maxy=miny+wndo->Height;

	dstrp=wndo->RPort; scrn=wndo->WScreen; vp=&(scrn->ViewPort);

	for (i=miny; i<=maxy; i++)
		{
		initStreamRead(srcrp,minx,i,1); cr=cb=cg=0;
		k=streamRead();

		for (j=minx+1; j<=maxx; j++)
			{
			EXTRACTRGB(k,r,g,b);
			t=adj=0;
			if (r!=cr) { adj++; t=t+abs(r-cr); }
			if (g!=cg) { adj++; t=t+abs(g-cg); }
			if (b!=cb) { adj++; t=t+abs(b-cb); }
			if (adj>1) tots[k]+=t;

			cr=r; cb=b; cg=g; k=streamRead();
			}
		}


	for (i=0; i<4096; i++)
		{
		k=tots[i]&0x7FFFFFFF;
		tots[i]=(k<<12) | (i&0x00000FFF);
		}

	for (i=0; i<16; i++)
		{
		for (j=4094; j>=i; j--) if (tots[j+1]>(t=tots[j])) { tots[j]=tots[j+1]; tots[j+1]=t; }
		}

	/* arr[0..15] holds 16 selected colours */
	/* tots[0..4096] holds ct.index number of colour */

	for (i=0; i<16; i++) pl[i]=(SHORT)(arr[i]=(tots[i] & 0x00000FFF));

	/* Bring darkest colour to 0 in attempt to make the screen's border black */
	/* (well, it's better than having the border bright pink or something)    */

	EXTRACTRGB(pl[15],cr,cg,cb);

	for (i=14; i>=0; i--)
		{
		EXTRACTRGB(pl[i],xr,xg,xb);
		if ( (cr+cg+cb) < (xr+xg+xb) )
			{
			j=pl[i]; pl[i]=pl[i+1]; pl[i+1]=j;
			j=arr[i]; arr[i]=arr[i+1]; arr[i+1]=j;
			}
		else
			{
			cr=xr; cg=xg; cb=xb;
			}
		}

	LoadRGB4(vp,pl,16);

	for (i=0; i<4096; i++) tots[i]=(-1);
	for (i=0; i<16; i++) { t=arr[i]; tots[t]=i; }

#ifdef PERF_MON
	tot_direct=tot_offhue=0;
#endif

	for (i=miny; i<=maxy; i++)
		{
		initStreamRead(srcrp,minx,i,1); cc=arr[0]; cham=FHAM(FINDEX,0);
		EXTRACTRGB(cc,cr,cg,cb); SetAPen(dstrp,cc);

		lx=minx;

		for (j=minx; j<=maxx; j++)
			{
			k=streamRead(); 
			if (k!=cc)
				{
				if ((ir=tots[k])!=(-1)) /* ir>=0 if the colour is directly available */
					{

#ifdef PERF_MON
					tot_direct++;
#endif

					cc=k; cham=FHAM(FINDEX,ir); EXTRACTRGB(cc,cr,cg,cb);

					Move(dstrp,lx,i); Draw(dstrp,j-1,i); SetAPen(dstrp,cham); lx=j;
					}
				else
					{
					EXTRACTRGB(k,xr,xg,xb);

					cadj=ct=99; cindex=0;

					arr[16]=cc;
					for (l=0; l<17; l++)
						{
						EXTRACTRGB(arr[l],r,g,b);

						adj=t=0;
						
						if (r!=xr) { adj++; t+=abs(r-xr); } /* calc. red adj */
						if (g!=xg) { adj++; t+=abs(g-xg); } /* calc. green adj */
						if (b!=xb) { adj++; t+=abs(b-xb); } /* calc. blue adj */

						if (l<16) adj++;

						if (adj<=cadj) if (t<=ct) { cadj=adj; ct=t; cindex=l; }
						}

#ifdef PERF_MON
					if (cadj<=1) tot_direct++; else tot_offhue+=ct;
#endif

					/* ir now contains the best modify option 0..15 or MODIFY */

					if (cindex!=16) /* i.e. it's best to use a colour index */
						{
						cc=arr[cindex]; cham=FHAM(FINDEX,cindex); EXTRACTRGB(cc,cr,cg,cb);

						Move(dstrp,lx,i); Draw(dstrp,j-1,i); SetAPen(dstrp,cham); lx=j;
						}
					else
						{
						ss=sc=0;
						if (abs(xr-cr)>ss) { ss=abs(xr-cr); sc=1; }
						if (abs(xg-cg)>ss) { ss=abs(xg-cg); sc=2; }
						if (abs(xb-cb)>ss) { ss=abs(xb-cb); sc=3; }

						switch (sc)
							{
							case 1 : { cc=FMAKERGB(xr,cg,cb); cham=FHAM(FMODIFY_RED,xr); cr=xr; break; }
							case 2 : { cc=FMAKERGB(cr,xg,cb); cham=FHAM(FMODIFY_GREEN,xg); cg=xg; break; }
							case 3 : { cc=FMAKERGB(cr,cg,xb); cham=FHAM(FMODIFY_BLUE,xb); cb=xb; break; }
							}

						Move(dstrp,lx,i); Draw(dstrp,j-1,i); SetAPen(dstrp,cham); lx=j;
						}
					}
				}
			}

		if (lx!=maxx) { Move(dstrp,lx,i); Draw(dstrp,maxx,i); }

		}

cleanup:

	if (tots) FreeMem(tots,CTOTSIZE);
	if (arr) FreeMem(arr,CARRSIZE);

#ifdef PERF_MON
	fprintf(stderr,"Total colour hits : %06d\n",tot_direct);
	fprintf(stderr,"Total hue error   : %06d\n",tot_offhue);
#endif

	return(rtc);
	}

#endif
