/*
 *  palettepanel.c - paletteWindow class
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "prefs.h"
#include "macros.h"

/*
 * methods
 */

#define MUIM_paletteWindow_Finish (TAGBASE_AMLAUKKA+0x0011)
#define MUIM_paletteWindow_Import (TAGBASE_AMLAUKKA+0x0012)
#define MUIM_paletteWindow_Save (TAGBASE_AMLAUKKA+0x0013)

/*
 * internal structures
 */

struct MUIP_paletteWindow_Table
{
	UBYTE*  r;
	UBYTE*  g;
	UBYTE*  b;

	UBYTE*  pal;
	ULONG   epsilon;
};

struct MUIP_paletteWindow_Finish
{
	ULONG   method_id;
	LONG    level;
};

/*
 * function prototypes
 */

static ULONG CalcTable(struct MUIP_paletteWindow_Table*);
static ULONG paletteWindow_Save(struct IClass*, Object*,Msg);
static ULONG paletteWindow_Import(struct IClass*, Object*,Msg);
static ULONG paletteWindow_NEW(struct IClass*, Object*, struct opSet*);
static ULONG paletteWindow_Finish(struct IClass*, Object*, struct MUIP_paletteWindow_Finish*);

/*
 * functions
 */


static ULONG
paletteWindow_Import(struct IClass* cl, Object* obj,Msg msg)
{
	char* name;
	struct paletteWindowData* data = INST_DATA(cl, obj);
	BPTR file;
	ALIGNED struct FileInfoBlock fib;

	name = getfilename(obj, "Select a raw palette file", NULL, "#?.pal", FALSE);
	if (name && *name)
	{
		file = Open(name, MODE_OLDFILE);
		if (file)
		{
			if (ExamineFH(file, &fib))
			{
				if (fib.fib_Size == 256)
				{
					Read(file, data->rgb, fib.fib_Size);
					set(data->ST_Name, MUIA_String_Contents, FilePart(name));
				}
				else
					MUI_Request((APTR)xget(obj, MUIA_ApplicationObject),
								NULL, 0, NULL, "Okay",
								"This file: %s is not correct in size.",
								name);
			}
			Close(file);
		}
		else
			MUI_Request((APTR)xget(obj, MUIA_ApplicationObject),
						NULL, 0, NULL, "Okay",
						"Cannot open the palette file: %s.",
						name);
	}

	return 0;
}

static ULONG
paletteWindow_Save(struct IClass* cl, Object* obj,Msg msg)
{
	struct MUIP_paletteWindow_Table table;
	struct paletteWindowData* data = INST_DATA(cl, obj);
	BPTR file;
	char* name;

	name = getfilename(obj, "Select a filename for the table", NULL, "#?", TRUE);
	if (name && *name)
	{
		file = Open(name, MODE_NEWFILE);
		if (file)
		{
			set((APTR)xget(obj, MUIA_ApplicationObject), MUIA_Application_Sleep, TRUE);

			table.r = AllocVec(1<<15, MEMF_ANY);
			table.g = AllocVec(1<<15, MEMF_ANY);
			table.b = AllocVec(1<<15, MEMF_ANY);
			if (table.r && table.g && table.b)
			{
				table.epsilon = atoi((char*)xget(data->ST_Epsilon, MUIA_String_Acknowledge));
				if (table.epsilon > (1 << 31) - 1)
					table.epsilon = (1 << 31) - 1;

				table.pal = (UBYTE*)data->rgb;
				CalcTable(&table);
				Write(file, table.r, 32768);
				Write(file, table.g, 32768);
				Write(file, table.b, 32768);
			}

			set(data->ST_CachedName, MUIA_String_Contents, name);

			if (table.r)
				FreeVec(table.r);
			if (table.g)
				FreeVec(table.g);
			if (table.b)
				FreeVec(table.b);

			set((APTR)xget(obj, MUIA_ApplicationObject), MUIA_Application_Sleep, FALSE);

			Close(file);
		}
		else
			MUI_Request((APTR)xget(obj, MUIA_ApplicationObject),
						NULL, 0, NULL, "Okay",
						"Cannot create the table file: %s.",
						name);

	}

	return 0;
}


