/* video hardware for Namco System II */

#include "driver.h"
#include "vidhrdw/generic.h"
#include "namcos2.h"
#include "namcoic.h"

data16_t *namcos2_sprite_ram;
data16_t *namcos2_68k_palette_ram;
size_t namcos2_68k_palette_size;
size_t namcos2_68k_roz_ram_size;
data16_t *namcos2_68k_roz_ram;

static data16_t namcos2_68k_roz_ctrl[0x8];
static struct tilemap *tilemap_roz;

static void
TilemapCB( data16_t code, int *tile, int *mask )
{
	*mask = code;

	switch( namcos2_gametype )
	{
	case NAMCOS2_FINAL_LAP_2:
	case NAMCOS2_FINAL_LAP_3:
		*tile = (code&0x07ff)|((code&0x4000)>>3)|((code&0x3800)<<1);
		break;

	default:
		/* The order of bits needs to be corrected to index the right tile  14 15 11 12 13 */
		*tile = (code&0x07ff)|((code&0xc000)>>3)|((code&0x3800)<<2);
		break;
	}
} /* TilemapCB */

/**
 * namcos2_gfx_ctrl selects a bank of 128 sprites within spriteram
 *
 * namcos2_gfx_ctrl also supplies palette and priority information that is applied to the output of the
 *                  Namco System 2 ROZ chip
 *
 * -xxx ---- ---- ---- roz priority
 * ---- xxxx ---- ---- roz palette
 * ---- ---- xxxx ---- always zero?
 * ---- ---- ---- xxxx sprite bank
 */
static data16_t namcos2_gfx_ctrl;

READ16_HANDLER( namcos2_gfx_ctrl_r )
{
	return namcos2_gfx_ctrl;
} /* namcos2_gfx_ctrl_r */

WRITE16_HANDLER( namcos2_gfx_ctrl_w )
{
	COMBINE_DATA(&namcos2_gfx_ctrl);
} /* namcos2_gfx_ctrl_w */

static void get_tile_info_roz(int tile_index)
{
	int tile = namcos2_68k_roz_ram[tile_index];
	SET_TILE_INFO(3,tile,0/*color*/,0)
} /* get_tile_info_roz */

struct RozParam
{
	UINT32 size;
	UINT32 startx,starty;
	int incxx,incxy,incyx,incyy;
	int color;
	int wrap;
};

static void
DrawRozHelper(
	struct mame_bitmap *bitmap,
	struct tilemap *tilemap,
	const struct rectangle *clip,
	const struct RozParam *rozInfo )
{
	tilemap_set_palette_offset( tilemap, rozInfo->color );

	if( bitmap->depth == 15 || bitmap->depth == 16 )
	{
		UINT32 size_mask = rozInfo->size-1;
		struct mame_bitmap *srcbitmap = tilemap_get_pixmap( tilemap );
		struct mame_bitmap *transparency_bitmap = tilemap_get_transparency_bitmap( tilemap );
		UINT32 startx = rozInfo->startx + clip->min_x * rozInfo->incxx + clip->min_y * rozInfo->incyx;
		UINT32 starty = rozInfo->starty + clip->min_x * rozInfo->incxy + clip->min_y * rozInfo->incyy;
		int sx = clip->min_x;
		int sy = clip->min_y;
		while( sy <= clip->max_y )
		{
			int x = sx;
			UINT32 cx = startx;
			UINT32 cy = starty;
			UINT16 *dest = ((UINT16 *)bitmap->line[sy]) + sx;
			while( x <= clip->max_x )
			{
				UINT32 xpos = (cx>>16);
				UINT32 ypos = (cy>>16);
				if( rozInfo->wrap )
				{
					xpos &= size_mask;
					ypos &= size_mask;
				}
				else if( xpos>rozInfo->size || ypos>=rozInfo->size )
				{
					goto L_SkipPixel;
				}

				if( ((UINT8 *)transparency_bitmap->line[ypos])[xpos]&TILE_FLAG_FG_OPAQUE )
				{
					*dest = ((UINT16 *)srcbitmap->line[ypos])[xpos]+rozInfo->color;
				}
L_SkipPixel:
				cx += rozInfo->incxx;
				cy += rozInfo->incxy;
				x++;
				dest++;
			} /* next x */
			startx += rozInfo->incyx;
			starty += rozInfo->incyy;
			sy++;
		} /* next y */
	}
	else
	{
		tilemap_draw_roz(
			bitmap,
			clip,
			tilemap,
			rozInfo->startx, rozInfo->starty,
			rozInfo->incxx, rozInfo->incxy,
			rozInfo->incyx, rozInfo->incyy,
			rozInfo->wrap,0,0); /* wrap, flags, pri */
	}
} /* DrawRozHelper */

