/* GnarPlot paint sprite C code V0.31 22/2/08
   Copyright 2008 Jeffrey Lee
   This file is part of GnarlPlot.
   GnarlPlot 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.
   GnarlPlot 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 GnarlPlot.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef _GP_PAINTSPR_C
#define _GP_PAINTSPR_C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"

#include "spr.h"
#include "col.h"

/* Code for handling Paint sprites */

typedef struct {
	int _width,_height; /* GP values */
	int _ps,_mt;
	int _len;
	int ofs; /* Sprite block values */
	char name[12];
	int width;
	int height;
	int firstbit,lastbit;
	int sprofs;
	int maskofs;
	int type;
	char d[0];
} _gp_paint_spr;

static gp_spr *gp_spr_paint_create(gp_sprfmt *f,int w,int h,int ox,int oy)
{
	return 0; /* Can't create outright */
}

static gp_spr *gp_spr_paint_fromscreen(gp_sprfmt *f,gp_screen *scr,int x,int y,int w,int h,int ox,int oy)
{
	/* Create a sprite format which matches that of the screen */
	gp_spr *s;
	int sz;
	_gp_paint_spr *spr;
	int cx,cy,gap;
	if ((w < 1) || (h < 1) || (scr == 0) || (f == 0))
		return 0;
	s = malloc(sizeof(gp_spr));
	if (s == 0)
		return 0;
	s->f = f;
	if (scr->ps == 0)
		sz = ((w+3) & ~3)*h; /* Must round size up */
	else if (scr->ps == 1)
		sz = ((w+1) & ~1)*h*2;
	else /* 2 */
		sz = w*h*4;
	if (scr->mt)
		sz = sz*2;
	sz += sizeof(_gp_paint_spr);
	spr = malloc(sz);
	if (spr == 0)
	{
		free(s);
		return 0;
	}
	s->d = spr;
	spr->_width = w;
	spr->_height = h;
	spr->_ps = scr->ps;
	if (scr->mt)
		spr->_mt = GP_MT_ONOFF;
	else
		spr->_mt = GP_MT_NONE;
	spr->_len = sz;
	if (scr->ps == 0)
		gap = (-w) & 3; /* Number of extra bytes per line */
	else if (scr->ps == 1)
		gap = (w & 1)*2;
	else
		gap = 0;
	spr->ofs = 0; /* No next sprite */
	strncpy(spr->name,"gnarlplot",12);
	spr->width = (((w << scr->ps) + gap) >> 2)-1;
	spr->height = h-1;
	spr->firstbit = 0;
	spr->lastbit = (31+(gap*8)) & 31;
	spr->sprofs = 44;
	if (spr->_mt != GP_MT_NONE)
		spr->maskofs = 44 + (sz-sizeof(_gp_paint_spr))/2;
	else
		spr->maskofs = 44;
	if (scr->ps == 0)
		spr->type = 13;
	else
		spr->type = ((4+scr->ps) << 27) + (90 << 14) + (90 << 1) + 1;
	/* Now write the pixels */
	for (cy=0;cy<h;cy++)
		for (cx=0;cx<w;cx++)
			if (scr->ps == 0)
			{
				spr->d[cx+cy*(gap+w)] = gp_screen_readpix(scr,x+cx,y+cy,0);
				if (spr->_mt)
					spr->d[cx+(cy+h)*(gap+w)] = gp_screen_readmask(scr,x+cx,y+cy,GP_MT_ONOFF,0);
			}
			else if (scr->ps == 1)
			{
				((short *) spr->d)[cx+cy*(gap/2+w)] = gp_screen_readpix(scr,x+cx,y+cy,1) & 0x7FFF;
				if (spr->_mt)
					((short *) spr->d)[cx+(cy+h)*(gap/2+w)] = gp_screen_readmask(scr,x+cx,y+cy,GP_MT_ONOFF,1);
			}
			else
			{
				((int *) spr->d)[cx+cy*w] = gp_screen_readpix(scr,x+cx,y+cy,2) & 0xFFFFFF;
				if (spr->_mt)
					((int *) spr->d)[cx+(cy+h)*w] = gp_screen_readmask(scr,x+cx,y+cy,GP_MT_ONOFF,2);
			}
	return s;
}

