/* 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 "editor.h"
#include "sprfile.h"
#include "preview.h"

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

//DEPPEND

#define BIG_SIZE 960
#define SMALL_SIZE 480
#define BIG_ENTRY 60
#define SMALL_ENTRY 30

#define CURRENT_SIZE ((e->toolflags&TOOL_SMALL)?SMALL_SIZE:BIG_SIZE)
#define CURRENT_ENTRY ((e->toolflags&TOOL_SMALL)?SMALL_ENTRY:BIG_ENTRY)

int editor_typing = 0;

limpx_event *event_editor_drag_box;

int editor_mousetoindex(paleditor *e,limpx_mouse_obj *m)
{
	limpx_window_state ws;
	ws = LimpX_GetWindowOutline(e->palwin);
	int x = m->x-ws.vminx;
	int y = (m->y-ws.vmaxy)+44; /* Hack! */
	x /= CURRENT_ENTRY;
	y /= CURRENT_ENTRY;
	y=-y;
	if((x<0) || (x>15) || (y<0) || (y>15))
		return -1;
	return x+16*y;
}

void editor_resettoolicons(paleditor *e)
{
	/* Set the icons according to the toolflags */
	limpx_icon *i;
	i = LimpX_IconFromWindow(e->palwin,PALICON_LINK);
	LimpX_SetIcon_Flags(i,(LimpX_GetIcon_Flags(i) & ~LIMPX_ICONFLAG_SELECTED) | ((e->toolflags & TOOL_LINK)?LIMPX_ICONFLAG_SELECTED:0));
	i = LimpX_IconFromWindow(e->palwin,PALICON_GRADIENT);
	LimpX_SetIcon_Flags(i,(LimpX_GetIcon_Flags(i) & ~LIMPX_ICONFLAG_SELECTED) | ((e->toolflags & TOOL_GRADIENT)?LIMPX_ICONFLAG_SELECTED:0));
	i = LimpX_IconFromWindow(e->palwin,PALICON_COPY);
	LimpX_SetIcon_Flags(i,(LimpX_GetIcon_Flags(i) & ~LIMPX_ICONFLAG_SELECTED) | ((e->toolflags & TOOL_COPY)?LIMPX_ICONFLAG_SELECTED:0));
	i = LimpX_IconFromWindow(e->palwin,PALICON_SWAP);
	LimpX_SetIcon_Flags(i,(LimpX_GetIcon_Flags(i) & ~LIMPX_ICONFLAG_SELECTED) | ((e->toolflags & TOOL_SWAP)?LIMPX_ICONFLAG_SELECTED:0));
}

void editor_redrawentries(paleditor *e,int start,int end)
{
	/* Inclusive, exclusive redraw */
	if((start & 0xF0) == ((end-1) & 0xF0))
	{
		/* Same row */
		LimpX_ForceRedraw(e->palwin,(start & 0xF)*CURRENT_ENTRY,-((start & 0xF0)/16+1)*CURRENT_ENTRY,(((end-1) & 0xF)+1)*CURRENT_ENTRY+4,-(start & 0xF0)/16*CURRENT_ENTRY+4);
	}
	else
	{
		/* Multiple rows - just redraw all affected */
		LimpX_ForceRedraw(e->palwin,0,-(((end-1) & 0xF0)/16+1)*CURRENT_ENTRY,CURRENT_SIZE+4,-(start & 0xF0)/16*CURRENT_ENTRY+4);
	}
}

void editor_redrawentry(paleditor *e,int index)
{
	if(index<0)
		return;
	LimpX_ForceRedraw(e->palwin,(index & 0xF)*CURRENT_ENTRY,-((index & 0xF0)/16+1)*CURRENT_ENTRY,((index & 0xF)+1)*CURRENT_ENTRY+4,-(index & 0xF0)/16*CURRENT_ENTRY+4);
}

