/* Copyright 2008 Jeffrey Lee
   This file is part of Pale.
   Pale 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 3 of the License, or
   (at your option) any later version.
   Pale 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 Pale.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "pale.h"
#include "loadsave.h"
#include "preview.h"
#include "sprfile.h"
#include "editor.h"
#include "version.h"

#include "/WoumInclude:lib/futil.h"
#include "/WoumInclude:lib/osspr.h"

//DEPPEND

/*
   Two main windows: Sprites and palettes
   Sprite window would contain list of all the sprites (including thumbnails), with an indication of which palette # each sprite uses. Would also have checkboxes/other to flag whether a particular sprite is being edited. Double-clicking a sprite will open a zoomed view of the sprite and palette editor.
   Palette window would list all the unique palettes. No checkboxes for editing; instead, double-clicking a palette will open the palette editor for that palette, as well as marking all the sprites that use that palette for editing.

   Palette tools:
   - Sprite link - toggles whether rearranging palette entries also rearranges pixels on the sprites
   - Gradient - click on two palette entries to have a gradient created between them
   - Edit - Select one or more palette entry and text boxes at top/bottom of editor window can be used to change the colour values. Can select multiple entries, drag them around to rearrange palette, etc.
   - Copy mode - Toggles copying of entries instead of moving
   - Undo - Supports undo? Just need a wrapper around all write operations, that makes backup of original memory value. May have to be careful if tools can rearrange the memory map of the sprite though.

   Sprite tools:
   - Right click selects colour?
   - Left click paints colour?
   - Also need tools to highlight which are the used colours, which colour is used where, etc. But should this all be in one combined toolbox, or should there be seperate sprite/palette toolboxes?
   - pixel remapping - use pointer/rectangle/etc. to do rule-based pixel changes

  typedef struct {
  	rgb pal[256]; // palette
  	int nsprites; // num. sprites to apply changes to
  	int *sprites; // list of sprite no's to apply changes to
  } peleditor;
  // note no palette id - this means that palettes can be split/joined without breaking back-references

  - On loading sprite file, Paint-esque window appears listing all sprites (and palettes they use?)
  - Double-clicking a sprite opens a palette editor window:
    - Palette window contains RGB boxes in bottom row where mask colour is?
      - No - mask colour can be set by clicking menu over palette entry; entry will then appear with ///'s drawn over it to show that it is the mask entry (and ///'s over it in the sprite view?)
    - May also contain the tools - since each window would need seperate undo?
    - Underneath/on side of palette window would be another window/pane, containing all the sprites that are using that palette
      - Sprites can be removed via menu
      - Sprites can be added by dragging in from main sprite list window
      - Double-clicking a sprite will open preview (default preview will be of the sprite that was double-clicked in the main window)

   TODO: Use message_prequit to do unsaved data warnings, not message_quit

   Schedule:
   - Undo support!
     - Per-file undo (not silly per-editor stuff)
     - Should each memory write go through the undo handler, or should it copy the sprite file and compare the old and new versions?
       - Former uses less RAM, latter may be better for large operations
     - How will it deal with the fact that palette editors aren't strongly bound to sprite palettes?
   - Work out what needs adding
     - Watch film to get new vehicle idea
     - Draw vehicle, run through ddconvert
     - Try using Pale to tidy up
     - Make list of features that would make it easier!
   - Extended features
     - Colour quantisation function for >256 colour sprites
     - 16 colour sprite editing
     - Saving of palette subsets (selections)
       - Maybe extend xfer_send window to contain 'selection', 'start at 0' boxes?
     - Handling of save request messages (so palettes can be 'saved' from one editor to another)
   - Fix ability to select both copy & swap
   - Add highlight to entry under pointer when dragging entries
   - Add ability to swap with a different palette
   - Add cursor controls for moving palette entries?
   - Bug in LimpX which causes it to lose the menu warning event for file save item?
   - Fix sprite file window to use properly scaled sprites
   - New spritefile view:
     - pane inside left edge of spritefile window contains mini views of each unique palette
     - sprites which use those palettes are laid out to the right
     - '?' palette (or empty block?) at the bottom for unsuitable sprites
     - palette list is fixed(?) so sprites can be dragged away until none of them use a specific palette, and the palette will persist
     - new palettes can be loaded by dragging palette files into the window
     - editors will always edit the full list for that palette?
     - two modes of dragging sprites from one palette to another: remap and non-remap
     - need way of highlighting palettes which are identical? or just assume that if two palettes are modified so that they are identical, they should get merged?
   - Change sprite palettes to be strongly bound to editor palettes?
     - a sprite can only be in one editor at once
     - dragging it into one will overwrite the palette with that of the editor, as well as potentially remapping the entries (if sprite link enabled)
*/

sprfile *sprfiles=0;

limpx_icon *icon_bar;
limpx_window *win_xfer,*win_proginfo,*win_warn;
limpx_window_header *temp_sprite,*temp_spritefile,*temp_palette,*temp_smpalette,*temp_sprlist;

int open_menu=-1;
sprfile *open_menu_sprfile=0;
preview *open_menu_preview=0;
paleditor *open_menu_paleditor=0;

limpx_menu *menu_main,*menu_spritefile,*menu_palette,*menu_sprite,*menu_sprlist;
int menu_main_info,menu_main_help,menu_main_quit;
int menu_spritefile_save;
int menu_palette_save,menu_palette_small,menu_palette_mask;
int menu_sprite_newview,menu_sprite_save,menu_sprite_zin,menu_sprite_zout;
int menu_sprlist_view,menu_sprlist_remove;


limpx_event *event_quit,*event_iconbar_click,*event_menu_selection,*event_gen_openwindow,*event_close_window,*event_load_handler,*event_gen_redraw,*event_gen_click,*event_drag_box,*event_menu_warning,*event_save_drag,*event_save_msg_ok,*event_save_ok,*event_mode_change,*event_pointer_enter,*event_pointer_leave,*event_warn_click,*event_null_code,*event_key_pressed;