static gp_spr *gp_spr_paint_fromspr(gp_sprfmt *f,gp_spr *src,int x,int y,int w,int h,int ox,int oy)
{
	/* Create a sprite format which matches that of the source */
	gp_spr *s;
	int sz;
	_gp_paint_spr *spr;
	int cx,cy,gap;
	if ((w < 1) || (h < 1) || (src == 0) || (f == 0))
		return 0;
	s = malloc(sizeof(gp_spr));
	if (s == 0)
		return 0; 
	s->f = f;
	if ((src->f->getps)(src) == 0)
		sz = ((w+3) & ~3)*h; /* Must round size up */
	else if ((src->f->getps)(src) == 1)
		sz = ((w+1) & ~1)*h*2;
	else /* 2 */
		sz = w*h*4;
	if ((src->f->getmt)(src) != GP_MT_NONE)
		sz = sz*2 + sizeof(_gp_paint_spr);
	else
		sz += sizeof(_gp_paint_spr);
	spr = malloc(sz);
	if (spr == 0)
	{
		free(s);
		return 0;
	}
	s->d = spr;
	spr->_width = w;
	spr->_height = h;
	spr->_ps = (src->f->getps)(src);
	if ((src->f->getmt)(src) != GP_MT_NONE)
		spr->_mt = GP_MT_ONOFF;
	else
		spr->_mt = GP_MT_NONE;
	spr->_len = sz;
	if (spr->_ps == 0)
		gap = (-w) & 3; /* Number of extra bytes per line */
	else if (spr->_ps == 1)
		gap = (w & 1)*2;
	else
		gap = 0;
	spr->ofs = 0; /* No next sprite */
	strncpy(spr->name,"gnarlplot",12);
	spr->width = (((w << spr->_ps) + gap) >> 2)-1;
	spr->height = h-1;
	spr->firstbit = 0;
	spr->lastbit = (31+(gap*8)) & 31;
	spr->sprofs = 44;
	if (spr->_mt != GP_MT_NONE)
		spr->maskofs = 44 + (sz-sizeof(_gp_paint_spr))/2;
	else
		spr->maskofs = 44;
	if (spr->_ps == 0)
		spr->type = 13;
	else
		spr->type = ((4+spr->_ps) << 27) + (90 << 14) + (90 << 1) + 1;
	/* Now write the pixels */
	for (cy=0;cy<h;cy++)
		for (cx=0;cx<w;cx++)
			if (spr->_ps == 0)
			{
				spr->d[cx+cy*(gap+w)] = (src->f->getpix)(src,x+cx,y+cy,0);
				if (spr->_mt != GP_MT_NONE)
					spr->d[cx+(cy+h)*(gap+w)] = (src->f->getmask)(src,x+cx,y+cy,GP_MT_ONOFF,0);
			}
			else if (spr->_ps == 1)
			{
				((short *) spr->d)[cx+cy*(gap/2+w)] = (src->f->getpix)(src,x+cx,y+cy,1) & 0x7FFF;
				if (spr->_mt != GP_MT_NONE)
					((short *) spr->d)[cx+(cy+h)*(gap/2+w)] = (src->f->getmask)(src,x+cx,y+cy,GP_MT_ONOFF,1);
			}
			else
			{
				((int *) spr->d)[cx+cy*w] = (src->f->getpix)(src,x+cx,y+cy,2) & 0xFFFFFF;
				if (spr->_mt != GP_MT_NONE)
					((int *) spr->d)[cx+(cy+h)*w] = (src->f->getmask)(src,x+cx,y+cy,GP_MT_ONOFF,2);
			}
	return s;
}

static int gp_spr_paint_getsize(gp_spr *s)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->d == 0))
		return 0;
	spr = s->d;
	return spr->_len;
}

static int gp_spr_paint_getps(gp_spr *s)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->d == 0))
		return 0;
	spr = s->d;
	return spr->_ps;
}

static int gp_spr_paint_getmt(gp_spr *s)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->d == 0))
		return 0;
	spr = s->d;
	return spr->_mt;
}

static int gp_spr_paint_getw(gp_spr *s)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->d == 0))
		return 0;
	spr = s->d;
	return spr->_width;
}

static int gp_spr_paint_geth(gp_spr *s)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->d == 0))
		return 0;
	spr = s->d;
	return spr->_height;
}

static int gp_spr_paint_getox(gp_spr *s)
{
	return 0;
}

static int gp_spr_paint_getoy(gp_spr *s)
{
	return 0;
}