#define SPRLISTWIN_WIDTH 292
#define SPRLISTWIN_HEIGHT 44

void editor_rebuildsprlistwindow(paleditor *e)
{
	limpx_window_state ws;
	int i;
	/* Delete all existing icons for simplicity */
	i = LimpX_GetWindow_Icons(e->sprlistwin); 
	while(i-- > 0)
		LimpX_DeleteIcon(LimpX_IconFromWindow(e->sprlistwin,i));
	/* Get current window width, so we can flow the icons properly */
	ws = LimpX_GetWindowOutline(e->sprlistwin);
	/* Set icon basics */
	temp_iconblock.sminx = 0;
	temp_iconblock.sminy = -SPRLISTWIN_HEIGHT;
	temp_iconblock.smaxx = SPRLISTWIN_WIDTH;
	temp_iconblock.smaxy = 0;
	temp_iconblock.flags = 0x17005001;
	/* Create icons! */
	for(i=0;i<e->nsprites;i++)
	{
		memcpy(&(temp_iconblock.data[0]),&(((int *) e->sprites[i])[1]),12);
		LimpX_NewIcon(e->sprlistwin,&temp_iconblock);
		/* Move to new position */
		temp_iconblock.sminy -= SPRLISTWIN_HEIGHT;
		temp_iconblock.smaxy -= SPRLISTWIN_HEIGHT;
	}
	/* Ensure window y min is correct */
	LimpX_SetExtent(e->sprlistwin,temp_sprlist->wminx,temp_iconblock.sminy+SPRLISTWIN_HEIGHT,temp_sprlist->wmaxx,temp_sprlist->wmaxy);
	/* Done; now just redraw window */
	LimpX_ForceRedraw(0,ws.vminx,ws.vminy,ws.vmaxx,ws.vmaxy);
}

void editor_redraw(paleditor *e,limpx_redraw_obj *r,int first)
{
	int size,i;
	size = CURRENT_ENTRY;
	LimpX_SetColour(7);
	regs.r[0] = 4+96;
	regs.r[1] = r->vminx;
	regs.r[2] = r->vmaxy;
	_kernel_swi(OS_Plot,&regs,&regs);
	regs.r[0] = 1+96;
	regs.r[1] = CURRENT_SIZE;
	regs.r[2] = -CURRENT_SIZE;
	_kernel_swi(OS_Plot,&regs,&regs);
	for(i=0;i<256;i++)
	{
		if((i>=e->selstart) && (i<e->selend))
		{
			LimpX_SetColour(0);
			regs.r[0] = 4+96;
			regs.r[1] = r->vminx+(i&15)*size;
			regs.r[2] = r->vmaxy-((i>>4)&15)*size;
			_kernel_swi(OS_Plot,&regs,&regs);
			regs.r[0] = 1+96;
			regs.r[1] = size;
			regs.r[2] = -size;
			_kernel_swi(OS_Plot,&regs,&regs);
		}
		regs.r[0] = e->pal[i*2].c;
		regs.r[3] = 256;
		regs.r[4] = 0;
		_kernel_swi(ColourTrans_SetGCOL,&regs,&regs);
		regs.r[0] = 4+96;
		regs.r[1] = r->vminx+(i&15)*size+4;
		regs.r[2] = r->vmaxy-((i>>4)&15)*size-4;
		_kernel_swi(OS_Plot,&regs,&regs);
		regs.r[0] = 1+96;
		regs.r[1] = size-8;
		regs.r[2] = -(size-8);
		_kernel_swi(OS_Plot,&regs,&regs);
		if(i == e->maskindex)
		{
			LimpX_SetColour(4<<4);
			regs.r[0] = 4;
			regs.r[1] = r->vminx+(i&15)*size;
			regs.r[2] = r->vmaxy-((i>>4)&15)*size-size;
			_kernel_swi(OS_Plot,&regs,&regs);
			regs.r[0] = 1;
			regs.r[1] = size;
			regs.r[2] = size;
			_kernel_swi(OS_Plot,&regs,&regs);
		}
	}
}