limpx_icon_block temp_iconblock;

void *toolsprites;

int xeig,yeig;

_kernel_swi_regs regs;

int drag_box_active = 0;
int last_small_flag = 0;

static int func_close_window(limpx_window *win);
static int func_quit(int code,int *block);

void report_error(char *text)
{
#ifdef DEBUG
	fprintf(debuglog,"Reporting error: %s\n",text);
#endif
	LimpX_ReportError(0x600,text,0x511,"Pale",(char *) NULL,0,(char *) NULL);
}

int which_window(limpx_window *w,sprfile **ss,preview **pp,paleditor **ee)
{
	sprfile *s;
	preview *p;
	paleditor *e;
	if (w != NULL)
	{
		s = sprfiles;
		while (s != NULL)
		{
			if(w == s->mainwin)
			{
				*ss = s;
				*pp = 0;
				*ee = 0;
				return 1;
			}
			p = s->previews;
			while(p != NULL)
			{
				if(p->win == w)
				{
					*pp = p;
					*ss = 0;
					*ee = 0;
					return 1;
				}
				p = p->next;
			}
			e = s->editors;
			while(e != NULL)
			{
				if((e->palwin == w) || (e->sprlistwin == w))
				{
					*ee = e;
					*ss = 0;
					*pp = 0;
					return 1;
				}
				e = e->next;
			}
			s = s->next;
		}
	}
	return 0;
}


static int func_save_ok(limpx_mouse_obj *m,limpx_window *w,limpx_icon *i)
{
	char *name = LimpX_GetIcon_Text(LimpX_IconFromWindow(w,1));
	// Jump straight into data save
	if (m->buttons != 4)
		return 0;
	if((open_menu == MENU_SPRITEFILE) || (open_menu == MENU_WARN) || (open_menu == -MENU_WARN))
		save_file(name,open_menu_sprfile);
	else if(open_menu == MENU_PALETTE)
		save_palette(name,open_menu_paleditor->pal);
	else if(open_menu == MENU_SPRITE)
		save_sprite(name,open_menu_preview);
	if ((open_menu == MENU_WARN) || (open_menu == -MENU_WARN)) // Close file
	{
		LimpX_CreateMenu((limpx_menu *) NULL,0,0);
		func_close_window(open_menu_sprfile->mainwin);
		if (open_menu == -MENU_WARN)
			func_quit(0,(int *) NULL);
		return 1;
	}
	LimpX_CreateMenu((limpx_menu *) NULL,0,0);
	return 1;
}

static int func_drag_box_save(int minx,int miny,int maxx,int maxy)
{
	limpx_mouse_obj m;
	char *p,*op;
	int block[64];
#ifdef DEBUG
	fprintf(debuglog,"Drag end:\n");
#endif
	// Remove current event
	LimpX_DeleteEvent(event_drag_box);
#ifdef DEBUG
	fprintf(debuglog,"  Event removed\n");
#endif
	LimpX_DragASprite_Stop();
	// Get mouse pos
	LimpX_GetPointerInfo(&m);
	// Need to get file leafname...
	op = p = LimpX_GetIcon_Text(LimpX_IconFromWindow(win_xfer,1));
	while (p != NULL)
	{
		op = p;
		p = strchr(op,'.'); // Find the next '.'
		if (p != NULL)
			p++; // Skip char
	}
	block[0] = 45+strlen(op); // 44 for message, string length, terminator
	while (block[0] & 3)
		block[0]++;
	block[1] = 0;
	block[2] = 0;
	block[3] = 0; // Your ref
	block[4] = LIMPX_USERMSG_DATA_SAVE;
	block[5] = m.window;
	block[6] = m.icon;
	block[7] = m.x;
	block[8] = m.y;
	if((open_menu == MENU_SPRITEFILE) || (open_menu == MENU_WARN) || (open_menu == -MENU_WARN))
	{
		block[9] = open_menu_sprfile->len;
		block[10] = 0xff9;
	}
	else if(open_menu == MENU_PALETTE)
	{
		block[9] = 1536; /* 6*256 */
		block[10] = 0xfed;
	}
	else if(open_menu == MENU_SPRITE)
	{
		block[9] = 0; /* TODO! */
		block[10] = 0xff9;
	}
	strcpy((char *) &block[11],op);
#ifdef DEBUG
	fprintf(debuglog,"  Message set up\n");
#endif
	LimpX_SendMessage(LIMPX_POLLCODE_USER_MESSAGE,block,m.window,m.icon);
#ifdef DEBUG
	fprintf(debuglog,"  Message sent\n");
#endif
	LimpX_CloseWindow(win_xfer);
#ifdef DEBUG
	fprintf(debuglog,"  Window closed, exiting\n");
#endif
	return 1;
}

static int func_save_drag(limpx_mouse_obj *m,limpx_window *w,limpx_icon *i)
{
	int ox,oy;
	limpx_window_state p;
	int block[4];
	if (m->buttons != 64)
		return 0;
#ifdef DEBUG
	fprintf(debuglog,"Starting drag\n");
#endif
	p = LimpX_GetWindowState(w);
	ox = p.vminx - p.scrollx;
	oy = p.vmaxy - p.scrolly;
	LimpX_GetIconState(i,&temp_iconblock);
	LimpX_DragBox(w,5,temp_iconblock.sminx + ox,temp_iconblock.sminy + oy,temp_iconblock.smaxx + ox,temp_iconblock.smaxy + oy,0,0,-1,-1,0,0,0,0);
	block[0] = temp_iconblock.sminx + ox;
	block[1] = temp_iconblock.sminy + oy;
	block[2] = temp_iconblock.smaxx + ox;
	block[3] = temp_iconblock.smaxy + oy;
	if(open_menu == MENU_PALETTE)
		LimpX_DragASprite_Start(0x85,1,"file_fed",block,NULL);
	else
		LimpX_DragASprite_Start(0x85,1,"file_ff9",block,NULL);
	// Add drag event
	event_drag_box = LimpX_NewEvent_Drag_Box(func_drag_box_save);
#ifdef DEBUG
	fprintf(debuglog,"Func end\n");
#endif
	return 1;
}