static int gp_spr_paint_getpix(gp_spr *s,int x,int y,int ps)
{
	_gp_paint_spr *spr;
	_kernel_swi_regs regs;
	if ((s == 0) || (s->d == 0) || (x < 0) || (y < 0))
		return 0;
	spr = s->d;
	if ((x >= spr->_width) || (y >= spr->_height))
		return 0;
	regs.r[0] = 512+41;
	regs.r[1] = (int) &spr->ofs; /* Setting the sprite area ptr to the sprite itself seems to work without any nasty side effects... */
	regs.r[2] = (int) &spr->ofs;
	regs.r[3] = x;
	regs.r[4] = (spr->_height-1)-y;
	_kernel_swi(OS_SpriteOp,&regs,&regs);
	if (spr->_ps == 0)
		return gp_col_conv(spr->_ps,gp_col_from_tbgr(regs.r[5] | regs.r[6]),ps); /* TTBBGGRR moose */
	else
		return gp_col_conv(spr->_ps,regs.r[5] | regs.r[6],ps);
}

static int gp_spr_paint_getmask(gp_spr *s,int x,int y,int mt,int ps)
{
	_gp_paint_spr *spr;
	_kernel_swi_regs regs;
	if ((s == 0) || (s->d == 0) || (x < 0) || (y < 0))
		return gp_mask_conv(GP_MT_ONOFF,0,0,mt,ps); /* Transparent */
	spr = s->d;
	if ((x >= spr->_width) || (y >= spr->_height))
		return gp_mask_conv(GP_MT_ONOFF,0,0,mt,ps);
	if (spr->_mt == GP_MT_NONE)
		return gp_mask_conv(GP_MT_NONE,0,0,mt,ps); /* None */
	regs.r[0] = 512+43;
	regs.r[1] = (int) &spr->ofs;
	regs.r[2] = (int) &spr->ofs;
	regs.r[3] = x;
	regs.r[4] = (spr->_height-1)-y;
	_kernel_swi(OS_SpriteOp,&regs,&regs);
	return gp_mask_conv(spr->_mt,spr->_ps,regs.r[5],mt,ps);
}

static int gp_spr_paint_setox(gp_spr *s,int ox)
{
	return 1; /* Can't set ox */
}

static int gp_spr_paint_setoy(gp_spr *s,int oy)
{
	return 1; /* Can't set oy */
}

static int gp_spr_paint_setpix(gp_spr *s,int x,int y,int ps,int c)
{
	_gp_paint_spr *spr;
	_kernel_swi_regs regs;
	if ((s == 0) || (s->d == 0) || (x < 0) || (y < 0))
		return 1;
	spr = s->d;
	if ((x >= spr->_width) || (y >= spr->_height))
		return 1;
	c = gp_col_conv(ps,c,spr->_ps);
	regs.r[0] = 512+42;
	regs.r[1] = (int) &spr->ofs;
	regs.r[2] = (int) &spr->ofs;
	regs.r[3] = x;
	regs.r[4] = (spr->_height-1)-y;
	if (spr->_ps == 0)
	{
		c = gp_col_to_tbgr(c);
		regs.r[5] = c & 63; /* BBGGRR */
		regs.r[6] = c & 192; /* TT000000 */
	}
	else
	{
		regs.r[5] = c;
		regs.r[6] = 0;
	}
	return (_kernel_swi(OS_SpriteOp,&regs,&regs) != 0);
}

static int gp_spr_paint_setmask(gp_spr *s,int x,int y,int mt,int ps,int c)
{
	_gp_paint_spr *spr;
	_kernel_swi_regs regs;
	if ((s == 0) || (s->d == 0) || (x < 0) || (y < 0))
		return 1;
	spr = s->d;
	if ((x >= spr->_width) || (y >= spr->_height) || (spr->_mt == GP_MT_NONE))
		return 1;
	c = gp_mask_conv(ps,mt,c,spr->_ps,spr->_mt);
	regs.r[0] = 512+44;
	regs.r[1] = (int) &spr->ofs;
	regs.r[2] = (int) &spr->ofs;
	regs.r[3] = x;
	regs.r[4] = (spr->_height-1)-y;
	regs.r[5] = (c != 0);
	return (_kernel_swi(OS_SpriteOp,&regs,&regs) != 0);
}

