/*(LGPL)
----------------------------------------------------------------------
	region.c - Graphics Engine
----------------------------------------------------------------------
 * Copyright (C) 2007 David Olofson
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "region.h"
#include <stdlib.h>

static struct
{
	RGN_region	*region;
	int		x, y;
	SDL_Surface	*target;
} s;


static Uint32 gp1(SDL_Surface *surface, int x, int y)
{
	return *((Uint8 *)surface->pixels + y * surface->pitch + x);
}


static Uint32 gp2(SDL_Surface *surface, int x, int y)
{
	return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
}


static Uint32 gp3(SDL_Surface *surface, int x, int y)
{
	const SDL_PixelFormatDetails *fmt =
			SDL_GetPixelFormatDetails(surface->format);
	int bpp = fmt ? fmt->bytes_per_pixel : 3;
	Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
	return p[0] << 16 | p[1] << 8 | p[2];
#else
	return p[0] | p[1] << 8 | p[2] << 16;
#endif
}


static Uint32 gp4(SDL_Surface *surface, int x, int y)
{
	return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
}


RGN_region *RGN_ScanMask(SDL_Surface *src, Uint32 key)
{
	RGN_region *rgn = (RGN_region *)calloc(1, sizeof(RGN_region));
	Uint32 (*gp)(SDL_Surface *surface, int x, int y);
	int x, y;
	if(!rgn)
		return NULL;
	rgn->rows = src->h;
	rgn->spans = (Uint16 **)calloc(1, sizeof(Uint16 *) * src->h);
	if(!rgn->spans)
	{
		RGN_FreeRegion(rgn);
		return NULL;
	}
	const SDL_PixelFormatDetails *src_fmt =
			SDL_GetPixelFormatDetails(src->format);
	if(!src_fmt)
	{
		RGN_FreeRegion(rgn);
		return NULL;
	}
	switch(src_fmt->bytes_per_pixel)
	{
	  case 1:
		gp = gp1;
		break;
	  case 2:
		gp = gp2;
		break;
	  case 3:
		gp = gp3;
		break;
	  case 4:
		gp = gp4;
		break;
	  default:
		RGN_FreeRegion(rgn);
		return NULL;
	}
	SDL_LockSurface(src);
	for(y = 0; y < src->h; ++y)
	{
		int i = 0;
		int drawing = 0;
		Uint16 *sp = (Uint16 *)malloc(sizeof(Uint16) * 2 * (src->w + 1));
		if(!sp)
		{
			SDL_UnlockSurface(src);
			RGN_FreeRegion(rgn);
			return NULL;
		}
		for(x = 0; x < src->w; ++x)
		{
			int nd = (gp(src, x, y) == key);
			if(nd != drawing)
			{
				sp[i++] = x;
				drawing = nd;
			}
		}
		if(drawing)
			sp[i++] = src->w;
		sp[i++] = 0;
		sp[i++] = 0;
		rgn->spans[y] = (Uint16 *)realloc(sp, sizeof(Uint16) * i);
		if(!rgn->spans[y])
			rgn->spans[y] = sp;
	}
	SDL_UnlockSurface(src);
	return rgn;
}


void RGN_FreeRegion(RGN_region *rgn)
{
	if(!rgn)
		return;
	if(rgn->spans)
	{
		int i;
		for(i = rgn->rows - 1; i >= 0; --i)
			free(rgn->spans[i]);
		free(rgn->spans);
	}
	free(rgn);
}


void RGN_Target(SDL_Surface *tgt)
{
	s.target = tgt;
}


void RGN_SetRegion(RGN_region *rgn, int xpos, int ypos)
{
	s.region = rgn;
	s.x = xpos;
	s.y = ypos;
}


int RGN_Blit(SDL_Surface *src, SDL_Rect *sr, int x, int y)
{
// FIXME: Clipping is only half done here...
	int yy0, yy, yy1;
	int sxmin, sxmax, symin, symax;
	SDL_Rect sr2;
	if(!s.region)
	{
		SDL_Rect dr;
		dr.x = x;
		dr.y = y;
		return SDL_BlitSurface(src, sr, s.target, &dr);
	}
	if(sr)
	{
		sxmin = sr->x;
		symin = sr->y;
		sxmax = sxmin + sr->w;
		symax = symin + sr->h;
	}
	else
	{
		sxmin = 0;
		symin = 0;
		sxmax = src->w;
		symax = src->h;
	}
	yy0 = s.y;
	if(yy0 >= y + (symax - symin))
		return 0;
	if(yy0 < y)
		yy0 = y;
	yy1 = s.y + s.region->rows;
	if(y >= yy1)
		return 0;
	if(yy1 > y + (symax - symin))
		yy1 = y + (symax - symin);
	sr2.h = 1;
	for(yy = yy0; yy < yy1; ++yy)
	{
		SDL_Rect dr;
		Uint16 *spans = s.region->spans[yy - s.y];
		sr2.y = symin + y - yy0;
		dr.y = yy;
		for( ; spans[0] && spans[1] ; spans += 2)
		{
			int x0 = s.x + spans[0];
			int x1 = s.x + spans[1];
			dr.x = x0;
			sr2.x = sxmin + x0 - x;
			sr2.w = x1 - x0;
			SDL_BlitSurface(src, &sr2, s.target, &dr);
		}
	}
	return 0;
}
