/***************************************************************************

   Caveman Ninja Video emulation - Bryan McPhail, mish@tendril.co.uk

****************************************************************************/

#include "driver.h"
#include "vidhrdw/generic.h"
#include "deco16ic.h"

/******************************************************************************/

static int cninja_bank_callback(const int bank)
{
	if ((bank>>4)&0xf) return 0x0000; /* Only 2 banks */
	return 0x1000;
}

static int edrandy_bank_callback(const int bank)
{
	if ((bank>>4)&0xf) return 0x0000; /* Only 2 banks */
	return 0x1000;
}

static int robocop2_bank_callback(const int bank)
{
	return (bank&0x30)<<8;
}

static int mutantf_1_bank_callback(const int bank)
{
	return ((bank>>4)&0x3)<<12;
}

static int mutantf_2_bank_callback(const int bank)
{
	return ((bank>>5)&0x1)<<14;
}

/******************************************************************************/

VIDEO_START( cninja )
{
	if (deco16_2_video_init(1))
		return 1;

	deco16_set_tilemap_bank_callback(2,cninja_bank_callback);
	deco16_set_tilemap_bank_callback(3,cninja_bank_callback);
	deco16_set_tilemap_colour_base(3,48);

	return 0;
}

VIDEO_START( stoneage )
{
	if (deco16_2_video_init(1))
		return 1;

	deco16_set_tilemap_bank_callback(2,edrandy_bank_callback);
	deco16_set_tilemap_bank_callback(3,edrandy_bank_callback);
	deco16_set_tilemap_colour_base(3,48);

	/* The bootleg has broken scroll registers */
	tilemap_set_scrolldx(deco16_get_tilemap(3,0),-10,-10);
	tilemap_set_scrolldx(deco16_get_tilemap(1,0),-10,-10);
	tilemap_set_scrolldx(deco16_get_tilemap(0,1),2,2);

	return 0;
}

VIDEO_START( edrandy )
{
	if (deco16_2_video_init(0))
		return 1;

	deco16_set_tilemap_bank_callback(2,edrandy_bank_callback);
	deco16_set_tilemap_bank_callback(3,edrandy_bank_callback);
	deco16_set_tilemap_colour_base(3,48);

	return 0;
}

VIDEO_START( robocop2 )
{
	if (deco16_2_video_init(0))
		return 1;

	deco16_set_tilemap_bank_callback(1,robocop2_bank_callback);
	deco16_set_tilemap_bank_callback(2,robocop2_bank_callback);
	deco16_set_tilemap_bank_callback(3,robocop2_bank_callback);
	deco16_set_tilemap_colour_base(3,48);

	return 0;
}

VIDEO_START( mutantf )
{
	if (deco16_2_video_init(0))
		return 1;

	deco16_set_tilemap_bank_callback(0,mutantf_1_bank_callback);
	deco16_set_tilemap_bank_callback(1,mutantf_2_bank_callback);
	deco16_set_tilemap_bank_callback(2,mutantf_1_bank_callback);
	deco16_set_tilemap_bank_callback(3,mutantf_1_bank_callback);

	deco16_set_tilemap_colour_base(1,0x30);
	deco16_set_tilemap_colour_base(2,0x20);
	deco16_set_tilemap_colour_base(3,0x40);

	alpha_set_level(0x80);

	return 0;
}

/******************************************************************************/

VIDEO_EOF( cninja )
{
	deco16_raster_display_position=0;
}

static void raster_pf3_draw(struct mame_bitmap *bitmap, const struct rectangle *cliprect, int flags, int pri)
{
	struct tilemap *tilemap=deco16_get_tilemap(2,0);
	int ptr=0,start,end=0;
	struct rectangle clip;
	int overflow=deco16_raster_display_position;

	clip.min_x = cliprect->min_x;
	clip.max_x = cliprect->max_x;

	/* Finish list up to end of visible display */
	deco16_raster_display_list[overflow++]=255;
	deco16_raster_display_list[overflow++]=deco16_pf12_control[1];
	deco16_raster_display_list[overflow++]=deco16_pf12_control[2];
	deco16_raster_display_list[overflow++]=deco16_pf12_control[3];
	deco16_raster_display_list[overflow++]=deco16_pf12_control[4];
	deco16_raster_display_list[overflow++]=deco16_pf34_control[1];
	deco16_raster_display_list[overflow++]=deco16_pf34_control[2];
	deco16_raster_display_list[overflow++]=deco16_pf34_control[3];
	deco16_raster_display_list[overflow++]=deco16_pf34_control[4];

	while (ptr<overflow) {
		start=end;
		end=deco16_raster_display_list[ptr++];

		/* Restore state of registers before IRQ */
		deco16_pf12_control[1]=deco16_raster_display_list[ptr++];
		deco16_pf12_control[2]=deco16_raster_display_list[ptr++];
		deco16_pf12_control[3]=deco16_raster_display_list[ptr++];
		deco16_pf12_control[4]=deco16_raster_display_list[ptr++];
		deco16_pf34_control[1]=deco16_raster_display_list[ptr++];
		deco16_pf34_control[2]=deco16_raster_display_list[ptr++];
		deco16_pf34_control[3]=deco16_raster_display_list[ptr++];
		deco16_pf34_control[4]=deco16_raster_display_list[ptr++];

		clip.min_y = start;
		clip.max_y = end;

		/* Update tilemap for this register state, and draw */
		deco16_pf34_update(deco16_pf3_rowscroll,deco16_pf4_rowscroll);
		tilemap_draw(bitmap,&clip,tilemap,flags,pri);
	}
}

