/****************************************************************************
 *									    *
 *			     Palette Requester				    *
 *									    *
 *		     (c) Copyright 1989 Jonathan Potter                     *
 *									    *
 * This program is freely redistributable, although all rights to it remain *
 * with the author. It may be used freely in any program as long as this    *
 * notice remains intact, however, if you do this, please mention the	    *
 * author in the program. If you wish to use this in a commercial program   *
 * of any kind, you must register with a $15 donation.			    *
 * Please send donations, bug reports, comments and suggestions to :	    *
 *									    *
 *				Jonathan Potter 			    *
 *				3 William Street			    *
 *				Clarence Park 5034			    *
 *				South Australia 			    *
 *				Australia				    *
 *									    *
 *			   Ph : (08) 2932788  home                          *
 *									    *
 ****************************************************************************/

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <libraries/dosextens.h>
#include <graphics/gfxbase.h>
#include "PaletteReq.h"

#define RED 32
#define BLUE 33
#define GREEN 34
#define CRESET 35
#define CCANCEL 36
#define COK 37
#define USER 38
#define SPREAD 39
#define COPY 40
#define HUE 41
#define SAT 42
#define LUM 43

#define INITREG(n)     (GetRGB4(cvp->ColorMap,n))
#define REDREG(n)      ((n>>8)&0xf)
#define GREENREG(n)    ((n>>4)&0xf)
#define BLUEREG(n)     (n&0xf)

static int redtable[32], greentable[32], bluetable[32];
static char values[4];

static struct IntuiText
	coktext={1,0,JAM1,41,1,NULL,(UBYTE *)"OKAY",NULL},
	canceltext={1,0,JAM1,33,1,NULL,(UBYTE *)"CANCEL",NULL},
	usertext={1,0,JAM1,0,1,NULL,(UBYTE *)"",NULL},
	resettext={1,0,JAM1,37,1,NULL,(UBYTE *)"RESET",NULL},
	spreadtext={1,0,JAM1,33,1,NULL,(UBYTE *)"SPREAD",NULL},
	copytext={1,0,JAM1,41,1,NULL,(UBYTE *)"COPY",NULL},
	RText={1,0,JAM1,-15,1,NULL,"R",NULL},
	GText={1,0,JAM1,-15,1,NULL,"G",NULL},
	BText={1,0,JAM1,-15,1,NULL,"B",NULL},
	HText={1,0,JAM1,106,1,NULL,"H",NULL},
	SText={1,0,JAM1,106,1,NULL,"S",NULL},
	LText={1,0,JAM1,106,1,NULL,"L",NULL},
	valstext={0,1,JAM2,0,0,NULL,values,NULL};

static short border1_xy[]={
	0,0,115,0,115,10,0,10,0,0};
static struct Border border1={
	-1,-1,1,0,JAM1,5,border1_xy,NULL};
static short border2_xy[]={
	0,0,239,0,239,10,0,10,0,0};
static struct Border border2={
	-1,-1,1,0,JAM1,5,border2_xy,NULL};

static struct PropInfo
	RProp={AUTOKNOB|FREEHORIZ,0,0,1<<12},
	GProp={AUTOKNOB|FREEHORIZ,0,0,1<<12},
	BProp={AUTOKNOB|FREEHORIZ,0,0,1<<12},
	HProp={AUTOKNOB|FREEHORIZ,0,0,1<<12},
	SProp={AUTOKNOB|FREEHORIZ,0,0,1<<12},
	LProp={AUTOKNOB|FREEHORIZ,0,0,1<<12};

static struct Image RImage, GImage, BImage, HImage, SImage, LImage,
	WCol={0,0,235,8,1,NULL,0,1,NULL};

