//;--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T--T
/******************************************************************************
 *
 * NewIcon Datatype
 *
 * Written by Pascal Hurni and Christian Buchner and David N. Junod
 *
 ******************************************************************************
 * dispatch.c
 *
 */

/************************************************************************
 *																								*
 *	Prefs:																					*
 *		NOBORDER/S		Dont' draw the border around images.					*
 *		NONORMAL/S		Dont' include the normal state image.					*
 *		NOSELECTED/S	Dont' include the selected state image.				*
 *		XOFFSET/N		Defines the number of pixels between the horizontal*
 *							borders and the images. Default to 2.					*
 *		YOFFSET/N		Defines the number of pixels between the vertical	*
 *							borders and the images. Default to 2.					*
 *		BORDERPEN/N		The pen number used to draw the border. Default		*
 *							to 1.																*
 *		COLORMAP			A picture filename containing a colormap. The		*
 *							images will be remaped to this colormap.				*
 *																								*
 ************************************************************************/


//#define DEBUG
#include "debug.h"
#include "classbase.h"

/*****************************************************************************/

void MineWriteChunkyPixels( struct RastPort *rp, LONG xstart, LONG ystart,
                            LONG xstop, LONG ystop, UBYTE *array, LONG bytesperrow,
                            UBYTE *convtable, LONG flags,
                            struct ClassBase *cb );

UBYTE MyObtainBestPen( struct ColorRegister color, struct ColorRegister *cmap,
                       UWORD ncol );

Object *GetFileColorMap( STRPTR filename, struct ColorRegister **pt_cmap, ULONG *pt_ncolors,
                         struct ClassBase *cb );
void FreeFileColorMap( Object *obj, struct ClassBase *cb );


#define PREFSTEMPLATE	"NOBORDER/S,NONORMAL/S,NOSELECTED/S,XOFFSET/N,YOFFSET/N,BORDERPEN/N,COLORMAP"

enum {
	PREFSARG_NOBORDER,
	PREFSARG_NONORMAL,
	PREFSARG_NOSELECTED,
	PREFSARG_XOFFSET,
	PREFSARG_YOFFSET,
	PREFSARG_BORDERPEN,
	PREFSARG_COLORMAP,
	PREFSARG_MAX
};

/*****************************************************************************/

ULONG setdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
	return (SetDTAttrsA (o, NULL, NULL, (struct TagItem *) & data));
}

/*****************************************************************************/

ULONG getdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
	return (GetDTAttrsA (o, (struct TagItem *) & data));
}

/*****************************************************************************/

Class *initClass (struct ClassBase * cb)
{
	Class *cl;
	
	/* Create our class.  Note that this particular class doesn't have any instance data */
	if (cl = MakeClass (NEWICONDTCLASS, PICTUREDTCLASS, NULL, NULL, 0L))
	{
		cl->cl_Dispatcher.h_Entry = (ULONG (*)())Dispatch;
		cl->cl_UserData = (ULONG) cb;
		AddClass (cl);
	}
	
	return (cl);
}

/*****************************************************************************/

ULONG __asm Dispatch (register __a0 Class * cl, register __a2 Object * o, register __a1 Msg msg)
{
	struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
	ULONG retval;
	
	switch (msg->MethodID)
	{
		case OM_NEW:
		if (retval = DoSuperMethodA (cl, o, msg))
		{
			if (!GetPicture (cb, cl, (Object *) retval, ((struct opSet *) msg)->ops_AttrList))
			{
				CoerceMethod (cl, (Object *) retval, OM_DISPOSE);
				return NULL;
			}
		}
		break;
		
		/* Let the superclass handle everything else */
		default:
		retval = (ULONG) DoSuperMethodA (cl, o, msg);
		break;
	}
	
	return (retval);
}

/*****************************************************************************/