/******************************************************************************/

static void cninja_drawsprites(struct mame_bitmap *bitmap, const struct rectangle *cliprect)
{
	int offs;

	for (offs = 0x400-4;offs >=0 ;offs -= 4)
	{
		int x,y,sprite,colour,multi,fx,fy,inc,flash,mult,pri=0;

		sprite = buffered_spriteram16[offs+1];
		if (!sprite) continue;

		x = buffered_spriteram16[offs+2];

		/* Sprite/playfield priority */
		switch (x&0xc000) {
		case 0x0000: pri=0; break;
		case 0x4000: pri=0xf0; break;
		case 0x8000: pri=0xf0|0xcc; break;
		case 0xc000: pri=0xf0|0xcc; break; /* Perhaps 0xf0|0xcc|0xaa (Sprite under bottom layer) */
		}

		y = buffered_spriteram16[offs];
		flash=y&0x1000;
		if (flash && (cpu_getcurrentframe() & 1)) continue;
		colour = (x >> 9) &0x1f;

		fx = y & 0x2000;
		fy = y & 0x4000;
		multi = (1 << ((y & 0x0600) >> 9)) - 1;	/* 1x, 2x, 4x, 8x height */

		x = x & 0x01ff;
		y = y & 0x01ff;
		if (x >= 256) x -= 512;
		if (y >= 256) y -= 512;
		x = 240 - x;
		y = 240 - y;

		sprite &= ~multi;
		if (fy)
			inc = -1;
		else
		{
			sprite += multi;
			inc = 1;
		}

		if (flip_screen) {
			y=240-y;
			x=240-x;
			if (fx) fx=0; else fx=1;
			if (fy) fy=0; else fy=1;
			mult=16;
		}
		else mult=-16;

		while (multi >= 0)
		{
			pdrawgfx(bitmap,Machine->gfx[3],
					sprite - multi * inc,
					colour,
					fx,fy,
					x,y + mult * multi,
					cliprect,TRANSPARENCY_PEN,0,pri);

			multi--;
		}
	}
}

static void robocop2_drawsprites(struct mame_bitmap *bitmap, const struct rectangle *cliprect)
{
	int offs;

	for (offs = 0x400-4;offs >=0 ;offs -= 4)
	{
		int x,y,sprite,colour,multi,fx,fy,inc,flash,mult,pri=0;
		sprite = buffered_spriteram16[offs+1];
		if (!sprite) continue;

		x = buffered_spriteram16[offs+2];

		/* Sprite/playfield priority */
		switch (x&0xc000) {
		case 0x0000: pri=0; break;
		case 0x4000: pri=0xf0; break;
		case 0x8000: pri=0xf0|0xcc; break;
		case 0xc000: pri=0xf0|0xcc; break; /* Perhaps 0xf0|0xcc|0xaa (Sprite under bottom layer) */
		}

		y = buffered_spriteram16[offs];
		flash=y&0x1000;
		if (flash && (cpu_getcurrentframe() & 1)) continue;
		colour = (x >> 9) &0x1f;

		fx = y & 0x2000;
		fy = y & 0x4000;
		multi = (1 << ((y & 0x0600) >> 9)) - 1;	/* 1x, 2x, 4x, 8x height */

		x = x & 0x01ff;
		y = y & 0x01ff;
		if (x >= 320) x -= 512;
		if (y >= 256) y -= 512;
		x = 304 - x;
		y = 240 - y;

		sprite &= ~multi;
		if (fy)
			inc = -1;
		else
		{
			sprite += multi;
			inc = 1;
		}

		if (flip_screen) {
			y=240-y;
			x=304-x;
			if (fx) fx=0; else fx=1;
			if (fy) fy=0; else fy=1;
			mult=16;
		}
		else mult=-16;

		while (multi >= 0)
		{
			pdrawgfx(bitmap,Machine->gfx[3],
					sprite - multi * inc,
					colour,
					fx,fy,
					x,y + mult * multi,
					cliprect,TRANSPARENCY_PEN,0,pri);

			multi--;
		}
	}
}