static void
DrawROZ(struct mame_bitmap *bitmap,const struct rectangle *cliprect)
{
	const int xoffset = 38,yoffset = 0;
	struct RozParam rozParam;

	rozParam.color = (namcos2_gfx_ctrl & 0x0f00);
	rozParam.incxx  = (INT16)namcos2_68k_roz_ctrl[0];
	rozParam.incxy  = (INT16)namcos2_68k_roz_ctrl[1];
	rozParam.incyx  = (INT16)namcos2_68k_roz_ctrl[2];
	rozParam.incyy  = (INT16)namcos2_68k_roz_ctrl[3];
	rozParam.startx = (INT16)namcos2_68k_roz_ctrl[4];
	rozParam.starty = (INT16)namcos2_68k_roz_ctrl[5];
	rozParam.size = 2048;
	rozParam.wrap = 1;


	switch( namcos2_68k_roz_ctrl[7] )
	{
	case 0x4400: /* (2048x2048) */
		break;

	case 0x4488: /* attract mode */
		rozParam.wrap = 0;
		break;

	case 0x44cc: /* stage1 demo */
		rozParam.wrap = 0;
		break;

	case 0x44ee: /* (256x256) used in Dragon Saber */
		rozParam.wrap = 0;
		rozParam.size = 256;
		break;
	}

	rozParam.startx <<= 4;
	rozParam.starty <<= 4;
	rozParam.startx += xoffset * rozParam.incxx + yoffset * rozParam.incyx;
	rozParam.starty += xoffset * rozParam.incxy + yoffset * rozParam.incyy;

	rozParam.startx<<=8;
	rozParam.starty<<=8;
	rozParam.incxx<<=8;
	rozParam.incxy<<=8;
	rozParam.incyx<<=8;
	rozParam.incyy<<=8;

	DrawRozHelper( bitmap, tilemap_roz, cliprect, &rozParam );
}

READ16_HANDLER(namcos2_68k_roz_ctrl_r)
{
	return namcos2_68k_roz_ctrl[offset];
}

WRITE16_HANDLER( namcos2_68k_roz_ctrl_w )
{
	COMBINE_DATA(&namcos2_68k_roz_ctrl[offset]);
}

READ16_HANDLER( namcos2_68k_roz_ram_r )
{
	return namcos2_68k_roz_ram[offset];
}

WRITE16_HANDLER( namcos2_68k_roz_ram_w )
{
/*  extern int debug_key_pressed; */
	data16_t oldword = namcos2_68k_roz_ram[offset];
	COMBINE_DATA(&namcos2_68k_roz_ram[offset]);
	if (oldword != namcos2_68k_roz_ram[offset])
	{
		tilemap_mark_tile_dirty(tilemap_roz,offset);
/*      if( code_pressed(KEYCODE_Q) ) */
/*      { */
/*          debug_key_pressed = 1; */
/*      } */
	}
}

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

READ16_HANDLER( namcos2_68k_video_palette_r )
{
	if( (offset&0x1800) == 0x1800 )
	{
		offset &= 0x180f;
		if( offset == 0x180d ) return 0xff;
		if( offset == 0x180f ) return 0xff;
	}
	return namcos2_68k_palette_ram[offset];
} /* namcos2_68k_video_palette_r */

WRITE16_HANDLER( namcos2_68k_video_palette_w )
{
	if( (offset&0x1800) == 0x1800 )
	{
		offset &= 0x180f;
		if( ACCESSING_LSB )
		{
			namcos2_68k_palette_ram[offset] = data;
		}
		else
		{
			namcos2_68k_palette_ram[offset] = data>>8;
		}
	}
	else
	{
		COMBINE_DATA(&namcos2_68k_palette_ram[offset]);
	}
} /* namcos2_68k_video_palette_w */

static data16_t
GetPaletteRegister( int which )
{
	const data16_t *source = &namcos2_68k_palette_ram[0x3000/2];
	return ((source[which*2]&0xff)<<8) | (source[which*2+1]&0xff);
}