BOOL __asm GetPicture (register __a6 struct ClassBase * cb, register __a0 Class * cl, register __a2 Object * o, register __a1 struct TagItem * attrs)
{
	BOOL						success = FALSE;
	STRPTR					title;
	STRPTR					fname;
	struct BitMapHeader	*bmhd;
	struct BitMap			*bm;
	UWORD						i, n, ncolors;
	ULONG						modeid = VGAPRODUCT_KEY;
	struct ColorRegister	*cmap, *tmpcmap;
	LONG						*cregs;

	ULONG						TmpOffset;
	ULONG						XOffset = 2;
	ULONG						YOffset = 2;
	UWORD						BorderPen = 1;

	struct NewDiskObject	*ndo;
	ULONG						depth, rest;
	STRPTR					tmp;

	struct RastPort		rp;
	Point						PolyDrawArray[4];
	UBYTE						*ConvTable;

	Object					*obj;
	BPTR						fh;
	struct ColorRegister	*var_ColorMap = NULL;
	ULONG						var_NColors;
	ULONG						var_Border   = TRUE;
	ULONG						var_Normal   = TRUE;
	ULONG						var_Selected = TRUE;


	ENTERING;


	/* Get the default title */
	title = (STRPTR) GetTagData (DTA_Name, NULL, attrs);

	/* create the filename */

	if ( !(fname = AllocVec( strlen( title ) , 0L ) ) ) {
		SetIoErr(ERROR_NO_FREE_STORE);
	}
	else {

		strcpy( fname, title );

		if ( !((tmp = strrchr( fname, '.' )) && (strcmp( tmp, ".info" ) == 0)) ) {
			SetIoErr(ERROR_INVALID_COMPONENT_NAME);
		}
		else {

		*tmp = 0;

	
		/* Get the BitMapHeader to write to */
		if ( getdtattrs (cb, o, PDTA_BitMapHeader, &bmhd, TAG_DONE) != 1 )
		{
			SetIoErr(ERROR_OBJECT_WRONG_TYPE);
		}
		else
		{


			DPUT(("About to get prefs vars.\n" ));

			/*** Get the environement variables for the preferences */

			/* Opens the prefs file */
			if ( fh = Open( "ENV:Datatypes/NewIcon.prefs", MODE_OLDFILE ) ) {

				struct RDArgs *rdargs;
				UBYTE buf[256];
				ULONG para[PREFSARG_MAX];

				DPUT(("Prefs file opened.\n" ));

				/* Get next line */
				while ( FGets( fh, buf, sizeof(buf) ) ) {

					/* Process it if it's not a comment */
					tmp = buf;
					while ( *tmp < 33 && *tmp != 0 ) tmp++;
					if ( !((*tmp == ';') || (*tmp == '#') || (*tmp == '*') || (*tmp == 0)) ) {

						if ( rdargs = (struct RDArgs *)AllocDosObject( DOS_RDARGS, NULL ) ) {

							rdargs->RDA_Source.CS_Buffer = buf;
							rdargs->RDA_Source.CS_Length = strlen(buf);

							para[PREFSARG_NOBORDER]		= FALSE;
							para[PREFSARG_NONORMAL]		= FALSE;
							para[PREFSARG_NOSELECTED]	= FALSE;
							para[PREFSARG_XOFFSET]		= NULL;
							para[PREFSARG_YOFFSET]		= NULL;
							para[PREFSARG_BORDERPEN]	= NULL;
							para[PREFSARG_COLORMAP]		= NULL;

							if ( ReadArgs( PREFSTEMPLATE, (LONG *) para, rdargs) ) {

								if ( para[PREFSARG_NOBORDER] )
									var_Border = FALSE;

								if ( para[PREFSARG_NONORMAL] )
									var_Normal = FALSE;

								if ( para[PREFSARG_NOSELECTED] )
									var_Selected = FALSE;

								if ( para[PREFSARG_XOFFSET] ) {
									XOffset = *((LONG *) para[PREFSARG_XOFFSET]);
									if ( XOffset > 65535 ) XOffset = 2;
								}

								if ( para[PREFSARG_YOFFSET] ) {
									YOffset = *((LONG *) para[PREFSARG_YOFFSET]);
									if ( YOffset > 65535 ) YOffset = 2;
								}

								if ( para[PREFSARG_BORDERPEN] ) {
									BorderPen = (*((LONG *) para[PREFSARG_BORDERPEN])) & 255;
								}

								if ( para[PREFSARG_COLORMAP] ) {
									obj = GetFileColorMap( (STRPTR)para[PREFSARG_COLORMAP], &var_ColorMap, &var_NColors, cb );
								}

							}

							FreeDosObject( DOS_RDARGS, rdargs );

						}

					}

				}

				Close( fh );

			}


			DPUT(("About to GetNewDiskObject() with fname=[%s]\n", fname ));

			if ( ndo = GetNewDiskObject( fname ) ) {

				DPUT(("GetNewDiskObject() successful !\n" ));

				if ( (ndo->ndo_NormalImage != NULL && var_Normal) || (ndo->ndo_SelectedImage != NULL && var_Selected) ) {


					/*** Computes the BitMap dimensions */

					bmhd->bmh_Width = XOffset;
					bmhd->bmh_Height = 0;

					if ( ndo->ndo_NormalImage && var_Normal ) {
						bmhd->bmh_Width += ndo->ndo_NormalImage->Width+XOffset;
						bmhd->bmh_Height = ndo->ndo_NormalImage->Height;
						if ( var_Border )
							bmhd->bmh_Width += 1+1;

						DPUT(("NormalImage: Width=%ld, Height=%ld\n", (LONG)ndo->ndo_NormalImage->Width, (LONG)ndo->ndo_NormalImage->Height ));
					}

					if ( ndo->ndo_SelectedImage && var_Selected ) {
						bmhd->bmh_Width += ndo->ndo_SelectedImage->Width+XOffset;
						bmhd->bmh_Height = MAX( ndo->ndo_NormalImage->Height, bmhd->bmh_Height );
						if ( var_Border )
							bmhd->bmh_Width += 1+1;

						DPUT(("SelectedImage: Width=%ld, Height=%ld\n", (LONG)ndo->ndo_SelectedImage->Width, (LONG)ndo->ndo_SelectedImage->Height ));
					}

					bmhd->bmh_Height += YOffset+YOffset;
					if ( var_Border )
						bmhd->bmh_Height += 1+1;


					/*** Get the number of different colors in both image */

					if ( var_ColorMap ) {

						ncolors = var_NColors;

					}
					else {

						if ( !ndo->ndo_NormalImage || !var_Normal ) {
							ncolors = ndo->ndo_SelectedImage->NumColors;
						}
						else if ( !ndo->ndo_SelectedImage || !var_Selected ) {
							ncolors = ndo->ndo_NormalImage->NumColors;
						}
						else {

							ncolors = ndo->ndo_NormalImage->NumColors;

							for ( n=0; n<ndo->ndo_SelectedImage->NumColors; n++ ) {

								for ( i=0; i<ndo->ndo_NormalImage->NumColors; i++ ) {
									if ( *((struct ColorRegister *) &(ndo->ndo_NormalImage->Palette[i*3])) ==
									     *((struct ColorRegister *) &(ndo->ndo_SelectedImage->Palette[n*3])) ) {

										ncolors--;
										break;
									}

								}

								ncolors++;

							}

						}

					}

					/*** Calculate the depth according to ncolors */

					depth = 0;
					rest = ncolors-1;
					while ( rest > 0 ) {
						rest = rest >> 1;
						depth++;
					}
					if ( depth == 0 ) depth = 1;	/*** If ncolors was 1 */
					if ( depth > 8 ) {
						depth = 8;	/*** If ncolors was greater than 256 */
						ncolors = 256;
					}

					bmhd->bmh_Depth = depth;


					DPUT(("BMHD: Width=%ld, Height=%ld, NColors=%ld, Depth=%ld\n",
					    (LONG)bmhd->bmh_Width, (LONG)bmhd->bmh_Height, (LONG)ncolors, (LONG)bmhd->bmh_Depth ));

					/* Set the number of colors */
					setdtattrs (cb, o, PDTA_NumColors, ncolors, TAG_DONE);
			
					/* Get the destination for the color information */
					getdtattrs (cb, o,
									PDTA_ColorRegisters,	(ULONG) &cmap,
									PDTA_CRegs,				&cregs,
									TAG_DONE);


					/*** Set up the Colormap for both images */

					if ( var_ColorMap ) {

						DPUT(("The ColorMap :"));

						tmpcmap = cmap;

						for ( n=0; n<ncolors; n++ ) {

							/* Set the master color table */
							tmpcmap->red   = var_ColorMap->red;
							tmpcmap->green = var_ColorMap->green;
							tmpcmap->blue  = var_ColorMap->blue;

							/* Set the color table used for remapping */
							cregs[n * 3 + 0] = tmpcmap->red   << 24;
							cregs[n * 3 + 1] = tmpcmap->green << 24;
							cregs[n * 3 + 2] = tmpcmap->blue  << 24;

							D(if ( (n % 8) == 0 ) bprintf("\n") );
							DPUT(("%02lx%02lx%02lx ", (LONG) tmpcmap->red, (LONG) tmpcmap->green, (LONG) tmpcmap->blue ));

							tmpcmap++;
							var_ColorMap++;
						}

						DPUT(("\n\n"));

						/*** Frees the file colormap */
						FreeFileColorMap( obj, cb );

					}
					else {

						if ( !ndo->ndo_NormalImage ) {
							memcpy( cmap, ndo->ndo_SelectedImage->Palette, ndo->ndo_SelectedImage->NumColors * 3 );
						}
						else if ( !ndo->ndo_SelectedImage ) {
							memcpy( cmap, ndo->ndo_NormalImage->Palette, ndo->ndo_NormalImage->NumColors * 3 );
						}
						else {

							memcpy( cmap, ndo->ndo_NormalImage->Palette, ndo->ndo_NormalImage->NumColors * 3 );

							tmpcmap = cmap+ndo->ndo_NormalImage->NumColors;

							/*** BUG: It can happen that NormalImage->NumColors plus the different
							 *** colors in SelectedImage could exceeds 256 ! A check should be done
							 *** inside the for loop below.
							 **/

							for ( n=0; n<ndo->ndo_SelectedImage->NumColors; n++ ) {

								for ( i=0; i<ndo->ndo_NormalImage->NumColors; i++ ) {
									if ( *((struct ColorRegister *) &(ndo->ndo_NormalImage->Palette[i*3])) ==
									     *((struct ColorRegister *) &(ndo->ndo_SelectedImage->Palette[n*3])) ) {
										break;
									}

								}

								if ( i == ndo->ndo_NormalImage->NumColors ) {
									*tmpcmap = *((struct ColorRegister *) &(ndo->ndo_SelectedImage->Palette[n*3]));
									tmpcmap++;
								}

							}

						}

						DPUT(("The ColorMap :"));

						tmpcmap = cmap;

						for ( n=0; n<ncolors; n++ ) {

							D(if ( (n % 8) == 0 ) bprintf("\n") );
							DPUT(("%02lx%02lx%02lx ", (LONG) tmpcmap->red, (LONG) tmpcmap->green, (LONG) tmpcmap->blue ));

							/* Set the color table used for remapping */
							cregs[n * 3 + 0] = tmpcmap->red   << 24;
							cregs[n * 3 + 1] = tmpcmap->green << 24;
							cregs[n * 3 + 2] = tmpcmap->blue  << 24;

							tmpcmap++;
						}

						DPUT(("\n\n"));

					}


					/* Allocate the bitmap and the memory for the convtable */
					if ( ( (bm = AllocBitMap (bmhd->bmh_Width, bmhd->bmh_Height, bmhd->bmh_Depth, BMF_CLEAR, NULL)) == NULL ) ||
					     ( (ConvTable = AllocVec( ncolors, 0L ) ) == NULL ) ) {
						FreeBitMap( bm );
						FreeVec( ConvTable );
						SetIoErr (ERROR_NO_FREE_STORE);
					}
					else
					{
						/* Indicate success */
						success = TRUE;


						/*** Set up the RastPort */

						InitRastPort( &rp );
						rp.BitMap = bm;


						if ( ndo->ndo_NormalImage && var_Normal ) {


							if ( var_Border ) {
								/*** Draws the border for the Normal image */

								SetAPen( &rp, BorderPen );
								Move( &rp, XOffset, YOffset );
								PolyDrawArray[0].x = XOffset+ndo->ndo_NormalImage->Width+1;
								PolyDrawArray[0].y = YOffset;
								PolyDrawArray[1].x = XOffset+ndo->ndo_NormalImage->Width+1;
								PolyDrawArray[1].y = YOffset+ndo->ndo_NormalImage->Height+1;
								PolyDrawArray[2].x = XOffset;
								PolyDrawArray[2].y = YOffset+ndo->ndo_NormalImage->Height+1;
								PolyDrawArray[3].x = XOffset;
								PolyDrawArray[3].y = YOffset;
								PolyDraw( &rp, 4, (WORD *)PolyDrawArray );
							}

							/*** Computes the ConvTable for the Normal image */

							DPUT(("ConvTable for Normal image\n"));

							for ( i=0; i<ndo->ndo_NormalImage->NumColors; i++ ) {

								ConvTable[i] = MyObtainBestPen( *((struct ColorRegister *) &(ndo->ndo_NormalImage->Palette[i*3])),
								                                cmap, ncolors );

								DPUT(("ConvTable[%ld] = %ld for color = %02lx%02lx%02lx\n",
								    (LONG) i, (LONG) ConvTable[i],
								    (LONG) ndo->ndo_NormalImage->Palette[i*3],
								    (LONG) ndo->ndo_NormalImage->Palette[i*3+1],
								    (LONG) ndo->ndo_NormalImage->Palette[i*3+2] ));

							}


							/*** Copy the Normal image */

							if ( !var_Border ) {
								XOffset--;
								YOffset--;
							}
							MineWriteChunkyPixels( &rp, XOffset+1, YOffset+1,
							                       XOffset+ndo->ndo_NormalImage->Width,
							                       YOffset+ndo->ndo_NormalImage->Height,
							                       ndo->ndo_NormalImage->ChunkyData,
							                       ndo->ndo_NormalImage->Width,
							                       ConvTable,
							                       ndo->ndo_NormalImage->Flags,
							                       cb );
							if ( !var_Border ) {
								XOffset++;
								YOffset++;
							}

							TmpOffset = XOffset+ndo->ndo_NormalImage->Width+XOffset;
							if ( var_Border )
								TmpOffset += 2;


						}
						else {

							TmpOffset = XOffset;

						}


						if ( ndo->ndo_SelectedImage && var_Selected ) {

							if ( var_Border ) {
								/*** Draws the border for the Selected image */

								SetAPen( &rp, BorderPen );
								Move( &rp, TmpOffset, YOffset );
								PolyDrawArray[0].x = TmpOffset+ndo->ndo_SelectedImage->Width+1;
								PolyDrawArray[0].y = YOffset;
								PolyDrawArray[1].x = TmpOffset+ndo->ndo_SelectedImage->Width+1;
								PolyDrawArray[1].y = YOffset+ndo->ndo_SelectedImage->Height+1;
								PolyDrawArray[2].x = TmpOffset;
								PolyDrawArray[2].y = YOffset+ndo->ndo_SelectedImage->Height+1;
								PolyDrawArray[3].x = TmpOffset;
								PolyDrawArray[3].y = YOffset;
								PolyDraw( &rp, 4, (WORD *)PolyDrawArray );

							}

							/*** Computes the ConvTable for the Selected image */

							DPUT(("\nConvTable for Selected image\n"));

							for ( i=0; i<ndo->ndo_SelectedImage->NumColors; i++ ) {

								ConvTable[i] = MyObtainBestPen( *((struct ColorRegister *) &(ndo->ndo_SelectedImage->Palette[i*3])),
								                                cmap, ncolors );
								DPUT(("ConvTable[%ld] = %ld for color = %02lx%02lx%02lx\n",
								    (LONG) i, (LONG) ConvTable[i],
								    (LONG) ndo->ndo_SelectedImage->Palette[i*3],
								    (LONG) ndo->ndo_SelectedImage->Palette[i*3+1],
								    (LONG) ndo->ndo_SelectedImage->Palette[i*3+2] ));

							}


							/*** Copy the Selected image */

							if ( !var_Border ) {
								TmpOffset--;
								YOffset--;
							}
							MineWriteChunkyPixels( &rp, TmpOffset+1, YOffset+1,
							                       TmpOffset+ndo->ndo_SelectedImage->Width,
							                       YOffset+ndo->ndo_SelectedImage->Height,
							                       ndo->ndo_SelectedImage->ChunkyData,
							                       ndo->ndo_SelectedImage->Width,
							                       ConvTable,
							                       ndo->ndo_SelectedImage->Flags,
							                       cb );

						}


						if (success)
						{
							/* Set the attributes */
							setdtattrs (cb, o,
								DTA_ObjName,		title,
								DTA_NominalHoriz,	bmhd->bmh_Width,
								DTA_NominalVert,	bmhd->bmh_Height,
								GA_Width,			bmhd->bmh_Width,
								GA_Height,			bmhd->bmh_Height,
								PDTA_BitMap,		bm,
								PDTA_ModeID,		modeid,
								TAG_DONE);
						}
						else
						{
							/* Free the bitmap on failure */
							FreeBitMap(bm);
						}

						FreeVec( ConvTable );

					}

				}

				FreeNewDiskObject( ndo );

			}

		}

		}

		FreeVec( fname );

	}


	DPUT(("\nReturning with success=%ld\n", (LONG)success));

	LEAVING;

	return (success);

}