static void mutantf_drawsprites(struct mame_bitmap *bitmap, const struct rectangle *cliprect, const data16_t *spriteptr, int gfxbank)
{
	int offs,end,inc;

	/*
        Alternate format from most 16 bit games - same as Captain America

        Word 0:
            0x8000: Y flip
            0x4000: X flip
            0x2000: Flash (Sprite toggles on/off every frame)
            0x1fff: Y value
        Word 1:
            0xffff: X value
        Word 2:
            0xf000: Block height
            0x0f00: Block width
            0x00e0: Unused?
            0x001f: Colour
        Word 3:
            0xffff: Sprite value
    */

	/* This may look strange, but the alpha-blended sprite chip definitely draws end to
        front, ie, reversed from normal pdrawgfx style. */
	if (gfxbank==4) {
		offs=0;
		end=0x400;
		inc=4;
	} else {
		offs=0x3fc;
		end=-4;
		inc=-4;
	}

	while (offs!=end)
	{
		int x,y,sprite,colour,fx,fy,w,h,sx,sy,x_mult,y_mult;
		int trans=TRANSPARENCY_PEN;

		sprite = spriteptr[offs+3];
		if (!sprite) {
			offs+=inc;
			continue;
		}

		sx = spriteptr[offs+1];

		h = (spriteptr[offs+2]&0xf000)>>12;
		w = (spriteptr[offs+2]&0x0f00)>> 8;

		sy = spriteptr[offs];
		if ((sy&0x2000) && (cpu_getcurrentframe() & 1)) {
			offs+=inc;
			continue;
		}

		colour = (spriteptr[offs+2] >>0) & 0x1f;

		if (gfxbank==4) { /* Seems to be always alpha'd */
			trans=TRANSPARENCY_ALPHA;
			colour&=0xf;
		}

		fx = (spriteptr[offs+0]&0x4000);
		fy = (spriteptr[offs+0]&0x8000);

		if (flip_screen) {
			if (fx) fx=0; else fx=1;
			if (fy) fy=0; else fy=1;

			sx = sx & 0x01ff;
			sy = sy & 0x01ff;
			if (sx>0x180) sx=-(0x200 - sx);
			if (sy>0x180) sy=-(0x200 - sy);

			if (fx) { x_mult=-16; sx+=16*w; } else { x_mult=16; sx-=16; }
			if (fy) { y_mult=-16; sy+=16*h; } else { y_mult=16; sy-=16; }
		} else {
			sx = sx & 0x01ff;
			sy = sy & 0x01ff;
			if (sx&0x100) sx=-(0x100 - (sx&0xff));
			if (sy&0x100) sy=-(0x100 - (sy&0xff));
			sx = 304 - sx;
			sy = 240 - sy;
			if (sx >= 432) sx -= 512;
			if (sy >= 384) sy -= 512;
			if (fx) { x_mult=-16; sx+=16; } else { x_mult=16; sx-=16*w; }
			if (fy) { y_mult=-16; sy+=16; } else { y_mult=16; sy-=16*h; }
		}

		for (x=0; x<w; x++) {
			for (y=0; y<h; y++) {
				pdrawgfx(bitmap,Machine->gfx[gfxbank],
						sprite + y + h * x,
						colour,
						fx,fy,
						sx + x_mult * (w-x),sy + y_mult * (h-y),
						&Machine->visible_area,trans,0,0);
			}
		}

		offs+=inc;
	}
}

/******************************************************************************/

VIDEO_UPDATE( cninja )
{
	flip_screen_set( deco16_pf12_control[0]&0x80 );
	deco16_pf12_update(deco16_pf1_rowscroll,deco16_pf2_rowscroll);
	deco16_pf34_update(deco16_pf3_rowscroll,deco16_pf4_rowscroll);

	/* Draw playfields */
	fillbitmap(priority_bitmap,0,cliprect);
	fillbitmap(bitmap,Machine->pens[512],cliprect);
	deco16_tilemap_4_draw(bitmap,cliprect,TILEMAP_IGNORE_TRANSPARENCY,1);
	deco16_tilemap_3_draw(bitmap,cliprect,0,2);
	deco16_tilemap_2_draw(bitmap,cliprect,TILEMAP_BACK,2);
	deco16_tilemap_2_draw(bitmap,cliprect,TILEMAP_FRONT,4);
	cninja_drawsprites(bitmap,cliprect);
	deco16_tilemap_1_draw(bitmap,cliprect,0,0);
}