static int func_save_msg_ok(int code,int *block)
{
	// Call save code
#ifdef DEBUG
	fprintf(debuglog,"Save OK message!\n");
#endif
	if((open_menu == MENU_SPRITEFILE) || (open_menu == MENU_WARN) || (open_menu == -MENU_WARN))
		save_file((char *) &block[11],open_menu_sprfile);
	else if(open_menu == MENU_PALETTE)
		save_palette((char *) &block[11],open_menu_paleditor->pal);
	else if(open_menu == MENU_SPRITE)
		save_sprite((char *) &block[11],open_menu_preview);
	if ((open_menu == MENU_WARN) || (open_menu == -MENU_WARN)) // Close file
	{
		LimpX_CreateMenu((limpx_menu *) NULL,0,0);
		func_close_window(open_menu_sprfile->mainwin);
		if (open_menu == -MENU_WARN)
			func_quit(0,(int *) NULL);
		return 1;
	}
	LimpX_CreateMenu((limpx_menu *) NULL,0,0);
	return 1;
}

static int func_menu_warning(int code,int *block)
{
	if (open_menu == MENU_SPRITEFILE)
	{
		if (block[5] == LimpX_HandleFromWindow(win_xfer))
		{
			LimpX_SetIcon_Text(LimpX_IconFromWindow(win_xfer,1),open_menu_sprfile->name);
			LimpX_SetIcon_TextLength(LimpX_IconFromWindow(win_xfer,1),256);
			LimpX_SetIcon_Text(LimpX_IconFromWindow(win_xfer,2),"file_ff9");
			LimpX_CreateSubMenu((limpx_menu *) NULL,win_xfer,block[6],block[7]);
			LimpX_UpdateCaret();
			return 1;
		}
	}
	else if(open_menu == MENU_PALETTE)
	{
		if (block[5] == LimpX_HandleFromWindow(win_xfer))
		{
			LimpX_SetIcon_Text(LimpX_IconFromWindow(win_xfer,1),"Palette");
			LimpX_SetIcon_TextLength(LimpX_IconFromWindow(win_xfer,1),256);
			LimpX_SetIcon_Text(LimpX_IconFromWindow(win_xfer,2),"file_fed");
			LimpX_CreateSubMenu((limpx_menu *) NULL,win_xfer,block[6],block[7]);
			LimpX_UpdateCaret();
			return 1;
		}
	}
	else if(open_menu == MENU_SPRITE)
	{
		if (block[5] == LimpX_HandleFromWindow(win_xfer))
		{
			LimpX_SetIcon_Text(LimpX_IconFromWindow(win_xfer,1),preview_getspritename(open_menu_preview));
			LimpX_SetIcon_TextLength(LimpX_IconFromWindow(win_xfer,1),256);
			LimpX_SetIcon_Text(LimpX_IconFromWindow(win_xfer,2),"file_ff9");
			LimpX_CreateSubMenu((limpx_menu *) NULL,win_xfer,block[6],block[7]);
			LimpX_UpdateCaret();
			return 1;
		}
	}
	return 0;
}

static int func_iconbar_click(limpx_mouse_obj *m,limpx_window *w,limpx_icon *i)
{
#ifdef DEBUG
	fprintf(debuglog,"Iconbar click! %d,%d, %X,%X\n",m->x,m->y,(int) w,(int) i);
#endif
	// Check buttons
	if (m->buttons == 2)
	{
		// Open menu
		LimpX_CreateMenu(menu_main,m->x-64,80+(22*7));
		open_menu = MENU_MAIN;
		return 1;
	}
	return 0;
}