int
namcos2_GetPosIrqScanline( void )
{
	/* PaleteRegister(4)? used by Finest Hour; pc=0x356e */
	int scanline = GetPaletteRegister(5) - 34;
	if( scanline<0 )
	{
		scanline = 0;
	}
	else if( scanline > Machine->scrbitmap->height )
	{
		scanline = Machine->scrbitmap->height;
	}
	return scanline;
} /* namcos2_GetPosIrqScanline */

static void
UpdatePalette( void )
{
	int bank;
	for( bank=0; bank<0x20; bank++ )
	{
		int pen = bank*256;
		int offset = ((pen & 0x1800) << 2) | (pen & 0x07ff);
		int i;
		for( i=0; i<256; i++ )
		{
			int r = namcos2_68k_palette_ram[offset | 0x0000] & 0x00ff;
			int g = namcos2_68k_palette_ram[offset | 0x0800] & 0x00ff;
			int b = namcos2_68k_palette_ram[offset | 0x1000] & 0x00ff;
			palette_set_color(pen++,r,g,b);
			offset++;
		}
	}
} /* UpdatePalette */

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

static void
DrawSpriteInit( void )
{
	int i;
	/* set table for sprite color == 0x0f */
	for(i = 0;i <= 253;i++)
	{
		gfx_drawmode_table[i] = DRAWMODE_SOURCE;
	}
	gfx_drawmode_table[254] = DRAWMODE_SHADOW;
	gfx_drawmode_table[255] = DRAWMODE_NONE;
	for( i = 0; i<16*256; i++ )
	{
		palette_shadow_table[i] = i+0x2000;
	}
}

WRITE16_HANDLER( namcos2_sprite_ram_w )
{
	COMBINE_DATA(&namcos2_sprite_ram[offset]);
}

READ16_HANDLER( namcos2_sprite_ram_r )
{
	return namcos2_sprite_ram[offset];
}

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

static void
DrawCrossshair( struct mame_bitmap *bitmap, const struct rectangle *cliprect )
{
	int x1port, y1port, x2port, y2port;
	int beamx, beamy;

	switch( namcos2_gametype )
	{
	case NAMCOS2_GOLLY_GHOST:
		x1port = 0;
		y1port = 1;
		x2port = 2;
		y2port = 3;
		break;
	case NAMCOS2_LUCKY_AND_WILD:
		x1port = 4;
		y1port = 2;
		x2port = 3;
		y2port = 1;
		break;
	case NAMCOS2_STEEL_GUNNER_2:
		x1port = 4;
		x2port = 5;
		y1port = 6;
		y2port = 7;
		break;
	default:
		return;
	}

	beamx = readinputport(2+x1port)*bitmap->width/256;
	beamy = readinputport(2+y1port)*bitmap->height/256;
	draw_crosshair( bitmap, beamx, beamy, cliprect );

	beamx = readinputport(2+x2port)*bitmap->width/256;
	beamy = readinputport(2+y2port)*bitmap->height/256;
	draw_crosshair( bitmap, beamx, beamy, cliprect );
}

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

VIDEO_START( namcos2 )
{
	if( namco_tilemap_init(2,memory_region(REGION_GFX4),TilemapCB)==0 )
	{
		tilemap_roz = tilemap_create(get_tile_info_roz,tilemap_scan_rows,TILEMAP_TRANSPARENT,8,8,256,256);
		if( tilemap_roz )
		{
			tilemap_set_transparent_pen(tilemap_roz,0xff);
			DrawSpriteInit();
			return 0;
		}
	}
	return -1;
}

static void
ApplyClip( struct rectangle *clip, const struct rectangle *cliprect )
{
	clip->min_x = GetPaletteRegister(0) - 0x4a;
	clip->max_x = GetPaletteRegister(1) - 0x4a - 1;
	clip->min_y = GetPaletteRegister(2) - 0x21;
	clip->max_y = GetPaletteRegister(3) - 0x21 - 1;
	/* intersect with master clip rectangle */
	if( clip->min_x < cliprect->min_x ){ clip->min_x = cliprect->min_x; }
	if( clip->min_y < cliprect->min_y ){ clip->min_y = cliprect->min_y; }
	if( clip->max_x > cliprect->max_x ){ clip->max_x = cliprect->max_x; }
	if( clip->max_y > cliprect->max_y ){ clip->max_y = cliprect->max_y; }
} /* ApplyClip */