static struct Gadget
	cokgadget={
		NULL,9,118,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
		(APTR)&border1,NULL,&coktext,NULL,NULL,COK,NULL},
	cancelgadget={
		&cokgadget,133,118,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
		(APTR)&border1,NULL,&canceltext,NULL,NULL,CCANCEL,NULL},
	usergadget={
		&cancelgadget,133,105,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
		(APTR)&border1,NULL,&usertext,NULL,NULL,USER,NULL},
	resetgadget={
		&usergadget,9,105,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
		(APTR)&border1,NULL,&resettext,NULL,NULL,CRESET,NULL},
	spreadgadget={
		&resetgadget,133,92,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
		(APTR)&border1,NULL,&spreadtext,NULL,NULL,SPREAD,NULL},
	copygadget={
		&spreadgadget,9,92,114,9,GADGHCOMP,RELVERIFY,BOOLGADGET,
		(APTR)&border1,NULL,&copytext,NULL,NULL,COPY,NULL},
	Lum={
		&copygadget,132,79,100,10,GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
		(APTR)&LImage,NULL,&LText,NULL,(APTR)&LProp,LUM,NULL},
	Sat={
		&Lum,132,67,100,10,GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
		(APTR)&SImage,NULL,&SText,NULL,(APTR)&SProp,SAT,NULL},
	Hue={
		&Sat,132,55,100,10,GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
		(APTR)&HImage,NULL,&HText,NULL,(APTR)&HProp,HUE,NULL},
	Red={
		&Hue,24,55,100,10,GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
		(APTR)&RImage,NULL,&RText,NULL,(APTR)&RProp,RED,NULL},
	Green={
		&Red,24,67,100,10,GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
		(APTR)&GImage,NULL,&GText,NULL,(APTR)&GProp,GREEN,NULL},
	Blue={
		&Green,24,79,100,10,GADGHNONE|GADGIMAGE,RELVERIFY|GADGIMMEDIATE,PROPGADGET,
		(APTR)&BImage,NULL,&BText,NULL,(APTR)&BProp,BLUE,NULL};

static struct NewWindow colwin={
	40,23,255,131,0,1,CLOSEWINDOW|GADGETUP|GADGETDOWN|MOUSEBUTTONS|RAWKEY,
	WINDOWDRAG|WINDOWCLOSE|ACTIVATE|SMART_REFRESH|RMBTRAP,
	&Blue,NULL,"Palette",NULL,NULL,0,0,0,0,CUSTOMSCREEN};

static struct Window *PWindow;
static struct IntuiMessage *PMsg;
static struct ViewPort *cvp;
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;
static ULONG colb;