static int func_menu_selection(int *selections)
{
	limpx_mouse_obj m;
	limpx_window_state s;
	int i;
	switch (open_menu)
	{
	case MENU_MAIN:
		if (selections[0] == menu_main_quit)
		{
#ifdef DEBUG
			fprintf(debuglog,"Quit chosen from menu\n");
#endif
			func_quit(0,(int *) NULL);
		}
		else if (selections[0] == menu_main_help)
			system("filer_run <Pale$Dir>.!Help");
		break;
	case MENU_PALETTE:
		if (selections[0] == menu_palette_small)
		{
			open_menu_paleditor->toolflags ^= TOOL_SMALL;
			last_small_flag = open_menu_paleditor->toolflags & TOOL_SMALL;
			/* Rebuild palette window */
			s = LimpX_GetWindowState(open_menu_paleditor->palwin);
			LimpX_CloseWindow(open_menu_paleditor->palwin);
			LimpX_DeleteWindow(open_menu_paleditor->palwin);
			if(open_menu_paleditor->toolflags & TOOL_SMALL)
				open_menu_paleditor->palwin = LimpX_CreateWindow(temp_smpalette);
			else
				open_menu_paleditor->palwin = LimpX_CreateWindow(temp_palette);
			/* Set window title! */
			char name[16];
			if(open_menu_paleditor->nsprites)
			{
				void *spr = open_menu_paleditor->sprites[0];
				memcpy(name,&(((int *) spr)[1]),12);
				name[12] = 0;
			}
			else
				strcpy(name,"<unknown>");
			LimpX_SetWindow_Title(open_menu_paleditor->palwin,name);
			/* Set correct icon states */
			editor_resettoolicons(open_menu_paleditor);
			/* Reopen window in old position */
			LimpX_OpenWindowAt(open_menu_paleditor->palwin,s);
			/* And fix pane */
			editor_repositionsprlistwin(open_menu_paleditor,0);
		}
		else if(selections[0] == menu_palette_mask)
		{
			editor_redrawentry(open_menu_paleditor,open_menu_paleditor->maskindex);
			if(open_menu_paleditor->maskindex == open_menu_paleditor->selstart)
				open_menu_paleditor->maskindex = -1;
			else
				open_menu_paleditor->maskindex = open_menu_paleditor->selstart;
			editor_redrawentry(open_menu_paleditor,open_menu_paleditor->maskindex);
			editor_redrawpreviews(open_menu_paleditor);
		}
		break;
	case MENU_SPRITE:
		if(selections[0] == menu_sprite_newview)
		{
			preview_create1(open_menu_preview->file,open_menu_preview->spr,open_menu_preview->pal);
		}
		if ((selections[0] == menu_sprite_zin) && (open_menu_preview->zoom < ZOOM_MAX))
		{
			open_menu_preview->zoom++;
			preview_redrawonzoom(open_menu_preview);
		}
		else if((selections[0] == menu_sprite_zout) && (open_menu_preview->zoom > ZOOM_MIN))
		{
			open_menu_preview->zoom--;
			preview_redrawonzoom(open_menu_preview);
		}
		break;
	case MENU_SPRLIST:
		if (selections[0] == menu_sprlist_view)
		{
			for(i=0;i<open_menu_paleditor->nsprites;i++)
			{
				if(LimpX_GetIcon_Flags(LimpX_IconFromWindow(open_menu_paleditor->sprlistwin,i)) & LIMPX_ICONFLAG_SELECTED)
					preview_create1(open_menu_paleditor->file,open_menu_paleditor->sprites[i],open_menu_paleditor);
			}
		}
		else if(selections[0] == menu_sprlist_remove)
		{
			for(i=open_menu_paleditor->nsprites-1;i>=0;i--)
			{
				if(LimpX_GetIcon_Flags(LimpX_IconFromWindow(open_menu_paleditor->sprlistwin,i)) & LIMPX_ICONFLAG_SELECTED)
				{
					preview_close(open_menu_paleditor,open_menu_paleditor->sprites[i]);
					if(i<open_menu_paleditor->nsprites-1)
						memmove(&(open_menu_paleditor->sprites[i]),&(open_menu_paleditor->sprites[i+1]),4*(open_menu_paleditor->nsprites-(i+1)));
					open_menu_paleditor->nsprites--;
				}
			}
			editor_rebuildsprlistwindow(open_menu_paleditor);
		}
		break;
	}
	LimpX_GetPointerInfo(&m);
	if (m.buttons & 1)
	{
		if(open_menu == MENU_MAIN)
			LimpX_CreateMenu(menu_main,0,0);
		else if(open_menu == MENU_SPRITEFILE)
			LimpX_CreateMenu(menu_spritefile,0,0);
		else if(open_menu == MENU_PALETTE)
			LimpX_CreateMenu(menu_palette,0,0);
		else if(open_menu == MENU_SPRITE)
			LimpX_CreateMenu(menu_sprite,0,0);
		else if(open_menu == MENU_SPRLIST)
			LimpX_CreateMenu(menu_sprlist,0,0);
		else
			LimpX_CreateMenu((limpx_menu *) NULL,0,0);
	}
	return 1;
}

static int func_warn_click(limpx_mouse_obj *m,limpx_window *w,limpx_icon *i)
{
	int icon;
	icon = LimpX_HandleFromIcon(i);
	if (icon == 1)
		LimpX_CreateMenu((limpx_menu *) NULL,0,0);
	else if (icon == 2)
	{
		// Close the file
		LimpX_CreateMenu((limpx_menu *) NULL,0,0);
		open_menu_sprfile->modified = 0;
		func_close_window(open_menu_sprfile->mainwin);
		if (open_menu == -MENU_WARN)
			func_quit(0,(int *) NULL);
	}
	else if (icon == 3)
	{
		// Save the file
		// Open a box for them
		LimpX_SetIcon_Text(LimpX_IconFromWindow(win_xfer,1),open_menu_sprfile->name);
		LimpX_SetIcon_TextLength(LimpX_IconFromWindow(win_xfer,1),256);
		LimpX_CreateWindowAsMenu(win_xfer,m->x,m->y);
		LimpX_UpdateCaret();
	}
	return 1;
}

static int func_quit(int code,int *block) // These may be null if quit chosen from menu
{
#ifdef DEBUG
	if (code != 0)
		fprintf(debuglog,"Quit message received\n");
#endif
	// Close files
	LimpX_CreateMenu((limpx_menu *) NULL,0,0);
	open_menu = 0;
	while ((sprfiles != NULL) && (open_menu != MENU_WARN))
		func_close_window(sprfiles->mainwin); // Try closing each one one by one
	if (open_menu != MENU_WARN) // OK to quit
		LimpX_SetAbortFlag(1);
	else
		open_menu = -MENU_WARN; // Nasty hack to flag that we should continue closing later
	return 1;
}

static int func_gen_openwindow(limpx_window *win,limpx_window_state *pos)
{
	LimpX_OpenWindowAt(win,*pos);
	/* Is this a palette window? If so, open the sprite list next to it */
	sprfile *s;
	s = sprfiles;
	while(s)
	{
		paleditor *e;
		e=s->editors;
		while(e)
		{
			if(e->palwin == win)
			{
				editor_repositionsprlistwin(e,pos);
				return 1;
			}
			e = e->next;
		}
		s = s->next;
	}
	return 1;
}

