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

  video/nes.c

  Routines to control the unique NES video hardware/PPU.

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

#include <math.h>

#include "driver.h"
#include "video/ppu2c0x.h"
#include "video/generic.h"
#include "includes/nes.h"
#include "machine/nes_mmc.h"

mame_bitmap *nes_zapper_hack;
int nes_vram_sprite[8]; /* Used only by mmc5 for now */
static int last_frame_flip = 0;

static void ppu_nmi(int num, int *ppu_regs)
{
	cpunum_set_input_line(0, INPUT_LINE_NMI, PULSE_LINE);
}

static void nes_vh_reset(running_machine *machine)
{
	ppu2c0x_reset( 0, 1 );
}



static void nes_vh_start(running_machine *machine, ppu_t ppu_type, double scanlines_per_frame)
{
	ppu2c0x_interface ppu_interface;

	nes_zapper_hack = NULL;

	last_frame_flip =  0;

	memset(&ppu_interface, 0, sizeof(ppu_interface));
	ppu_interface.type				= ppu_type;
	ppu_interface.num				= 1;
	ppu_interface.vrom_region[0]	= nes.chr_chunks ? REGION_GFX1 : REGION_INVALID;
	ppu_interface.mirroring[0]		= PPU_MIRROR_NONE;
	ppu_interface.nmi_handler[0]	= ppu_nmi;

	ppu2c0x_init(machine, &ppu_interface);
	ppu2c0x_set_vidaccess_callback(0, nes_ppu_vidaccess);
	ppu2c0x_set_scanlines_per_frame(0, ceil(scanlines_per_frame));

	if (nes.four_screen_vram)
	{
		ppu2c0x_set_mirroring(0, PPU_MIRROR_4SCREEN);
	}
	else
	{
		switch(nes.hard_mirroring)
		{
			case 0:
				ppu2c0x_set_mirroring(0, PPU_MIRROR_HORZ);
				break;
			case 1:
				ppu2c0x_set_mirroring(0, PPU_MIRROR_VERT);
				break;
		}
	}

	add_reset_callback(Machine, nes_vh_reset);

	/* Reset the mapper variables. Will also mark the char-gen ram as dirty */
	mapper_reset(nes.mapper);
}



VIDEO_START( nes_ntsc )
{
	nes_vh_start(machine, PPU_2C02, PPU_NTSC_SCANLINES_PER_FRAME);
}

VIDEO_START( nes_pal )
{
	nes_vh_start(machine, PPU_2C07, PPU_PAL_SCANLINES_PER_FRAME);
}

PALETTE_INIT( nes )
{
	ppu2c0x_init_palette(machine, 0);
}

static void draw_sight(mame_bitmap *bitmap, int playerNum, int x_center, int y_center)
{
	int x,y;
	UINT16 color;

	if (playerNum == 2)
		color = Machine->pens[0]; /* grey */
	else
		color = Machine->pens[0x30]; /* white */

	if (x_center<2)   x_center=2;
	if (x_center>253) x_center=253;

	if (y_center<2)   y_center=2;
	if (y_center>253) y_center=253;

	for(y = y_center-5; y < y_center+6; y++)
		if((y >= 0) && (y < 256))
			*BITMAP_ADDR16(bitmap, y, x_center) = color;

	for(x = x_center-5; x < x_center+6; x++)
		if((x >= 0) && (x < 256))
			*BITMAP_ADDR16(bitmap, y_center, x) = color;
}

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

  Display refresh

***************************************************************************/
VIDEO_UPDATE( nes )
{
	int sights = 0;

	nes_zapper_hack = bitmap;

	/* render the ppu */
	ppu2c0x_render( 0, bitmap, 0, 0, 0, 0 );

	/* figure out what sights to draw, and draw them */
	if ((readinputport(PORT_CONFIG1) & 0x000f) == 0x0002)
		sights |= 0x0001;
	if ((readinputport(PORT_CONFIG1) & 0x000f) == 0x0003)
		sights |= 0x0002;
	if ((readinputport(PORT_CONFIG1) & 0x00f0) == 0x0020)
		sights |= 0x0001;
	if ((readinputport(PORT_CONFIG1) & 0x00f0) == 0x0030)
		sights |= 0x0002;
	if (sights & 0x0001)
		draw_sight(bitmap, 1, readinputport(PORT_ZAPPER0_X), readinputport(PORT_ZAPPER0_Y));
	if (sights & 0x0002)
		draw_sight(bitmap, 2, readinputport(PORT_ZAPPER1_X), readinputport(PORT_ZAPPER1_Y));

	/* if this is a disk system game, check for the flip-disk key */
	if (nes.mapper == 20)
	{
		// latch this input so it doesn't go at warp speed
		if ((readinputport(PORT_FLIPKEY) & 0x01) && (!last_frame_flip))
		{
			last_frame_flip = 1;
			nes_fds.current_side++;
			if (nes_fds.current_side > nes_fds.sides)
				nes_fds.current_side = 0;

			if (nes_fds.current_side == 0)
			{
				popmessage("No disk inserted.");
			}
			else
			{
				popmessage("Disk set to side %d", nes_fds.current_side);
			}
		}

		if (!(readinputport(PORT_FLIPKEY) & 0x01))
		{
			last_frame_flip = 0;
		}
	}
	return 0;
}