/*****************************************************************************/

void MineWriteChunkyPixels( struct RastPort *rp, LONG xstart, LONG ystart,
                            LONG xstop, LONG ystop, UBYTE *array, LONG bytesperrow,
                            UBYTE *convtable, LONG flags,
                            struct ClassBase *cb ) {

	/* Actually, bytesperrow is ignored */

	LONG		x,y;


	for ( y=ystart; y<=ystop; y++ ) {

		for ( x=xstart; x<=xstop; x++ ) {

			SetAPen( rp, convtable[*array++] );
			WritePixel( rp, x, y );

		}

	}

}

/*****************************************************************************/

UBYTE MyObtainBestPen( struct ColorRegister color, struct ColorRegister *cmap,
                       UWORD ncol ) {

	UWORD			red, green, blue;
	UWORD			i;
	UWORD			pen;
	UWORD			prox;
	UWORD			oldprox = 0xFFFF;

	red   = color.red   << 4;
	green = color.green << 4;
	blue  = color.blue  << 4;


	for ( i=0; i<ncol; i++ ) {

		if ( (prox = TestProximity( cmap->red << 4 , cmap->green << 4 , cmap->blue << 4 ,
		                            red, green, blue ) ) == 0 ) {
			return (UBYTE)i;
		}

		if ( prox < oldprox ) {
			oldprox = prox;
			pen = i;
		}

		cmap++;

	}

	return (UBYTE)pen;

}