static int func_close_window(limpx_window *win)
{
	limpx_mouse_obj m;
	sprfile *s;
	preview *p;
	paleditor *e;
	char title[256];
	if(!which_window(win,&s,&p,&e))
	{
		/* Just close it? */
		LimpX_CloseWindow(win);
		return 1;
	}
	if(s)
	{
		/* Close file */
		if(s->modified)
		{
			/* Warn user */
			sprintf(title,"Pale: %s",s->name);
			LimpX_SetWindow_Title(win_warn,title);
			LimpX_GetPointerInfo(&m);
			LimpX_CreateWindowAsMenu(win_warn,m.x-370,m.y+120);
			open_menu_sprfile = s;
			open_menu = MENU_WARN;
			return 1;
		}
		/* Else close all its children */
		while(s->previews)
			func_close_window(s->previews->win);
		while(s->editors)
			func_close_window(s->editors->palwin);
		/* Now close file */
		LimpX_CloseWindow(win);
		LimpX_DeleteWindow(win);
		free(s->name);
		free(s->sprs);
		if(s->next)
			s->next->prev = s->prev;
		if(s->prev)
			s->prev->next = s->next;
		else
			sprfiles = s->next;
		free(s);
	}
	else if(p)
	{
		/* Close preview */
		LimpX_CloseWindow(win);
		LimpX_DeleteWindow(win);
		if(p->next)
			p->next->prev = p->prev;
		if(p->prev)
			p->prev->next = p->next;
		else
			p->file->previews = p->next;
		free(p);
	}
	else if(e)
	{
		/* Close editor */
		/* First, close any previews that use this editor */
		preview *pp;
		p = e->file->previews;
		while(p)
		{
			pp = p->next;
			if(p->pal == e)
				func_close_window(p->win);
			p = pp;
		}
		/* We can guarantee that both palwin and sprlistwin exist, so close them both */
		LimpX_CloseWindow(e->palwin);
		LimpX_CloseWindow(e->sprlistwin);
		LimpX_DeleteWindow(e->palwin);
		LimpX_DeleteWindow(e->sprlistwin);
		if(e->sprites)
			free(e->sprites);
		if(e->next)
			e->next->prev = e->prev;
		if(e->prev)
			e->prev->next = e->next;
		else
			e->file->editors = e->next;
		free(e);
	}
	return 1;
}

static int func_load_handler(int code,int *block)
{
	limpx_icon *i;
	limpx_window *w;
	sprfile *s;
	preview *p;
	paleditor *e;
	futil_iblock f;
	w = LimpX_WindowFromHandle(block[5]);
	i = LimpX_IconFromHandle(block[5],block[6]);
#ifdef DEBUG
	fprintf(debuglog,"Load handler called! %X,%X\n",block[5],block[6]);
#endif
	/* Get file info */
	futil_ginfo((char *) &(block[11]),&f);
	/* Did they try loading a palette somewhere sensible? */
	if(which_window(w,&s,&p,&e) && (f.type == 1) && (f.filetype == 0xfed))
	{
		if((s) && (block[5] != -1))
			load_palette1(s,block[5],(char *) &(block[11]));
		else if(p)
			load_palette2(p,(char *) &(block[11]));
		else if(e)
			load_palette3(e,(char *) &(block[11]));
		else
			return 0; // Bad load request
		// Respond to message
		block[3] = block[2]; // Fill in our ref
		block[4] = LIMPX_USERMSG_DATA_LOAD_ACK;
		LimpX_SendMessage(code,block,block[1],0);
		return 1;
	} /* Else did they try loading a sprite file? */
	else if(((i == icon_bar) || (block[5] == -1)) && (f.filetype == 0xff9))
	{
		load_spritefile((char *) &(block[11]));
		// Respond to message
		block[3] = block[2]; // Fill in our ref
		block[4] = LIMPX_USERMSG_DATA_LOAD_ACK;
		LimpX_SendMessage(code,block,block[1],0);
		return 1;
	}
	return 0;
}

static int func_gen_redraw(limpx_window *win)
{
	limpx_redraw_obj r;
	sprfile *s;
	preview *p;
	paleditor *e;
	int tmp,first=1;
	/* Which window is this? */
	r.ro_handle = LimpX_HandleFromWindow(win);
	if(which_window(win,&s,&p,&e))
	{
		if(p)
		{
			tmp = LimpX_RedrawWindow(&r);
			while(tmp != 0)
			{
				preview_redraw(p,&r,first);
				tmp = LimpX_GetRectangle(&r);
				first=0;
			}
			return 1;
		}
		else if((e) && (win == e->palwin))
		{
			tmp = LimpX_RedrawWindow(&r);
			while(tmp != 0)
			{
				editor_redraw(e,&r,first);
				tmp = LimpX_GetRectangle(&r);
				first=0;
			}
			return 1;
		}
	}
	/* Hmm, that's strange... */
	tmp = LimpX_RedrawWindow(&r);
	while(tmp != 0)
		tmp = LimpX_GetRectangle(&r);
	return 1;
}

static int func_gen_click(limpx_mouse_obj *m,limpx_window *w,limpx_icon *i)
{
	sprfile *s;
	preview *p;
	paleditor *e;
	int ih;
	if(i != 0)
		ih = LimpX_HandleFromIcon(i);
	else
		ih = -1;
	/* What did they click? */
	if(!which_window(w,&s,&p,&e))
		return 0;
	if(s)
	{
		if((m->buttons & 4) && (ih != -1))
		{
			editor_create(s,ih);
			return 1;
		}
		else if((m->buttons & 0x40) && (ih != -1))
		{
			/* Start sprite drag */
			sprfile_startdrag(s,ih);
			return 1;
		}
		else if(m->buttons & 0x202)
		{
			open_sprfile_menu(s,ih,m);
			return 1;
		}
	}
	else if(p)
	{
		if(m->buttons & 0x1)
		{
			preview_selectcolour(p,m);
			return 1;
		}
		else if(m->buttons & 0x2)
		{
			open_preview_menu(p,m);
			return 1;
		}
	}
	else if(e)
	{
		if(w == e->palwin)
		{
			if(m->buttons & 0x2)
			{
				open_editor_menu(e,m);
				return 1;
			}
			switch(ih)
			{
			case PALICON_PALETTE:
				editor_clickpalette(e,m);
				return 1;
			case PALICON_LINK:
				e->toolflags &= ~TOOL_LINK;
				if(LimpX_GetIcon_Flags(i) & LIMPX_ICONFLAG_SELECTED)
					e->toolflags |= TOOL_LINK;
				return 1;
			case PALICON_GRADIENT:
				e->toolflags &= ~TOOL_GRADIENT;
				if(LimpX_GetIcon_Flags(i) & LIMPX_ICONFLAG_SELECTED)
					e->toolflags |= TOOL_GRADIENT;
				return 1;
			case PALICON_COPY:
				e->toolflags &= ~TOOL_COPY;
				if(LimpX_GetIcon_Flags(i) & LIMPX_ICONFLAG_SELECTED)
					e->toolflags |= TOOL_COPY;
				return 1;
			case PALICON_SWAP:
				e->toolflags &= ~TOOL_SWAP;
				if(LimpX_GetIcon_Flags(i) & LIMPX_ICONFLAG_SELECTED)
					e->toolflags |= TOOL_SWAP;
				return 1;
			}
		}
		else if(w == e->sprlistwin)
		{
			if(m->buttons & 0x2)
			{
				open_sprlistwin_menu(e,m);
				return 1;
			}
			if(ih != -1)
			{
				editor_openpreviews(e);
				return 1;
			}
		}
	}
	return 0;
}