palette_request(win,winx,winy,hail,user,depth)
struct Window *win;
int winx, winy;
char *hail;
char *user;
int depth;
{
	int col,reg,i,Class,Code,GadgetID,x,y,last,first,sw,sh;
	if (!IntuitionBase) return(NO_INTUITION);
	if (!GfxBase) return(NO_GRAPHICS);
	if (!win) { colwin.Type=WBENCHSCREEN; sw=640; sh=200; }
	else {
		colwin.Screen=win->WScreen;
		sw=win->WScreen->Width; sh=win->WScreen->Height;
	}
	if (hail) colwin.Title=hail;
	if (user) {
		if (strlen(user)>14) user[14]='\0';
		usertext.IText=user;
		usertext.LeftEdge=(115-(strlen(user)*8))/2;
	}
	else {
		resetgadget.NextGadget=&cancelgadget;
		resetgadget.Width=238;
		resetgadget.GadgetRender=(APTR)&border2;
		resettext.LeftEdge=99;
	}
	if (winx>-1) {
		colwin.LeftEdge=winx;
		if (winx+255>sw)
			colwin.LeftEdge=sw-255;
	}
	if (winy>-1) {
		colwin.TopEdge=winy;
		if (winy+131>sh)
			colwin.TopEdge=sh-131;
	}
	PWindow=(struct Window *) OpenWindow(&colwin);
	if (!PWindow) return(OPENWINDOW_FAILED);
	cvp=&(PWindow->WScreen->ViewPort);
	if (!depth) depth=2;
	if (depth<1) depth=1; if (depth>5) depth=5;
	drawersquares(depth);

	InitTable(depth);
	col=1; WCol.PlaneOnOff=col;
	valstext.FrontPen=col^(power(2,depth)-1); valstext.BackPen=1;
	DrawImage(PWindow->RPort,&WCol,10,45);
	PosPots(depth,col);
	PosHSLPots(depth,col);
	FOREVER {
		Wait(1<<PWindow->UserPort->mp_SigBit);
		while (PMsg=(struct IntuiMessage *) GetMsg(PWindow->UserPort)) {
			Class=PMsg->Class; Code=PMsg->Code;
			if (Class==GADGETUP || Class==GADGETDOWN)
				GadgetID=((struct Gadget *) PMsg->IAddress)->GadgetID;
			ReplyMsg((struct Message *) PMsg);
			switch (Class) {
				case CLOSEWINDOW:
					for (reg=0;reg<(power(2,depth));reg++)
						SetRGB4(cvp,reg,redtable[reg],greentable[reg],bluetable[reg]);
					CloseWindow(PWindow);
					return(FALSE);
					break;
				case MOUSEBUTTONS:
					if (Code!=SELECTDOWN) break;
					x=PWindow->MouseX; y=PWindow->MouseY;
					if (x<12 || x>243 || y<15 || y>40) break;
					if (col==ReadPixel(PWindow->RPort,x,y)) break;
					col=ReadPixel(PWindow->RPort,x,y);
					WCol.PlaneOnOff=col;
					DrawImage(PWindow->RPort,&WCol,10,45);
					PosPots(depth,col);
					PosHSLPots(depth,col);
					break;
				case GADGETDOWN:
					switch (GadgetID) {
						case RED:
						case GREEN:
						case BLUE:
							SetCols(depth,col);
							while (!(PMsg=(struct IntuiMessage *) GetMsg(PWindow->UserPort)))
								SetCols(depth,col);
							ReplyMsg((struct Message *) PMsg);
							SetCols(depth,col);
							break;
						case HUE:
						case SAT:
						case LUM:
							SetHSLCols(depth,col);
							while (!(PMsg=(struct IntuiMessage *) GetMsg(PWindow->UserPort)))
								SetHSLCols(depth,col);
							ReplyMsg((struct Message *) PMsg);
							SetHSLCols(depth,col);
							break;

					}
					break;
				case RAWKEY:
					if (Code!=0x45) break;
					GadgetID=CRESET; Class=GADGETUP;
				case GADGETUP:
					switch (GadgetID) {
						case CRESET:
							for (reg=0;reg<(power(2,depth));reg++) {
								SetRGB4(cvp,reg,redtable[reg],greentable[reg],bluetable[reg]);
								if (reg==col) {
									PosPots(depth,col);
									PosHSLPots(depth,col);
								}
							}
							break;
						case USER:
							if (!user) break;
							CloseWindow(PWindow);
							return(5);
						case COK:
							CloseWindow(PWindow);
							return(TRUE);
						case CCANCEL:
							for (reg=0;reg<(power(2,depth));reg++)
								SetRGB4(cvp,reg,redtable[reg],greentable[reg],bluetable[reg]);
							CloseWindow(PWindow);
							return(FALSE);
						case COPY:
							FOREVER {
								Wait(1<<PWindow->UserPort->mp_SigBit);
								if (PMsg=(struct IntuiMessage *) GetMsg(PWindow->UserPort)) {
									Class=PMsg->Class; Code=PMsg->Code;
									ReplyMsg((struct Message *) PMsg);
									if (Class!=MOUSEBUTTONS && Code!=SELECTDOWN) continue;
									x=PWindow->MouseX; y=PWindow->MouseY;
									if (x<12 || x>243 || y<15 || y>40) continue;
									GadgetID=ReadPixel(PWindow->RPort,x,y);
									if (GadgetID>-1 && GadgetID<32) break;
								}
							}
							colb=INITREG(col);
							SetRGB4(cvp,GadgetID,REDREG(colb),GREENREG(colb),BLUEREG(colb));
							col=GadgetID;
							WCol.PlaneOnOff=col;
							DrawImage(PWindow->RPort,&WCol,10,45);
							PosPots(depth,col);
							PosHSLPots(depth,col);
						case SPREAD:
							FOREVER {
								Wait(1<<PWindow->UserPort->mp_SigBit);
								if (PMsg=(struct IntuiMessage *) GetMsg(PWindow->UserPort)) {
									Class=PMsg->Class; Code=PMsg->Code;
									ReplyMsg((struct Message *) PMsg);
									if (Class!=MOUSEBUTTONS && Code!=SELECTDOWN) continue;
									x=PWindow->MouseX; y=PWindow->MouseY;
									if (x<12 || x>243 || y<15 || y>40) continue;
									GadgetID=ReadPixel(PWindow->RPort,x,y);
									if (GadgetID>-1 && GadgetID<32) break;
								}
							}
							first=col; last=GadgetID;
							if (first>last) {
								i=first; first=last; last=i;
							}
							if (first>=last-1) break;
							dospread(first,last);
							col=GadgetID;
							WCol.PlaneOnOff=col;
							DrawImage(PWindow->RPort,&WCol,10,45);
							PosPots(depth,col);
							PosHSLPots(depth,col);
							break;
						default:
							break;
					}
			}
		}
	}
}