#define MAGICMUL (255*256/31)
#define MAGICMUL2 (63*256/31)

static ULONG
CalcTable(struct MUIP_paletteWindow_Table* msg)
{
	int i, j, r, g, b, dr, dg, db, dist, big, bignum = 0;

	for( i = 0 ; i < 32768 ; i++)
	{
		r = (i >> 10) * MAGICMUL >> 8;
		g = ((i >> 5 ) & 0x1f) * MAGICMUL >> 8;
		b = (i & 0x1f) * MAGICMUL >> 8;
		big = (1 << 31) - 1;

		for( j = 0 ; j < 64 ; j++)
		{
			dr = (r - msg->pal[j*4+1])*2;
			dg = (g - msg->pal[j*4+2])*3;
			db = b - msg->pal[j*4+3];

			dist = dr*dr + dg*dg + db*db;

			if(dist < big)
			{
				bignum = j;
				big = dist;
			}
		}

		if(big < msg->epsilon)
		{
			msg->r[i] = bignum;
			msg->g[i] = bignum;
			msg->b[i] = bignum;
		}
		else
		{
			msg->r[i] = 0x80 | ((i>>10) * MAGICMUL2 >> 8);
			msg->g[i] = 0xc0 | (((i>>5) & 0x1f) * MAGICMUL2 >> 8);
			msg->b[i] = 0x40 | ((i & 0x1f) * MAGICMUL2 >> 8);
		}
	}

	return 0;
}

static ULONG
paletteWindow_NEW(struct IClass* cl, Object* obj, struct opSet* msg)
{
	struct paletteWindowData* data;
	struct paletteEntry* pal;

	Object* BT_Use;
	Object* BT_Cancel;
	Object* BT_Import;
	Object* BT_CalcTable;
	Object* PA_Originator;
	Object* ST_Name;
	Object* ST_CachedName;
	Object* ST_Epsilon;

	char epsilon[10];
	int i;

	pal = (struct paletteEntry*)GetTagData(MUIA_paletteWindow_Palette, NULL, msg->ops_AttrList);
	PA_Originator = (Object*)GetTagData(MUIA_paletteWindow_Orig, NULL, msg->ops_AttrList);

	sprintf(epsilon,"%d", pal->epsilon);

	obj = (Object*)DoSuperNew(cl, obj,
			MUIA_Window_Title, "Editing palette...",
			MUIA_Window_ID, MAKE_ID('P','A','L','E'),
			WindowContents, VGroup,
				Child, HGroup,
					Child, KeyLabel2("Name:",'n'),
					Child, ST_Name = StringObject, StringFrame,
						MUIA_ControlChar, 'n',
						MUIA_CycleChain, TRUE,
						MUIA_String_AdvanceOnCR, TRUE,
						MUIA_String_Contents, pal->name,
						MUIA_String_MaxLen, 40,
					End,
					Child, BT_Import = MakeButton("_Import"),
				End,
				Child, HGroup,
					Child, KeyLabel2("Epsilon:",'e'),
					Child, ST_Epsilon = StringObject, StringFrame,
						MUIA_ControlChar, 'e',
						MUIA_CycleChain, TRUE,
						MUIA_String_AdvanceOnCR, TRUE,
						MUIA_String_Contents, epsilon,
						MUIA_String_Accept, "0123456789",
						MUIA_String_MaxLen, 9,
					End,
					Child, KeyLabel2("Cached:",'a'),
					Child, ST_CachedName = StringObject, StringFrame,
						MUIA_ControlChar, 'a',
						MUIA_CycleChain, TRUE,
						MUIA_String_AdvanceOnCR, TRUE,
						MUIA_String_Contents, pal->filename,
						MUIA_String_MaxLen, 512,
					End,
					Child, BT_CalcTable = MakeButton("_Save table"),
				End,
				Child, MUI_MakeObject(MUIO_HBar, 2),
				Child, HGroup, MUIA_Group_SameSize, TRUE,
					Child, BT_Use = MakeButton("_Use"),
					Child, HSpace(0),
					Child, BT_Cancel = MakeButton("_Cancel"),
				End,
			End,
		TAG_MORE, msg->ops_AttrList);

	if(!obj)
		return 0;

	data = INST_DATA(cl, obj);

	data->PA_Orig = PA_Originator;
	data->pal = pal;
	data->ST_Name = ST_Name;
	data->ST_CachedName = ST_CachedName;
	data->ST_Epsilon = ST_Epsilon;
	data->changed = FALSE;

	for (i = 0; i < 64 ; i++)
		data->rgb[i] = pal->rgb[i];

	DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, obj, 2, MUIM_paletteWindow_Finish, 0);
	DoMethod(BT_Use, MUIM_Notify, MUIA_Pressed, FALSE, obj, 2, MUIM_paletteWindow_Finish, 1);
	DoMethod(BT_Cancel, MUIM_Notify, MUIA_Pressed, FALSE, obj, 2, MUIM_paletteWindow_Finish, 0);
	DoMethod(BT_Import, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_paletteWindow_Import);
	DoMethod(BT_CalcTable, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_paletteWindow_Save);

	set(BT_Import, MUIA_ShortHelp, "Import a new base palette. The file must\n"
								   "be 256 bytes long and contain 64 raw\n"
								   "palette entries");
	set(BT_CalcTable, MUIA_ShortHelp, "Save and calculate the lookup table.\n"
									  "This file will be 96 kB long and needed\n"
									  "by the 15 bit mode.");
	set(BT_Use, MUIA_ShortHelp, "Approve the changes to this palette entry.");
	set(BT_Cancel, MUIA_ShortHelp, "Destroy the changes you have made.");

	return (ULONG)obj;
}

