/* Tao Taido Video Hardware */

/*

its similar to other video system games

zooming might be wrong (only used on title logo?)

*/

#include "emu.h"
#include "includes/taotaido.h"


/* sprite tile codes 0x4000 - 0x7fff get remapped according to the content of these registers */
WRITE16_HANDLER( taotaido_sprite_character_bank_select_w )
{
	taotaido_state *state = space->machine().driver_data<taotaido_state>();
	if(ACCESSING_BITS_8_15)
		state->m_sprite_character_bank_select[offset*2] = data >> 8;
	if(ACCESSING_BITS_0_7)
		state->m_sprite_character_bank_select[offset*2+1] = data &0xff;
}

/* sprites are like the other video system / psikyo games, we can merge this with aerofgt and plenty of other
   things eventually */

static void draw_sprite(running_machine &machine, UINT16 spriteno, bitmap_t *bitmap, const rectangle *cliprect )
{
	taotaido_state *state = machine.driver_data<taotaido_state>();
	/*- SPR RAM Format -**

      4 words per sprite

      zzzz sssp  pppp pppp (y zoom, y size, y position)
      zzzz sssp  pppp pppp (x zoom, x size, x position)
      yxpc cccc  ---- ---- (flipy, flipx, priority?, colour)
      -nnn nnnn  nnnn nnnn (tile lookup)

    */

	int x,y;

	UINT16 *source = &state->m_spriteram_older[spriteno*4];
	const gfx_element *gfx = machine.gfx[0];


	int yzoom = (source[0] & 0xf000) >> 12;
	int xzoom = (source[1] & 0xf000) >> 12;

	int ysize = (source[0] & 0x0e00) >> 9;
	int xsize = (source[1] & 0x0e00) >> 9;

	int ypos = source[0] & 0x01ff;
	int xpos = source[1] & 0x01ff;

	int yflip = source[2] & 0x8000;
	int xflip = source[2] & 0x4000;
	int color = (source[2] & 0x1f00) >> 8;

	int tile = source[3] & 0xffff;

	xpos += (xsize*xzoom+2)/4;
	ypos += (ysize*yzoom+2)/4;

	xzoom = 32 - xzoom;
	yzoom = 32 - yzoom;


	for (y = 0;y <= ysize;y++)
	{
		int sx,sy;

		if (yflip) sy = ((ypos + yzoom * (ysize - y)/2 + 16) & 0x1ff) - 16;
			else sy = ((ypos + yzoom * y / 2 + 16) & 0x1ff) - 16;

		for (x = 0;x <= xsize;x++)
		{

			/* this indirection is a bit different to the other video system games */
			int realtile;

			realtile = state->m_spriteram2_older[tile&0x7fff];

			if (realtile > 0x3fff)
			{
				int block;

				block = (realtile & 0x3800)>>11;

				realtile &= 0x07ff;
				realtile |= state->m_sprite_character_bank_select[block] * 0x800;
			}

			if (xflip) sx = ((xpos + xzoom * (xsize - x) / 2 + 16) & 0x1ff) - 16;
				else sx = ((xpos + xzoom * x / 2 + 16) & 0x1ff) - 16;


			drawgfxzoom_transpen(bitmap,cliprect,gfx,
						realtile,
						color,
						xflip,yflip,
						sx,sy,
						xzoom << 11, yzoom << 11,15);

			tile++;

		}
	}
}

static void draw_sprites(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect )
{
	taotaido_state *state = machine.driver_data<taotaido_state>();
	/* first part of sprite ram is the list of sprites to draw, terminated with 0x4000 */
	UINT16 *source = state->m_spriteram_older;
	UINT16 *finish = state->m_spriteram_older + 0x2000/2;

	while( source<finish )
	{
		if (source[0] == 0x4000) break;

		draw_sprite(machine, source[0]&0x3ff, bitmap, cliprect);

		source++;
	}
}


/* the tilemap */