static gp_screen *gp_spr_paint_toscreen(gp_spr *s,gp_screen *scr)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->d == 0))
		return 0;
	spr = s->d;
	if (spr->_mt != GP_MT_NONE)
		return 0;
	if(!scr)
		scr = malloc(sizeof(gp_screen));
	if(!scr)
		return 0;
	scr->width = spr->_width;
	scr->height = spr->_height;
	scr->ps = spr->_ps;
	scr->mt = spr->_mt;
	scr->gap = (31-(spr->lastbit-spr->firstbit))/8;
	scr->bank0 = scr->bank1 = (void *) (((int) spr) + offsetof(_gp_paint_spr,ofs) + spr->sprofs + (spr->firstbit/8));
	return scr;
}

gp_sprfmt gp_spr_paint = {
	gp_spr_paint_create, gp_spr_paint_fromscreen, gp_spr_paint_fromspr, gp_spr_gen_delete, gp_spr_paint_getsize, gp_spr_paint_getps, gp_spr_paint_getmt, gp_spr_paint_getw, gp_spr_paint_geth, gp_spr_paint_getox, gp_spr_paint_getoy, gp_spr_paint_getpix, gp_spr_paint_getmask, gp_spr_gen_setw, gp_spr_gen_seth, gp_spr_paint_setox, gp_spr_paint_setoy, gp_spr_paint_setpix, gp_spr_paint_setmask, gp_spr_gen_plot, gp_spr_paint_toscreen, gp_spr_gen_tplot
};

int gp_spr_paint_size(gp_spr *s)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->f != &gp_spr_paint) || (s->d == 0))
		return 0; /* Fail */
	spr = s->d;
	return spr->_len - offsetof(_gp_paint_spr,ofs);
}

char *gp_spr_paint_getname(gp_spr *s,char *name)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->f != &gp_spr_paint) || (s->d == 0))
		return 0; /* Fail */
	spr = s->d;
	if (name)
	{
		strncpy(name,spr->name,12);
		name[12] = 0; /* Ensure it's terminated */
	}
	else
		name = spr->name;
	return name;
}

void gp_spr_paint_setname(gp_spr *s,char *name)
{
	_gp_paint_spr *spr;
	if ((s == 0) || (s->f != &gp_spr_paint) || (s->d == 0) || (name == 0))
		return; /* Fail */
	spr = s->d;
	strncpy(spr->name,name,12);
}

int gp_spr_paint_find(gp_spr **s,int n,char *name)
{
	int i;
	_gp_paint_spr *spr;
	if (s == 0)
		return -1;
	for (i=0;i<n;i++)
		if ((s[i]) && (s[i]->f == &gp_spr_paint))
		{
			spr = s[i]->d;
			if (strncmp(name,spr->name,12) == 0)
				return i;
		}
	return -1;
}

static int fputw(int i,FILE *f)
{
	if (fputc(i & 255,f) == EOF) return EOF;
	if (fputc((i >> 8) & 255,f) == EOF) return EOF;
	if (fputc((i >> 16) & 255,f) == EOF) return EOF;
	if (fputc((i >> 24) & 255,f) == EOF) return EOF;
	return 0;
}

static int fgetw(FILE *f,int *i)
{
	int t;
	t = fgetc(f); if (t == EOF) return EOF; *i = t;
	t = fgetc(f); if (t == EOF) return EOF; *i += t << 8;
	t = fgetc(f); if (t == EOF) return EOF; *i += t << 16;
	t = fgetc(f); if (t == EOF) return EOF; *i += t << 24;
	return 0;
}

int gp_spr_paint_save(gp_spr *s,FILE *f)
{
	int sz;
	_gp_paint_spr *spr;
	if ((s == 0) || (s->f != &gp_spr_paint) || (s->d == 0) || (f == 0))
		return 1; /* Fail */
	spr = s->d;
	/* ... else dump the OS sprite block */
	sz = spr->_len - offsetof(_gp_paint_spr,ofs);
	if (fputw(sz,f) == EOF) return 1;
	/* Now just dump the data as normal, from name onwards */
	if (fwrite(spr->name,sz-4,1,f) != 1)
		return 1;
	return 0;
}