static int func_null_code()
{
	// get mouse pos
	limpx_mouse_obj m;
	limpx_window *w;
	static int lupt;
	sprfile *s;
	preview *p;
	paleditor *e;
	LimpX_GetPointerInfo(&m);
	_kernel_swi(OS_ReadMonotonicTime,&regs,&regs);
	if ((regs.r[0] == lupt) && (m.buttons == 0))
		return 1;
	lupt = regs.r[0]; // Stop it eating up massive amounts of processor time (unless we seem to be drawing)
	w = LimpX_WindowFromHandle(m.window);
	/* Which window is this? */
	if(!which_window(w,&s,&p,&e))
		return 1;
	if(p && (p->pal))
		preview_updatehint(p,&m);
	else if(e && (w == e->palwin))
		editor_updatehint(e,&m);
	return 1;
}

static int func_pointer_enter(limpx_window *w)
{
	sprfile *s;
	preview *p;
	paleditor *e;
	if (w != NULL)
	{
		s = sprfiles;
		while (s != NULL)
		{
			p = s->previews;
			while(p != NULL)
			{
				if((p->win == w) && (p->pal))
				{
					if (event_null_code == NULL)
						event_null_code = LimpX_NewEvent_Null_Code(func_null_code);
					return 0; // Must return 0 so that it will break out of the limpx poll cycle and allow Pale to toggle the ignore-null flag
				}
				p = p->next;
			}
			e = s->editors;
			while(e != NULL)
			{
				if((e->palwin == w) || (e->sprlistwin == w))
				{
					if (event_null_code == NULL)
						event_null_code = LimpX_NewEvent_Null_Code(func_null_code);
					return 0; // Must return 0 so that it will break out of the limpx poll cycle and allow Pale to toggle the ignore-null flag
				}
				e = e->next;
			}
			s = s->next;
		}
	}
	return 1;
}

static int func_pointer_leave(limpx_window *w)
{
	sprfile *s;
	preview *p;
	paleditor *e;
	/* Ignore this event if we're dragging - otherwise hints will stop updating */
	if(drag_box_active)
		return 1;
	if (w != NULL)
	{
		s = sprfiles;
		while (s != NULL)
		{
			p = s->previews;
			while(p != NULL)
			{
				if((p->win == w) && (p->pal))
				{
					if (event_null_code == NULL)
						return 0;
					LimpX_DeleteEvent(event_null_code);
					event_null_code = NULL;
					return 0; // Must return 0 so that it will break out of the limpx poll cycle and allow Pale to toggle the ignore-null flag
				}
				p = p->next;
			}
			e = s->editors;
			while(e != NULL)
			{
				if((e->palwin == w) || (e->sprlistwin == w))
				{
					editor_highlight(e,-1);
					if (event_null_code == NULL)
						return 0;
					LimpX_DeleteEvent(event_null_code);
					event_null_code = NULL;
					return 0; // Must return 0 so that it will break out of the limpx poll cycle and allow Pale to toggle the ignore-null flag
				}
				e = e->next;
			}
			s = s->next;
		}
	}
	return 1;
}

static int func_mode_change(int code,int *block)
{
	regs.r[0] = -1;
	regs.r[1] = 4;
	_kernel_swi(OS_ReadModeVariable,&regs,&regs);
	xeig = regs.r[2];
	regs.r[1] = 5;
	_kernel_swi(OS_ReadModeVariable,&regs,&regs);
	yeig = regs.r[2];
	return 1;
}

static int func_key_pressed(limpx_caret_obj *c,int key)
{
	sprfile *s;
	preview *p;
	paleditor *e;
	limpx_window *w;
	w = LimpX_WindowFromHandle(c->window);
	if(!w)
		return 1;
	/* Is this a palette editor window? */
	if(!which_window(w,&s,&p,&e))
		return 1;
	if(!e)
		return 1;
	if((c->icon != PALICON_R) && (c->icon != PALICON_G) && (c->icon != PALICON_B))
		return 1;
	if(e->selend <= e->selstart)
		return 1;
	/* parse the text fields and set the palette entry */
	int r,g,b;
	if(sscanf(LimpX_GetIcon_Text(LimpX_IconFromWindow(w,PALICON_R)),"%d",&r) != 1)
		r = e->pal[e->selstart*2].r;
	if(sscanf(LimpX_GetIcon_Text(LimpX_IconFromWindow(w,PALICON_G)),"%d",&g) != 1)
		g = e->pal[e->selstart*2].g;
	if(sscanf(LimpX_GetIcon_Text(LimpX_IconFromWindow(w,PALICON_B)),"%d",&b) != 1)
		b = e->pal[e->selstart*2].b;
	/* Update the palettes */
	int i;
	editor_typing = 1;
	for (i=e->selstart;i<e->selend;i++)
		editor_writeentry(e,i,r,g,b);
	editor_typing = 0;
	/* Refresh */
	editor_redrawentries(e,e->selstart,e->selend);
	editor_redrawpreviews(e);
	sprfile_redraw(e->file,0);
	return 1;
}