static PosPots(depth,col)
int depth,col;
{
	ULONG hpr,hpg,hpb;
	colb=INITREG(col);
	hpr=REDREG(colb)*0x1111;
	hpg=GREENREG(colb)*0x1111;
	hpb=BLUEREG(colb)*0x1111;
	if (RProp.HorizPot==hpr && GProp.HorizPot==hpg && BProp.HorizPot==hpb)
		return(0);
	NewModifyProp(&Red,PWindow,NULL,FREEHORIZ|AUTOKNOB,hpr,0,1<<12,0,1);
	NewModifyProp(&Green,PWindow,NULL,FREEHORIZ|AUTOKNOB,hpg,0,1<<12,0,1);
	NewModifyProp(&Blue,PWindow,NULL,FREEHORIZ|AUTOKNOB,hpb,0,1<<12,0,1);
	ShowVals(depth,REDREG(colb),GREENREG(colb),BLUEREG(colb),col);
	return(0);
}

static PosHSLPots(depth,col)
int depth, col;
{
	colb=INITREG(col);
	RGBToHSL(colb,&HProp.HorizPot,&SProp.HorizPot,&LProp.HorizPot);
	NewModifyProp(&Hue,PWindow,NULL,FREEHORIZ|AUTOKNOB,HProp.HorizPot,0,1<<12,0,1);
	NewModifyProp(&Sat,PWindow,NULL,FREEHORIZ|AUTOKNOB,SProp.HorizPot,0,1<<12,0,1);
	NewModifyProp(&Lum,PWindow,NULL,FREEHORIZ|AUTOKNOB,LProp.HorizPot,0,1<<12,0,1);
	return(0);
}

static SetHSLCols(depth,col)
int depth,col;
{
	ULONG rgb, red, green, blue;
	rgb=(ULONG) HSLToRGB(HProp.HorizPot,SProp.HorizPot,LProp.HorizPot);
	red=rgb>>8; green=rgb>>4; blue=rgb;
	SetRGB4(cvp,col,red,green,blue);
	PosPots(depth,col);
	return(0);
}

static SetCols(depth,col)
int depth;
int col;
{
	USHORT re,gr,bl;
	re=RProp.HorizPot/0x1111;
	gr=GProp.HorizPot/0x1111;
	bl=BProp.HorizPot/0x1111;
	colb=INITREG(col);
	if (re==REDREG(colb) && gr==GREENREG(colb) && bl==BLUEREG(colb)) return(0);
	SetRGB4(cvp,col,re,gr,bl);
	ShowVals(depth,re,gr,bl,col);
	PosHSLPots(depth,col);
	return(0);
}

static InitTable(depth)
int depth;
{
	int reg;
	for (reg=0;reg<(power(2,depth));reg++) {
		colb=INITREG(reg);
		redtable[reg]=REDREG(colb);
		greentable[reg]=GREENREG(colb);
		bluetable[reg]=BLUEREG(colb);
	}
	return(0);
}

static ShowVals(depth,re,gr,bl,col)
int depth,re,gr,bl,col;
{
	values[0]=dostr(re);
	values[1]=dostr(gr);
	values[2]=dostr(bl);
	values[3]='\0';
	valstext.FrontPen=col^(power(2,depth)-1);
	valstext.BackPen=col;
	PrintIText(PWindow->RPort,&valstext,115,45);
	return(0);
}

static power(base,n)
int base,n;
{
	int p;
	for (p=1;n>0;--n) p*=base;
	return(p);
}

static drawersquares(depth)
int depth;
{
	int w,h,ox,x,y,i;
	w=235/(power(2,depth)); h=25; ox=12;
	if (depth==4) { w=29; h=12; ox=13; }
	if (depth==5) { w=14; h=12; ox=17; }
	i=0; x=ox; y=15;
	while (x<255 && y<45) {
		SetAPen(PWindow->RPort,i);
		RectFill(PWindow->RPort,x,y,x+w,y+h);
		++i;
		x+=w;
		if (i>=power(2,depth)) break;
		if (depth>3 && i==(power(2,(depth-1)))) {
			x=ox; y+=h;
		}
	}
	return(0);
}

static dospread(first,last)
int first,last;
{
	int redst,greenst,bluest,red1,green1,blue1,red2,green2,blue2,
		redw,greenw,bluew,i;
	colb=INITREG(first);
	red1=REDREG(colb); green1=GREENREG(colb); blue1=BLUEREG(colb);
	colb=INITREG(last);
	red2=REDREG(colb); green2=GREENREG(colb); blue2=BLUEREG(colb);
	redst=((red2-red1)<<8)/(last-first);
	greenst=((green2-green1)<<8)/(last-first);
	bluest=((blue2-blue1)<<8)/(last-first);
	for (i=first+1;i<last;i++) {
		redw=red1+(((redst*(i-first))+128)>>8);
		greenw=green1+(((greenst*(i-first))+128)>>8);
		bluew=blue1+(((bluest*(i-first))+128)>>8);
		SetRGB4(cvp,i,redw,greenw,bluew);
	}
	return(0);
}