VIDEO_UPDATE( namcos2_default )
{
	struct rectangle clip;
	int pri;

	UpdatePalette();
	fillbitmap( bitmap, get_black_pen(), cliprect );
	ApplyClip( &clip, cliprect );

	/* HACK: enable ROZ layer only if it has priority > 0 */
	tilemap_set_enable(tilemap_roz,(namcos2_gfx_ctrl & 0x7000) ? 1 : 0);

	for( pri=0; pri<16; pri++ )
	{
		if( (pri&1)==0 )
		{
			namco_tilemap_draw( bitmap, &clip, pri/2 );

			if( ((namcos2_gfx_ctrl & 0x7000) >> 12)==pri/2 )
			{
				DrawROZ(bitmap,&clip);
			}
			namcos2_draw_sprites( bitmap, &clip, pri/2, namcos2_gfx_ctrl );
		}
	}
	DrawCrossshair( bitmap,&clip );
} /* namcos2_default */

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

VIDEO_START( finallap )
{
	if( namco_tilemap_init(2,memory_region(REGION_GFX4),TilemapCB)==0 )
	{
		DrawSpriteInit();
		namco_road_init(3);
		return 0;
	}
	return -1;
}

VIDEO_UPDATE( finallap )
{
	struct rectangle clip;
	int pri;

	UpdatePalette();
	fillbitmap( bitmap, get_black_pen(), cliprect );
	ApplyClip( &clip, cliprect );

	for( pri=0; pri<16; pri++ )
	{
		if( (pri&1)==0 )
		{
			namco_tilemap_draw( bitmap, &clip, pri/2 );
		}
		namco_road_draw( bitmap,&clip,pri );
		namcos2_draw_sprites( bitmap,&clip,pri,namcos2_gfx_ctrl );
	}
} /* finallap */

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

VIDEO_START( luckywld )
{
	if( namco_tilemap_init(2,memory_region(REGION_GFX4),TilemapCB)==0 )
	{
		namco_obj_init( 0, 0x0, NULL );
		if( namcos2_gametype==NAMCOS2_LUCKY_AND_WILD )
		{
			namco_roz_init( 1, REGION_GFX5 );
		}
		if( namcos2_gametype!=NAMCOS2_STEEL_GUNNER_2 )
		{
			namco_road_init(3);
		}
		return 0;
	}
	return -1;
} /* luckywld */

VIDEO_UPDATE( luckywld )
{
	struct rectangle clip;
	int pri;

	UpdatePalette();
	fillbitmap( bitmap, get_black_pen(), cliprect );
	ApplyClip( &clip, cliprect );

	for( pri=0; pri<16; pri++ )
	{
		if( (pri&1)==0 )
		{
			namco_tilemap_draw( bitmap, &clip, pri/2 );
		}
		namco_road_draw( bitmap,&clip,pri );
		if( namcos2_gametype==NAMCOS2_LUCKY_AND_WILD )
		{
			namco_roz_draw( bitmap, &clip, pri );
		}
		namco_obj_draw( bitmap, &clip, pri );
	}
	DrawCrossshair( bitmap,&clip );
} /* luckywld */

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

VIDEO_START( sgunner )
{
	if( namco_tilemap_init(2,memory_region(REGION_GFX4),TilemapCB)==0 )
	{
		namco_obj_init( 0, 0x0, NULL );
		return 0;
	}
	return -1;
}

VIDEO_UPDATE( sgunner )
{
	struct rectangle clip;
	int pri;

	UpdatePalette();
	fillbitmap( bitmap, get_black_pen(), cliprect );
	ApplyClip( &clip, cliprect );

	for( pri=0; pri<8; pri++ )
	{
		namco_tilemap_draw( bitmap, &clip, pri );
		namco_obj_draw( bitmap, &clip, pri );
	}
	DrawCrossshair( bitmap,&clip );
}


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

VIDEO_START( metlhawk )
{
	if( namco_tilemap_init(2,memory_region(REGION_GFX4),TilemapCB)==0 )
	{
		namco_roz_init( 1, REGION_GFX5 );
		return 0;
	}
	return -1;
}

VIDEO_UPDATE( metlhawk )
{
	struct rectangle clip;
	int pri;

	UpdatePalette();
	fillbitmap( bitmap, get_black_pen(), cliprect );
	ApplyClip( &clip, cliprect );

	for( pri=0; pri<16; pri++ )
	{
		if( (pri&1)==0 )
		{
			namco_tilemap_draw( bitmap, &clip, pri/2 );
		}
		namco_roz_draw( bitmap, &clip, pri );
		namcos2_draw_sprites_metalhawk( bitmap,&clip,pri );
	}
}