static ULONG
paletteWindow_Finish(struct IClass* cl, Object* obj, struct MUIP_paletteWindow_Finish* msg)
{
	int i;
	struct paletteWindowData* data = INST_DATA(cl, obj);

	if (msg->level >= 1)
	{
		data->pal->epsilon = atoi((char*)xget(data->ST_Epsilon, MUIA_String_Acknowledge));
		if (data->pal->epsilon > (1 << 31) - 1)
			data->pal->epsilon = (1 << 31) - 1;

		for (i = 0; i < 64 ; i++)
			data->pal->rgb[i] = data->rgb[i];

		if (data->pal->name)
			FreeVec(data->pal->name);
		data->pal->name = AllocVec( strlen((char*)xget(data->ST_Name, MUIA_String_Acknowledge))+1, MEMF_ANY);
		strcpy(data->pal->name, (char*)xget(data->ST_Name, MUIA_String_Acknowledge));

		if (data->pal->filename)
			FreeVec(data->pal->filename);
		data->pal->filename = AllocVec(strlen((char*)xget(data->ST_CachedName, MUIA_String_Acknowledge))+1, MEMF_ANY);
		strcpy(data->pal->filename, (char*)xget(data->ST_CachedName, MUIA_String_Acknowledge));
	}

	DoMethod((Object*)xget(obj, MUIA_ApplicationObject), MUIM_Application_PushMethod, data->PA_Orig, 2, MUIM_evdPanel_Finish, obj);

	return 0;
}

ULONG REGARGS SAVEDS
paletteWindow_Dispatcher(REG(a0, struct IClass* cl),REG(a2, Object* obj),REG(a1, Msg msg))
{
	switch(msg->MethodID)
	{
		case OM_NEW:            return paletteWindow_NEW(cl, obj, (struct opSet*)msg);
		case MUIM_paletteWindow_Finish: return paletteWindow_Finish(cl, obj, (struct MUIP_paletteWindow_Finish*)msg);
		case MUIM_paletteWindow_Import: return paletteWindow_Import(cl, obj, msg);
		case MUIM_paletteWindow_Save: return paletteWindow_Save(cl, obj, msg);
	}
	return DoSuperMethodA(cl, obj, msg);
}