/*****************************************************************************/

Object *GetFileColorMap( STRPTR filename, struct ColorRegister **pt_cmap, ULONG *pt_ncolors,
                         struct ClassBase *cb ) {

	struct DataType	*dt;
	BPTR					fl;
	Object				*obj = NULL;

	if ( fl = Lock( filename, ACCESS_READ ) ) {

		if ( dt = ObtainDataTypeA( DTST_FILE, (APTR)fl, NULL ) ) {

			if ( dt->dtn_Header->dth_GroupID == GID_PICTURE ) {
				ReleaseDataType( dt );

				/*** At this point we are sure that 'filename' is a picture */

				/* get the picture object */
				if ( obj = NewDTObject( filename,
				                        DTA_SourceType, DTST_FILE,
				                        DTA_GroupID, GID_PICTURE,
				                        PDTA_Remap, FALSE,
				                        TAG_DONE) ) {

					if ( GetDTAttrs( obj, PDTA_ColorRegisters, pt_cmap,
						                      PDTA_NumColors, pt_ncolors ) != 2 ) {
						DisposeDTObject( obj );
						obj = NULL;
						*pt_cmap = NULL;
					}

					DPUT(("ColorMap got: ncolors = %ld, cmap = %lx\n", (LONG) *pt_ncolors, (LONG) *pt_cmap ));

				}

			}
			else
				ReleaseDataType( dt );

		}

		UnLock( fl );
	}

	return obj;

}

/*****************************************************************************/

void FreeFileColorMap( Object *obj, struct ClassBase *cb ) {

	DisposeDTObject( obj );

}