VIDEO_UPDATE( edrandy )
{
	flip_screen_set( deco16_pf12_control[0]&0x80 );
	deco16_pf12_update(deco16_pf1_rowscroll,deco16_pf2_rowscroll);
	deco16_pf34_update(deco16_pf3_rowscroll,deco16_pf4_rowscroll);

	fillbitmap(priority_bitmap,0,cliprect);
	fillbitmap(bitmap,Machine->pens[0],&Machine->visible_area);
	deco16_tilemap_4_draw(bitmap,cliprect,TILEMAP_IGNORE_TRANSPARENCY,1);
	if (deco16_raster_display_position)
		raster_pf3_draw(bitmap,cliprect,0,2);
	else
		deco16_tilemap_3_draw(bitmap,cliprect,0,2);
	deco16_tilemap_2_draw(bitmap,cliprect,0,4);
	cninja_drawsprites(bitmap,cliprect);
	deco16_tilemap_1_draw(bitmap,cliprect,0,0);
}

VIDEO_UPDATE( robocop2 )
{
	/* One of the tilemap chips can switch between 2 tilemaps at 4bpp, or 1 at 8bpp */
	if (deco16_priority&4) {
		deco16_set_tilemap_colour_mask(2,0);
		deco16_set_tilemap_colour_mask(3,0);
		deco16_pf34_set_gfxbank(0,4);
	} else {
		deco16_set_tilemap_colour_mask(2,0xf);
		deco16_set_tilemap_colour_mask(3,0xf);
		deco16_pf34_set_gfxbank(0,2);
	}

	/* Update playfields */
	flip_screen_set( deco16_pf12_control[0]&0x80 );
	deco16_pf12_update(deco16_pf1_rowscroll,deco16_pf2_rowscroll);
	deco16_pf34_update(deco16_pf3_rowscroll,deco16_pf4_rowscroll);

	/* Draw playfields */
	fillbitmap(priority_bitmap,0,cliprect);
	fillbitmap(bitmap,Machine->pens[0x200],cliprect);
	if ((deco16_priority&4)==0)
		deco16_tilemap_4_draw(bitmap,cliprect,TILEMAP_IGNORE_TRANSPARENCY,1);

	/* Switchable priority */
	switch (deco16_priority&0x8) {
		case 8:
			deco16_tilemap_2_draw(bitmap,cliprect,0,2);
			if (deco16_raster_display_position)
				raster_pf3_draw(bitmap,cliprect,0,4);
			else
				deco16_tilemap_3_draw(bitmap,cliprect,0,4);
			break;
		default:
		case 0:
			if (deco16_raster_display_position)
				raster_pf3_draw(bitmap,cliprect,0,2);
			else
				deco16_tilemap_3_draw(bitmap,cliprect,0,2);
			deco16_tilemap_2_draw(bitmap,cliprect,0,4);
			break;
	}

	robocop2_drawsprites(bitmap,cliprect);
	deco16_tilemap_1_draw(bitmap,cliprect,0,0);
}

VIDEO_UPDATE( mutantf )
{
	flip_screen_set( deco16_pf12_control[0]&0x80 );
	deco16_pf12_update(deco16_pf1_rowscroll,deco16_pf2_rowscroll);
	deco16_pf34_update(deco16_pf3_rowscroll,deco16_pf4_rowscroll);

	/* Draw playfields */
	fillbitmap(bitmap,Machine->pens[0x400],cliprect); /* Confirmed */

	/* There is no priority prom on this board, but there is a
    priority control word, the only values used in game appear
    to be 2, 6 & 7 though:

    Bit 0:  If set sprite chip 2 above sprite chip 1 else vice versa
    Bit 1:  Always set?
    Bit 2:  Almost always set  (Sometimes not set on screen transitions)

    The other bits may control alpha blend on the 2nd sprite chip, or
    layer order.
    */
	deco16_tilemap_4_draw(bitmap,cliprect,TILEMAP_IGNORE_TRANSPARENCY,0);
	deco16_tilemap_2_draw(bitmap,cliprect,0,0);
	deco16_tilemap_3_draw(bitmap,cliprect,0,0);

	/* We need to abuse the priority bitmap a little by clearing it before
        drawing each sprite layer.  This is because there is no priority
        orthogonality between sprite layers, but the alpha layer must obey
        priority between sprites in each layer.  Ie, if we didn't do this,
        then when two alpha blended shadows overlapped then they would be 25%
        transparent against the background, rather than 50% */
	if (deco16_priority&1) {
		fillbitmap(priority_bitmap,0,cliprect);
		mutantf_drawsprites(bitmap,cliprect,buffered_spriteram16,3);
		fillbitmap(priority_bitmap,0,cliprect);
		mutantf_drawsprites(bitmap,cliprect,buffered_spriteram16_2,4);
	} else {
		fillbitmap(priority_bitmap,0,cliprect);
		mutantf_drawsprites(bitmap,cliprect,buffered_spriteram16_2,4);
		fillbitmap(priority_bitmap,0,cliprect);
		mutantf_drawsprites(bitmap,cliprect,buffered_spriteram16,3);
	}
	deco16_tilemap_1_draw(bitmap,cliprect,0,0);
}