void open_editor_menu(paleditor *e,limpx_mouse_obj *m)
{
	if(e->toolflags & TOOL_SMALL)
		LimpX_SetMenuItem_ItemFlags(menu_palette,menu_palette_small,1);
	else
		LimpX_SetMenuItem_ItemFlags(menu_palette,menu_palette_small,0);
	if((e->selstart != e->selend) && (e->maskindex == e->selstart))
		LimpX_SetMenuItem_ItemFlags(menu_palette,menu_palette_mask,1);
	else
		LimpX_SetMenuItem_ItemFlags(menu_palette,menu_palette_mask,0);
	LimpX_UpdateMenuBlock(menu_palette);
	open_menu = MENU_PALETTE;
	open_menu_paleditor = e;
	LimpX_CreateMenu(menu_palette,m->x-64,m->y+12);
}

static void safeseticonnum(limpx_icon *i,int num)
{
	char buf[12];
	limpx_caret_obj c;
	sprintf(buf,"%d",num);
	LimpX_SetIcon_Text(i,buf);
	LimpX_GetCaretPosition(&c);
	if((c.window == LimpX_HandleFromIcon(LimpX_WindowFromIcon(i))) && (c.icon == LimpX_HandleFromIcon(i)))
		LimpX_SetCaretPosition(LimpX_WindowFromIcon(i),i,0,0,-1,0);
	LimpX_RedrawIcon(i);
}

static void editor_gradient(paleditor *e,int start,int end)
{
	int num;
	if(end<start)
	{
		num = end;
		end = start;
		start = num;
	}
	num = (end-start);
	int r,g,b,i;
	for(i=1;i<num;i++)
	{
		r = ((e->pal[end*2].r-e->pal[start*2].r)*i)/num;
		g = ((e->pal[end*2].g-e->pal[start*2].g)*i)/num;
		b = ((e->pal[end*2].b-e->pal[start*2].b)*i)/num;
		editor_writeentry(e,start+i,r+e->pal[start*2].r,g+e->pal[start*2].g,b+e->pal[start*2].b);
	}
	editor_redrawentries(e,start,end);
	editor_redrawpreviews(e);
	sprfile_redraw(e->file,0);
}

static paleditor *editor_drag=0;