WRITE16_HANDLER( taotaido_tileregs_w )
{
	taotaido_state *state = space->machine().driver_data<taotaido_state>();
	switch (offset)
	{
		case 0: // would normally be x scroll?
		case 1: // would normally be y scroll?
		case 2:
		case 3:
			logerror ("unhanded tilemap register write offset %02x data %04x \n",offset,data);
			break;

		/* tile banks */
		case 4:
		case 5:
		case 6:
		case 7:
			if(ACCESSING_BITS_8_15)
				state->m_video_bank_select[(offset-4)*2] = data >> 8;
			if(ACCESSING_BITS_0_7)
				state->m_video_bank_select[(offset-4)*2+1] = data &0xff;
				tilemap_mark_all_tiles_dirty(state->m_bg_tilemap);
			break;
	}
}

WRITE16_HANDLER( taotaido_bgvideoram_w )
{
	taotaido_state *state = space->machine().driver_data<taotaido_state>();
	COMBINE_DATA(&state->m_bgram[offset]);
	tilemap_mark_tile_dirty(state->m_bg_tilemap,offset);
}

static TILE_GET_INFO( taotaido_bg_tile_info )
{
	taotaido_state *state = machine.driver_data<taotaido_state>();
	int code = state->m_bgram[tile_index]&0x01ff;
	int bank = (state->m_bgram[tile_index]&0x0e00)>>9;
	int col  = (state->m_bgram[tile_index]&0xf000)>>12;

	code |= state->m_video_bank_select[bank]*0x200;

	SET_TILE_INFO(
			1,
			code,
			col,
			0);
}

static TILEMAP_MAPPER( taotaido_tilemap_scan_rows )
{
	/* logical (col,row) -> memory offset */
	return row*0x40 + (col&0x3f) + ((col&0x40)<<6);
}

VIDEO_START(taotaido)
{
	taotaido_state *state = machine.driver_data<taotaido_state>();
	state->m_bg_tilemap = tilemap_create(machine, taotaido_bg_tile_info,taotaido_tilemap_scan_rows,     16,16,128,64);

	state->m_spriteram_old = auto_alloc_array(machine, UINT16, 0x2000/2);
	state->m_spriteram_older = auto_alloc_array(machine, UINT16, 0x2000/2);

	state->m_spriteram2_old = auto_alloc_array(machine, UINT16, 0x10000/2);
	state->m_spriteram2_older = auto_alloc_array(machine, UINT16, 0x10000/2);
}


SCREEN_UPDATE(taotaido)
{
	taotaido_state *state = screen->machine().driver_data<taotaido_state>();
//  tilemap_set_scrollx(state->m_bg_tilemap,0,(state->m_scrollram[0x380/2]>>4)); // the values put here end up being wrong every other frame
//  tilemap_set_scrolly(state->m_bg_tilemap,0,(state->m_scrollram[0x382/2]>>4)); // the values put here end up being wrong every other frame

	/* not amazingly efficient however it should be functional for row select and linescroll */
	int line;
	rectangle clip;

	const rectangle &visarea = screen->visible_area();
	clip.min_x = visarea.min_x;
	clip.max_x = visarea.max_x;
	clip.min_y = visarea.min_y;
	clip.max_y = visarea.max_y;

	for (line = 0; line < 224;line++)
	{
		clip.min_y = clip.max_y = line;

		tilemap_set_scrollx(state->m_bg_tilemap,0,((state->m_scrollram[(0x00+4*line)/2])>>4)+30);
		tilemap_set_scrolly(state->m_bg_tilemap,0,((state->m_scrollram[(0x02+4*line)/2])>>4)-line);

		tilemap_draw(bitmap,&clip,state->m_bg_tilemap,0,0);
	}

	draw_sprites(screen->machine(), bitmap,cliprect);
	return 0;
}

SCREEN_EOF( taotaido )
{
	taotaido_state *state = machine.driver_data<taotaido_state>();
	/* sprites need to be delayed by 2 frames? */

	memcpy(state->m_spriteram2_older,state->m_spriteram2_old,0x10000);
	memcpy(state->m_spriteram2_old,state->m_spriteram2,0x10000);

	memcpy(state->m_spriteram_older,state->m_spriteram_old,0x2000);
	memcpy(state->m_spriteram_old,state->m_spriteram,0x2000);
}