/* HSL <-> RGB routines
   adapted from the Amiga Programmers' Suite Book 1
      by Robert J. Mical

------------------------------------------------------------------------------

 * *** hsl.c ****************************************************************
 *
 * ColorWindow Routine	--  HSL Translation Routines
 *     from Book 1 of the Amiga Programmers' Suite by RJ Mical
 *
 * Copyright (C) 1986, 1987, Robert J. Mical
 * All Rights Reserved.
 *
 * Any or all of this code can be used in any program as long as this
 * entire copyright notice is retained, ok?  Thanks.
 *
 * The Amiga Programmer's Suite Book 1 is copyrighted but freely distributable.
 * All copyright notices and all file headers must be retained intact.
 * The Amiga Programmer's Suite Book 1 may be compiled and assembled, and the
 * resultant object code may be included in any software product.  However, no
 * portion of the source listings or documentation of the Amiga Programmer's
 * Suite Book 1 may be distributed or sold for profit or in a for-profit
 * product without the written authorization of the author, RJ Mical.
 *
 * HISTORY	NAME		DESCRIPTION
 * -----------	--------------	--------------------------------------------
 * 3 Jan 87	RJ >:-{)*	Clean-up for release
 * 27 Feb 86	=RJ Mical=	Modified these routines for Zaphod
 * January 86	=RJ=		Modified the originals for Mandelbrot
 * Late 85	=RJ=		Created the color window for Graphicraft
 *
 * *************************************************************************

-----------------------------------------------------------------------------

*/


static HSLToRGB(hue,sat,lum)
USHORT hue, sat, lum;
{
	ULONG red,green,blue,rdiff,gdiff,bdiff,foo,ris,fall,inv;

	foo=hue/0x2aab; ris=(hue-(foo*0x2aab))*6; fall=0xffff-ris;
	red=0xffff; green=0xffff; blue=0xffff;
	switch (foo) {
		case 0:
			green=ris; blue=0;
			break;
		case 1:
			red=fall; blue=0;
			break;
		case 2:
			red=0; blue=ris;
			break;
		case 3:
			red=0; green=fall;
			break;
		case 4:
			red=ris; green=0;
			break;
		case 5:
			green=0; blue=fall;
			break;
	}

	red=(red*lum)>>16; green=(green*lum)>>16; blue=(blue*lum)>>16;
	inv=0xffff-sat;
	rdiff=lum-red; red=red+((rdiff*inv)>>16);
	gdiff=lum-green; green=green+((gdiff*inv)>>16);
	bdiff=lum-blue; blue=blue+((bdiff*inv)>>16);
	red=(red>>12)&0xf; green=(green>>12)&0xf; blue=(blue>>12)&0xf;
	return((SHORT)((red<<8)|(green<<4)|(blue)));
}

static RGBToHSL(rgb,rhue,rsat,rlum)
USHORT rgb;
USHORT *rhue,*rsat,*rlum;
{
	LONG min,max,hue,sat,lum,diff,rpart,gpart,bpart,rwork,gwork,bwork;

	rwork=((rgb>>8)&0xf)*0x111; gwork=((rgb>>4)&0xf)*0x111; bwork=(rgb&0xf)*0x111;
	if (rwork<gwork) min=rwork; else min=gwork; if (bwork<min) min=bwork;
	if (rwork>gwork) max=rwork; else max=gwork; if (bwork>max) max=bwork;
	lum=max; lum<<=4; diff=max-min;
	if (max) {
		sat=(diff<<16)/max; if (sat>0xffff) sat=0xffff;
	}
	else sat=0;
	if (sat==0) hue=0;
	else {
		rpart=(((max-rwork)<<16)/diff)>>4;
		gpart=(((max-gwork)<<16)/diff)>>4;
		bpart=(((max-bwork)<<16)/diff)>>4;
		if (rwork==max) hue=bpart-gpart;
		else if (gwork==max) hue=0x2000+rpart-bpart;
		else if (bwork==max) hue=0x4000+gpart-rpart;
		if (hue<0) hue+=0x6000; hue=(hue*2667)/1000;
	}
	*rhue=hue; *rsat=sat; *rlum=lum;
	return(0);
}

/*--------------------------------------------------------------------------*/

dostr(val)
int val;
{
	if (val<10) return(val+48);
	return(val+87);
}