static int func_editor_drag_box(int minx,int miny,int maxx,int maxy)
{
	limpx_mouse_obj m;
	/* Remove current event */
	LimpX_DeleteEvent(event_editor_drag_box);
	LimpX_DragASprite_Stop();
	drag_box_active = 0;
	/* Completely ignore the drag box pos and use the current mouse pos */
	LimpX_GetPointerInfo(&m);
	limpx_window *w = LimpX_WindowFromHandle(m.window);
	/* Is this one of ours? */
	if(!w)
		return 1;
	sprfile *s;
	preview *p;
	paleditor *e;
	if(!which_window(w,&s,&p,&e))
		return 1;
	if(!e)
		return 1;
	if(e->palwin != w)
		return 1;
	int i = editor_mousetoindex(e,&m);
	int j,k;
	if(i<0)
		return 1;
	/* What do we do? */
	if((e == editor_drag) && !(e->toolflags & TOOL_COPY))
	{
		/* Fixup selection range to not overflow */
		if((e->toolflags & TOOL_SWAP) && ((e->selend+i-e->selstart) > 256))
			e->selend = 256-(i-e->selstart);
		/* Remap */
		if(i == e->selstart)
			return 1;
		if((e->toolflags & TOOL_SWAP) && (i >= e->selstart) && (i < e->selend))
			return 1; /* Don't allow swapping within selection zone */
		if((e->toolflags & TOOL_SWAP) && (i+(e->selend-e->selstart)-1 >= e->selstart) && (i+(e->selend-e->selstart)-1 < e->selend))
			return 1; /* Don't allow swapping within selection zone */
		char remap[256];
		k=0;
		for(j=0;j<256;j++)
		{
			if((j>=i) && (j<i+(e->selend-e->selstart)))
				remap[e->selstart+j-i] = j;
			else
			{
				if(e->toolflags & TOOL_SWAP)
				{
					if((j>=e->selstart) && (j<e->selend))
						remap[i+j-e->selstart] = j;
					else
						remap[j] = j;
				}
				else
				{
					if(k==e->selstart)
						k = e->selend;
					remap[k++] = j;
				}
			}
		}
		e->selend += (i-e->selstart);
		e->selstart = i;
		editor_remap(e,remap);
		return 1;
	}
	/* Else overwrite */
	if((e == editor_drag) && (i > e->selstart))
	{
		/* Fixup selection range to not overflow */
		if((e->selend+i-e->selstart) > 256)
			e->selend = 256-(i-e->selstart);
		/* Go backwards to avoid overwrite problems */
		for(j=(e->selend-e->selstart)-1;j>=0;j--)
			editor_writeentry(e,j+i,e->pal[(e->selstart+j)*2].r,e->pal[(e->selstart+j)*2].g,e->pal[(e->selstart+j)*2].b);
		editor_redrawentries(e,i,i+(e->selend-e->selstart));
		editor_redrawpreviews(e);
		return 1;
	}
	for(j=0;j<editor_drag->selend-editor_drag->selstart;j++)
	{
		if(j+i<256)
			editor_writeentry(e,j+i,editor_drag->pal[(editor_drag->selstart+j)*2].r,editor_drag->pal[(editor_drag->selstart+j)*2].g,editor_drag->pal[(editor_drag->selstart+j)*2].b);
	}
	editor_redrawentries(e,i,i+(e->selend-e->selstart));
	editor_redrawpreviews(e);
	return 1;
}


void editor_clickpalette(paleditor *e,limpx_mouse_obj *m)
{
	int i = editor_mousetoindex(e,m);
	if(i<0)
		return;
	if((m->buttons == 4) && ((i < e->selstart) || (i >= e->selend)))
	{
		editor_redrawentries(e,e->selstart,e->selend);
		if((e->toolflags & TOOL_GRADIENT) && (e->selstart != e->selend))
		{
			editor_gradient(e,e->selstart,i);
			e->toolflags &= ~TOOL_GRADIENT;
			limpx_icon *ic = LimpX_IconFromWindow(e->palwin,PALICON_GRADIENT);
			LimpX_SetIcon_Flags(ic,(LimpX_GetIcon_Flags(ic) & ~LIMPX_ICONFLAG_SELECTED));
			LimpX_RedrawIcon(ic);
		}
		e->selstart = i;
		e->selend = i+1;
	}
	else if(m->buttons == 1)
	{
		editor_redrawentries(e,e->selstart,e->selend);
		if(i<=e->selstart)
			e->selstart = i;
		else
			e->selend = i+1;
	}
	else if((m->buttons == 0x40) && (e->selstart != e->selend))
	{
		/* Drag entries */
		int ox,oy;
		limpx_window_state p;
		int block[4];
		p = LimpX_GetWindowState(e->palwin);
		ox = p.vminx - p.scrollx;
		oy = p.vmaxy - p.scrolly;
		temp_iconblock.sminx = (e->selstart&0xF)*CURRENT_ENTRY;
		temp_iconblock.smaxy = -(e->selstart&0xF0)/16*CURRENT_ENTRY;
		temp_iconblock.smaxx = temp_iconblock.sminx+CURRENT_ENTRY;
		temp_iconblock.sminy = temp_iconblock.smaxy-CURRENT_ENTRY;
		LimpX_DragBox(e->palwin,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] = m->x-CURRENT_ENTRY/2;
		block[1] = m->y-CURRENT_ENTRY/2;
		block[2] = block[0]+CURRENT_ENTRY;
		block[3] = block[1]+CURRENT_ENTRY;
		LimpX_DragASprite_Start(0x85,1,"!pale",block,NULL);
		editor_drag = e;
		/* Add drag event */
		event_editor_drag_box = LimpX_NewEvent_Drag_Box(func_editor_drag_box);
		drag_box_active = 1;
		return;
	}
	editor_redrawentries(e,e->selstart,e->selend);
	safeseticonnum(LimpX_IconFromWindow(e->palwin,PALICON_R),e->pal[e->selstart*2].r);
	safeseticonnum(LimpX_IconFromWindow(e->palwin,PALICON_G),e->pal[e->selstart*2].g);
	safeseticonnum(LimpX_IconFromWindow(e->palwin,PALICON_B),e->pal[e->selstart*2].b);
}