gp_spr *gp_spr_paint_load(FILE *f)
{
	int nextofs;
	gp_spr *s;
	_gp_paint_spr *spr;
	_kernel_swi_regs regs;
	if (f == 0)
		return 0;
	if (fgetw(f,&nextofs) == EOF) return 0;
	s = malloc(sizeof(gp_spr));
	if (s == 0)
		return 0;
	spr = malloc(nextofs+offsetof(_gp_paint_spr,name));
	if (spr == 0)
	{
		free(s);
		return 0;
	}
	s->f = &gp_spr_paint;
	s->d = spr;
	if (fread(spr->name,nextofs-4,1,f) != 1)
	{
		free(s);
		free(spr);
		return 0;
	}
	spr->ofs = 0;
	/* Now interrogate the sprite to get its data... */
	if (spr->sprofs == spr->maskofs)
		spr->_mt = GP_MT_NONE;
	else
		spr->_mt = GP_MT_ONOFF;
	spr->_height = spr->height+1;
	spr->_len = nextofs+offsetof(_gp_paint_spr,name);
	regs.r[0] = 40+512;
	regs.r[1] = (int) &spr->ofs;
	regs.r[2] = (int) &spr->ofs;
	if (_kernel_swi(OS_SpriteOp,&regs,&regs))
		return 0;
	spr->_width = regs.r[3];
	switch (((unsigned int) spr->type) >> 27)
	{
	case 5: spr->_ps = 1; break;
	case 6: case 8: spr->_ps = 2; break;
	default: spr->_ps = 0; /* Best guess */
	}
	return s;
}

gp_spr **gp_spr_paint_loadfile(char *fname,int *n)
{
	/* Load all the sprites from file 'f', returning an array of their pointers and the number loaded */
	FILE *f;
	int i;
	gp_spr **s;
	if ((fname == 0) || (n == 0) || ((f = fopen(fname,"r")) == 0))
		return 0;
	if ((fgetw(f,n) == EOF) || (*n == 0))
	{
		fclose(f);
		return 0;
	}
	if (fgetw(f,&i) == EOF)
	{
		fclose(f);
		return 0;
	}
	if (fseek(f,i-4,SEEK_SET) != 0)
	{
		fclose(f);
		return 0;
	}
	s = malloc((*n)*4);
	if (s == 0)
	{
		fclose(f);
		return 0;
	}
	for (i=0;i<*n;i++)
	{
		s[i] = gp_spr_paint_load(f);
		if (s[i] == 0)
		{
			fclose(f);
			free(s);
			return 0;
		}
	}
	fclose(f);
	return s;
}

int gp_spr_paint_savefile(char *fname,int n,gp_spr **s)
{
	/* Save all sprites in 's' to a file */
	FILE *f;
	int i,sz;
	_kernel_swi_regs regs;
	gp_spr **l;
	if ((fname == 0) || (n == 0) || ((f = fopen(fname,"w")) == 0))
		return 1; /* Fail */
	if (fputw(n,f) == EOF)
	{
		fclose(f);
		return 1;
	}
	if (fputw(16,f) == EOF)
	{
		fclose(f);
		return 1;
	}
	l = malloc(4*n);
	if (l == 0)
		return 0;
	sz = 0;
	for (i=0;i<n;i++)
	{
		if ((s[i] == 0) || (s[i]->f == &gp_spr_paint))
			l[i] = s[i];
		else
			l[i] = gp_spr_paint_fromspr(&gp_spr_paint,s[i],0,0,(s[i]->f->getw)(s[i]),(s[i]->f->geth)(s[i]),0,0);
		sz += gp_spr_paint_size(l[i]);
	}
	if (fputw(sz+16,f) == EOF)
	{
		for (i=0;i<n;i++)
			if ((l[i] != s[i]) && (l[i]))
				(l[i]->f->delete)(l[i]);
		free(l);
		fclose(f);
		return 1;
	}
	for (i=0;i<n;i++)
		if (gp_spr_paint_save(l[i],f) != 0)
		{
			for (i=0;i<n;i++)
				if ((l[i] != s[i]) && (l[i]))
					(l[i]->f->delete)(l[i]);
			free(l);
			fclose(f);
			return 1;
		}
	fclose(f);
	for (i=0;i<n;i++)
		if ((l[i] != s[i]) && (l[i]))
			(l[i]->f->delete)(l[i]);
	free(l);
	regs.r[0] = 18;
	regs.r[1] = (int) fname;
	regs.r[2] = 0xff9;
	if (_kernel_swi(OS_File,&regs,&regs))
		return 1;
	return 0;
}

#endif