static void init_interface()
{
	limpx_window_header *cur;
	LimpX_Initialise("Pale");
	LimpX_OpenTemplate("<Pale$Dir>.Templates");
	cur = LimpX_LoadTemplate("xfer_send");
	win_xfer = LimpX_CreateWindow(cur);
	LimpX_KillTemplate(cur);
	cur = LimpX_LoadTemplate("proginfo");
	win_proginfo = LimpX_CreateWindow(cur);
	LimpX_KillTemplate(cur);
	cur = LimpX_LoadTemplate("warn");
	win_warn = LimpX_CreateWindow(cur);
	LimpX_KillTemplate(cur);
	temp_sprite = LimpX_LoadTemplate("Sprite");
	temp_spritefile = LimpX_LoadTemplate("SpriteFile");
	temp_palette = LimpX_LoadTemplate("palette");
	temp_smpalette = LimpX_LoadTemplate("smpalette");
	temp_sprlist = LimpX_LoadTemplate("sprlist");
	LimpX_CloseTemplate();
	// Iconbar icon
	temp_iconblock.sminx = 0;
	temp_iconblock.sminy = 0;
	temp_iconblock.smaxx = 68;
	temp_iconblock.smaxy = 70;
	temp_iconblock.flags = 0x301A; // Sprite, H&V centered, click without auto repeat
	strcpy((char *) temp_iconblock.data,"!Pale");
	icon_bar = LimpX_NewIconBarIcon(-1,0,&temp_iconblock);

	LimpX_SetIcon_Text(LimpX_IconFromWindow(win_proginfo,0),"Pale");
	LimpX_SetIcon_Text(LimpX_IconFromWindow(win_proginfo,1),"SpriteFile palette editor");
	LimpX_SetIcon_Text(LimpX_IconFromWindow(win_proginfo,2),"Jeffrey Lee");
	LimpX_SetIcon_Text(LimpX_IconFromWindow(win_proginfo,3),version_string);

	/* [Pale]
	    Info            > win_proginfo
	    Help...
	    Quit
	*/
	/* [SpriteFile]
	    Save            > win_xfer
	*/
	/* [Palette]
	    Save            > win_xfer
	    Small colours
	   ---------------
	    Mask entry
	*/
	/* [Sprite]
	    Save            > win_xfer
	    Zoom in
	    Zoom out
	*/
	/* [Sprlist]
	    Open viewer
	    Remove
	*/
	menu_main = LimpX_NewMenu();
	LimpX_SetMenu_Text(menu_main,"Pale");
	menu_main_info = LimpX_NewMenuItem(menu_main);
		LimpX_SetMenuItem_IconFlags(menu_main,menu_main_info,0x7000021);
		LimpX_SetMenuItem_Text(menu_main,menu_main_info,"Info");
		LimpX_SetMenuItem_SubWindow(menu_main,menu_main_info,win_proginfo);
	menu_main_help = LimpX_NewMenuItem(menu_main);
		LimpX_SetMenuItem_IconFlags(menu_main,menu_main_help,0x7000021);
		LimpX_SetMenuItem_Text(menu_main,menu_main_help,"Help...");
	menu_main_quit = LimpX_NewMenuItem(menu_main);
		LimpX_SetMenuItem_IconFlags(menu_main,menu_main_quit,0x7000021);
		LimpX_SetMenuItem_Text(menu_main,menu_main_quit,"Quit");
	LimpX_UpdateMenuBlock(menu_main);
	menu_spritefile = LimpX_NewMenu();
	LimpX_SetMenu_Text(menu_spritefile,"Pale");
	menu_spritefile_save = LimpX_NewMenuItem(menu_spritefile);
		LimpX_SetMenuItem_IconFlags(menu_spritefile,menu_spritefile_save,0x7000021);
		LimpX_SetMenuItem_Text(menu_spritefile,menu_spritefile_save,"Save");
		LimpX_SetMenuItem_SubWindow(menu_spritefile,menu_spritefile_save,win_xfer);
		LimpX_SetMenuItem_ItemFlags(menu_spritefile,menu_spritefile_save,LIMPX_MENUITEM_MESSAGE);
	LimpX_UpdateMenuBlock(menu_spritefile);
	menu_palette = LimpX_NewMenu();
	LimpX_SetMenu_Text(menu_palette,"Pale");
	menu_palette_save = LimpX_NewMenuItem(menu_palette);
		LimpX_SetMenuItem_IconFlags(menu_palette,menu_palette_save,0x7000021);
		LimpX_SetMenuItem_Text(menu_palette,menu_palette_save,"Save");
		LimpX_SetMenuItem_SubWindow(menu_palette,menu_palette_save,win_xfer);
		LimpX_SetMenuItem_ItemFlags(menu_palette,menu_palette_save,LIMPX_MENUITEM_MESSAGE);
	menu_palette_small = LimpX_NewMenuItem(menu_palette);
		LimpX_SetMenuItem_IconFlags(menu_palette,menu_palette_small,0x7000121);
		LimpX_SetMenuItem_Text(menu_palette,menu_palette_small,"Small colours");
		LimpX_SetMenuItem_ItemFlags(menu_palette,menu_palette_small,LIMPX_MENUITEM_DOTTED);
	menu_palette_mask = LimpX_NewMenuItem(menu_palette);
		LimpX_SetMenuItem_IconFlags(menu_palette,menu_palette_mask,0x7000021);
		LimpX_SetMenuItem_Text(menu_palette,menu_palette_mask,"Mask entry");
	LimpX_UpdateMenuBlock(menu_palette);
	menu_sprite = LimpX_NewMenu();
	LimpX_SetMenu_Text(menu_sprite,"Pale");
	menu_sprite_newview = LimpX_NewMenuItem(menu_sprite);
		LimpX_SetMenuItem_IconFlags(menu_sprite,menu_sprite_newview,0x7000021);
		LimpX_SetMenuItem_Text(menu_sprite,menu_sprite_newview,"New view");
	menu_sprite_save =  LimpX_NewMenuItem(menu_sprite);
		LimpX_SetMenuItem_IconFlags(menu_sprite,menu_sprite_save,0x7000021);
		LimpX_SetMenuItem_Text(menu_sprite,menu_sprite_save,"Save");
		LimpX_SetMenuItem_SubWindow(menu_sprite,menu_sprite_save,win_xfer);
		LimpX_SetMenuItem_ItemFlags(menu_sprite,menu_sprite_save,LIMPX_MENUITEM_MESSAGE);
	menu_sprite_zin = LimpX_NewMenuItem(menu_sprite);
		LimpX_SetMenuItem_IconFlags(menu_sprite,menu_sprite_zin,0x7000021);
		LimpX_SetMenuItem_Text(menu_sprite,menu_sprite_zin,"Zoom in");
	menu_sprite_zout = LimpX_NewMenuItem(menu_sprite);
		LimpX_SetMenuItem_IconFlags(menu_sprite,menu_sprite_zout,0x7000021);
		LimpX_SetMenuItem_Text(menu_sprite,menu_sprite_zout,"Zoom out");
	LimpX_UpdateMenuBlock(menu_sprite);
	menu_sprlist = LimpX_NewMenu();
	LimpX_SetMenu_Text(menu_sprlist,"Pale");
	menu_sprlist_view = LimpX_NewMenuItem(menu_sprlist);
		LimpX_SetMenuItem_IconFlags(menu_sprlist,menu_sprlist_view,0x7000021);
		LimpX_SetMenuItem_Text(menu_sprlist,menu_sprlist_view,"Open viewer");
	menu_sprlist_remove = LimpX_NewMenuItem(menu_sprlist);
		LimpX_SetMenuItem_IconFlags(menu_sprlist,menu_sprlist_remove,0x7000021);
		LimpX_SetMenuItem_Text(menu_sprlist,menu_sprlist_remove,"Remove");
	LimpX_UpdateMenuBlock(menu_sprlist);
	open_menu = MENU_NONE;

	event_iconbar_click = LimpX_NewEvent_Mouse_Click(func_iconbar_click,(limpx_window *) NULL,icon_bar);
	event_menu_selection = LimpX_NewEvent_Menu_Selection(func_menu_selection);
	event_quit = LimpX_NewEvent_User_Message(func_quit,LIMPX_USERMSG_QUIT);
	event_gen_openwindow = LimpX_NewEvent_Open_Window(func_gen_openwindow,(limpx_window *) NULL);
	event_close_window = LimpX_NewEvent_Close_Window(func_close_window,(limpx_window *) NULL);
	event_load_handler = LimpX_NewEvent_User_Message(func_load_handler,LIMPX_USERMSG_DATA_LOAD);
	event_gen_redraw = LimpX_NewEvent_Redraw_Window(func_gen_redraw,(limpx_window *) NULL);
	event_gen_click = LimpX_NewEvent_Mouse_Click(func_gen_click,(limpx_window *) NULL,(limpx_icon *) NULL);
	event_menu_warning = LimpX_NewEvent_User_Message(func_menu_warning,LIMPX_USERMSG_MENU_WARNING);
	event_save_drag = LimpX_NewEvent_Mouse_Click(func_save_drag,win_xfer,LimpX_IconFromWindow(win_xfer,2));
	event_save_msg_ok = LimpX_NewEvent_User_Message(func_save_msg_ok,LIMPX_USERMSG_DATA_SAVE_ACK);
	event_save_ok = LimpX_NewEvent_Mouse_Click(func_save_ok,win_xfer,LimpX_IconFromWindow(win_xfer,0));
	event_mode_change = LimpX_NewEvent_User_Message(func_mode_change,LIMPX_USERMSG_MODE_CHANGE);
	event_pointer_enter = LimpX_NewEvent_Pointer_Enter(func_pointer_enter,(limpx_window *) NULL);
	event_pointer_leave = LimpX_NewEvent_Pointer_Leave(func_pointer_leave,(limpx_window *) NULL);
	event_warn_click = LimpX_NewEvent_Mouse_Click(func_warn_click,win_warn,(limpx_icon *) NULL);
	event_null_code = (limpx_event *) NULL;
	event_key_pressed = LimpX_NewEvent_Key_Pressed(func_key_pressed,0,0);
	// Load tool sprites
	toolsprites = OSSpr_LoadFile("<Pale$Dir>.Sprites");
	temp_palette->sprite_block = temp_smpalette->sprite_block = (int) toolsprites;
	func_mode_change(0,(int *) NULL);
}

static void interface_poll()
{
	limpx_pollblock *p;
	while (LimpX_GetAbortFlag() == 0)
	{
		if (event_null_code == NULL)
			p = LimpX_Poll(1,NULL);
		else
			p = LimpX_Poll(0,NULL);
#ifdef DEBUG
		fprintf(debuglog,"Poll returns %d:\n",p->code);
		fprintf(debuglog,"%d,%X,%d,%d,%d\n",p->block[0],p->block[1],p->block[2],p->block[3],p->block[4]);
#endif
	}
}

static void finish_interface()
{
#ifdef DEBUG
	fprintf(debuglog,"finish_interface()\n");
#endif
	// Shut everything down...
	LimpX_KillAll();
	LimpX_CloseDown();
}

int main(int argc,char *argv[])
{
#ifdef DEBUG
	debuglog = fopen("<Pale$Dir>.Debug","w");
#endif
	init_interface();
	interface_poll();
	finish_interface();
#ifdef DEBUG
	fprintf(debuglog,"End\n");
	fclose(debuglog);
#endif
	return 0;
}