void open_sprlistwin_menu(paleditor *e,limpx_mouse_obj *m)
{
	open_menu = MENU_SPRLIST;
	open_menu_paleditor = e;
	LimpX_CreateMenu(menu_sprlist,m->x-64,m->y+12);
}

void editor_openpreviews(paleditor *e)
{
	int i;
	for(i=0;i<LimpX_GetWindow_Icons(e->sprlistwin);i++)
		if(LimpX_GetIcon_Flags(LimpX_IconFromWindow(e->sprlistwin,i)) & LIMPX_ICONFLAG_SELECTED)
			preview_create1(e->file,e->sprites[i],e);
}

void editor_updatehint(paleditor *e,limpx_mouse_obj *m)
{
	int i = editor_mousetoindex(e,m);
	char help[64];
	editor_highlight(e,i);
	if(i<0)
		return;
	sprintf(help,"#%d R%d G%d B%d",i,e->pal[i*2].r,e->pal[i*2].g,e->pal[i*2].b);
	if(strcmp(help,LimpX_GetIcon_Text(LimpX_IconFromWindow(e->palwin,PALICON_HINT))))
	{
		LimpX_SetIcon_Text(LimpX_IconFromWindow(e->palwin,PALICON_HINT),help);
		LimpX_RedrawIcon(LimpX_IconFromWindow(e->palwin,PALICON_HINT));
	}
}

void editor_redrawpreviews(paleditor *e)
{
	preview *p;
	p = e->file->previews;
	while(p)
	{
		if(p->pal == e)
			preview_redrawall(p);
		p = p->next;
	}
}

paleditor *editor_create(sprfile *s,int index)
{
	void *spr = sprfile_getsprite(s,index);
	if(!spr)
		return 0;
	rgb *pal = sprfile_getpalette(spr);
	if(!pal)
	{
		report_error("Sprite has palette in unsupported format");
		return 0;
	}
	/* Else create */
	paleditor *e = malloc(sizeof(paleditor));
	memcpy(e->pal,pal,sizeof(e->pal));
	e->nsprites = 0;
	e->sprites = 0;
	if(last_small_flag)
	{
		e->palwin = LimpX_CreateWindow(temp_smpalette);
		e->toolflags = TOOL_SMALL;
	}
	else
	{
		e->palwin = LimpX_CreateWindow(temp_palette);
		e->toolflags = 0;
	}
	e->sprlistwin = LimpX_CreateWindow(temp_sprlist);
	e->maskindex = e->highlight = -1;
	e->selstart = 0;
	e->selend = 1;
	e->file = s;
	e->next = s->editors;
	e->prev = 0;
	if(s->editors)
		s->editors->prev = e;
	s->editors = e;
	/* Update sprite list; for now, just alloc a block big enough to list all sprites */
	e->sprites = malloc(sizeof(void *)*OSSpr_CountSprites(s->sprs));
	/* Now scan and fill */
	int i;
	for(i=0;i<OSSpr_CountSprites(s->sprs);i++)
	{
		spr = sprfile_getsprite(s,i);
		pal = sprfile_getpalette(spr);
		if((pal) && !memcmp(pal,e->pal,sizeof(e->pal)))
			e->sprites[e->nsprites++] = spr;
	}
	/* Rebuild sprlistwin, etc. */
	editor_rebuildsprlistwindow(e);
	editor_resettoolicons(e);
	/* Set window title! */
	char name[16];
	spr = sprfile_getsprite(s,index);
	memcpy(name,&(((int *) spr)[1]),12);
	name[12] = 0;
	LimpX_SetWindow_Title(e->palwin,name);
	/* Open windows */
	LimpX_OpenWindow(e->palwin);
	editor_repositionsprlistwin(e,0);
	/* Now open preview of sprite 'index' */
	preview_create1(s,spr,e);
	return e;
}

void editor_redrawpal(paleditor *e)
{
	/* redraw whole window for now */
	LimpX_ForceRedraw(e->palwin,0,-30000,30000,0);
}

void editor_repositionsprlistwin(paleditor *e,limpx_window_state *pos)
{
	limpx_window_state pos2;
	if(!e)
		return;
	if(!pos)
	{
		pos2 = LimpX_GetWindowState(e->palwin);
		pos = &pos2;
	}
	limpx_window_state ws;
	ws = LimpX_GetWindowState(e->sprlistwin);
	if((ws.vminx != pos->vmaxx) || (ws.vmaxy != pos->vmaxy) || (pos->behind != LimpX_HandleFromWindow(e->palwin)))
	{
		ws.vmaxx += pos->vmaxx-ws.vminx;
		ws.vminx = pos->vmaxx;
		ws.vminy = (ws.vmaxy-ws.vminy)+pos->vmaxy;
		ws.vmaxy = pos->vmaxy;
		ws.behind = LimpX_HandleFromWindow(e->palwin);
		LimpX_OpenWindowAt(e->sprlistwin,ws);
	}
}

void editor_writeentry(paleditor *e,int index,int r,int g,int b)
{
	if((!e) || (index < 0))
		return;
	rgb col;
	col.a = 0xff;
	col.r = RNG(r,0,255);
	col.g = RNG(g,0,255);
	col.b = RNG(b,0,255);
	e->pal[index*2] = col;
	e->pal[index*2+1] = col;
	/* Update dependents */
	int i;
	for(i=0;i<e->nsprites;i++)
	{
		rgb *pal = sprfile_getpalette(e->sprites[i]);
		if(pal)
		{
			pal[index*2] = col;
			pal[index*2+1] = col;
		}
	}
	if((e->nsprites) && (!e->file->modified))
	{
		e->file->modified = 1;
		sprfile_updatetitle(e->file);
	}
	if((index == e->selstart) && !editor_typing)
	{
		safeseticonnum(LimpX_IconFromWindow(e->palwin,PALICON_R),e->pal[e->selstart*2].r);
		safeseticonnum(LimpX_IconFromWindow(e->palwin,PALICON_G),e->pal[e->selstart*2].g);
		safeseticonnum(LimpX_IconFromWindow(e->palwin,PALICON_B),e->pal[e->selstart*2].b);
	}
}

void editor_remap(paleditor *e,char *remap)
{
	palette_remap(e->pal,remap);
	editor_redrawpal(e);
	if(!e->file->modified)
	{
		e->file->modified = 1;
		sprfile_updatetitle(e->file);
	}
	int i;
	for(i=0;i<e->nsprites;i++)
	{
		palette_remap(sprfile_getpalette(e->sprites[i]),remap);
		if(e->toolflags & TOOL_LINK)
			sprite_remap(e->file,e->sprites[i],remap);
		else
			sprfile_redraw(e->file,e->sprites[i]);
	}
}

void editor_highlight(paleditor *e,int i)
{
	if(e->highlight == i)
		return;
	e->highlight = i;
	editor_redrawpreviews(e);
}
