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

    crsshair.h

    Crosshair handling.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

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

#include "driver.h"
#include "rendutil.h"



/***************************************************************************
    CONSTANTS
***************************************************************************/

#define CROSSHAIR_RAW_SIZE		100
#define CROSSHAIR_RAW_ROWBYTES	((CROSSHAIR_RAW_SIZE + 7) / 8)


/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

typedef struct _crosshair_global crosshair_global;
struct _crosshair_global
{
	UINT8 				used[MAX_PLAYERS];		/* usage per player */
	UINT8 				visible[MAX_PLAYERS];	/* visibility per player */
	bitmap_t *			bitmap[MAX_PLAYERS];	/* bitmap per player */
	render_texture *	texture[MAX_PLAYERS];	/* texture per player */
	const device_config *screen[MAX_PLAYERS];	/* the screen on which this player's crosshair is drawn */
	float 				x[MAX_PLAYERS];			/* current X position */
	float				y[MAX_PLAYERS];			/* current Y position */
	UINT8				fade;					/* color fading factor */
	UINT8 				animation_counter;		/* animation frame index */
};


/***************************************************************************
    GLOBAL VARIABLES
***************************************************************************/

/* global state */
static crosshair_global global;

/* raw bitmap */
static const UINT8 crosshair_raw_top[] =
{
	0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
	0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,
	0x00,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xf0,0x00,
	0x01,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xf8,0x00,
	0x03,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfc,0x00,
	0x07,0xfe,0x00,0x00,0x00,0x0f,0xfe,0x00,0x00,0x00,0x07,0xfe,0x00,
	0x0f,0xff,0x00,0x00,0x01,0xff,0xff,0xf0,0x00,0x00,0x0f,0xff,0x00,
	0x1f,0xff,0x80,0x00,0x1f,0xff,0xff,0xff,0x00,0x00,0x1f,0xff,0x80,
	0x3f,0xff,0x80,0x00,0xff,0xff,0xff,0xff,0xe0,0x00,0x1f,0xff,0xc0,
	0x7f,0xff,0xc0,0x03,0xff,0xff,0xff,0xff,0xf8,0x00,0x3f,0xff,0xe0,
	0xff,0xff,0xe0,0x07,0xff,0xff,0xff,0xff,0xfc,0x00,0x7f,0xff,0xf0,
	0x7f,0xff,0xf0,0x1f,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xe0,
	0x3f,0xff,0xf8,0x7f,0xff,0xff,0xff,0xff,0xff,0xc1,0xff,0xff,0xc0,
	0x0f,0xff,0xf8,0xff,0xff,0xff,0xff,0xff,0xff,0xe1,0xff,0xff,0x00,
	0x07,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xfe,0x00,
	0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x00,
	0x01,0xff,0xff,0xff,0xff,0xf0,0x01,0xff,0xff,0xff,0xff,0xf8,0x00,
	0x00,0x7f,0xff,0xff,0xff,0x00,0x00,0x1f,0xff,0xff,0xff,0xe0,0x00,
	0x00,0x3f,0xff,0xff,0xf8,0x00,0x00,0x03,0xff,0xff,0xff,0xc0,0x00,
	0x00,0x1f,0xff,0xff,0xe0,0x00,0x00,0x00,0xff,0xff,0xff,0x80,0x00,
	0x00,0x0f,0xff,0xff,0x80,0x00,0x00,0x00,0x3f,0xff,0xff,0x00,0x00,
	0x00,0x03,0xff,0xfe,0x00,0x00,0x00,0x00,0x0f,0xff,0xfc,0x00,0x00,
	0x00,0x01,0xff,0xfc,0x00,0x00,0x00,0x00,0x07,0xff,0xf8,0x00,0x00,
	0x00,0x03,0xff,0xf8,0x00,0x00,0x00,0x00,0x01,0xff,0xf8,0x00,0x00,
	0x00,0x07,0xff,0xfc,0x00,0x00,0x00,0x00,0x03,0xff,0xfc,0x00,0x00,
	0x00,0x0f,0xff,0xfe,0x00,0x00,0x00,0x00,0x07,0xff,0xfe,0x00,0x00,
	0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00,0x0f,0xff,0xfe,0x00,0x00,
	0x00,0x1f,0xff,0xff,0x80,0x00,0x00,0x00,0x1f,0xff,0xff,0x00,0x00,
	0x00,0x1f,0xff,0xff,0x80,0x00,0x00,0x00,0x1f,0xff,0xff,0x00,0x00,
	0x00,0x3f,0xfe,0xff,0xc0,0x00,0x00,0x00,0x3f,0xff,0xff,0x80,0x00,
	0x00,0x7f,0xfc,0x7f,0xe0,0x00,0x00,0x00,0x7f,0xe7,0xff,0xc0,0x00,
	0x00,0x7f,0xf8,0x3f,0xf0,0x00,0x00,0x00,0xff,0xc3,0xff,0xc0,0x00,
	0x00,0xff,0xf8,0x1f,0xf8,0x00,0x00,0x01,0xff,0x83,0xff,0xe0,0x00,
	0x00,0xff,0xf0,0x07,0xf8,0x00,0x00,0x01,0xfe,0x01,0xff,0xe0,0x00,
	0x00,0xff,0xf0,0x03,0xfc,0x00,0x00,0x03,0xfc,0x01,0xff,0xe0,0x00,
	0x01,0xff,0xe0,0x01,0xfe,0x00,0x00,0x07,0xf8,0x00,0xff,0xf0,0x00,
	0x01,0xff,0xe0,0x00,0xff,0x00,0x00,0x0f,0xf0,0x00,0xff,0xf0,0x00,
	0x01,0xff,0xc0,0x00,0x3f,0x80,0x00,0x1f,0xc0,0x00,0x7f,0xf0,0x00,
	0x01,0xff,0xc0,0x00,0x1f,0x80,0x00,0x1f,0x80,0x00,0x7f,0xf0,0x00,
	0x03,0xff,0xc0,0x00,0x0f,0xc0,0x00,0x3f,0x00,0x00,0x7f,0xf8,0x00,
	0x03,0xff,0x80,0x00,0x07,0xe0,0x00,0x7e,0x00,0x00,0x3f,0xf8,0x00,
	0x03,0xff,0x80,0x00,0x01,0xf0,0x00,0xf8,0x00,0x00,0x3f,0xf8,0x00,
	0x03,0xff,0x80,0x00,0x00,0xf8,0x01,0xf0,0x00,0x00,0x3f,0xf8,0x00,
	0x03,0xff,0x80,0x00,0x00,0x78,0x01,0xe0,0x00,0x00,0x3f,0xf8,0x00,
	0x07,0xff,0x00,0x00,0x00,0x3c,0x03,0xc0,0x00,0x00,0x3f,0xfc,0x00,
	0x07,0xff,0x00,0x00,0x00,0x0e,0x07,0x00,0x00,0x00,0x1f,0xfc,0x00,
	0x07,0xff,0x00,0x00,0x00,0x07,0x0e,0x00,0x00,0x00,0x1f,0xfc,0x00,
	0x07,0xff,0x00,0x00,0x00,0x03,0x9c,0x00,0x00,0x00,0x1f,0xfc,0x00,
	0x07,0xff,0x00,0x00,0x00,0x01,0x98,0x00,0x00,0x00,0x1f,0xfc,0x00,
	0x07,0xff,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x1f,0xfc,0x00
};

/* per-player colors */
static const rgb_t crosshair_colors[] =
{
	MAKE_RGB(0x40,0x40,0xff),
	MAKE_RGB(0xff,0x40,0x40),
	MAKE_RGB(0x40,0xff,0x40),
	MAKE_RGB(0xff,0xff,0x40),
	MAKE_RGB(0xff,0x40,0xff),
	MAKE_RGB(0x40,0xff,0xff),
	MAKE_RGB(0xff,0xff,0xff)
};


/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

static void crosshair_exit(running_machine *machine);
static void animate(const device_config *device, void *param, int vblank_state);


/***************************************************************************
    CORE IMPLEMENTATION
***************************************************************************/


/*-------------------------------------------------
    create_bitmap - create the rendering
    structures for the given player
-------------------------------------------------*/

static void create_bitmap(int player)
{
	/* if we don't have a bitmap for this player yet */
	if (global.bitmap[player] == NULL)
	{
		int x, y;
		char filename[20];
		rgb_t color = crosshair_colors[player];

		/* first try to load a bitmap for the crosshair */
		sprintf(filename, "cross%d.png", player);
		global.bitmap[player] = render_load_png(NULL, filename, NULL, NULL);

		/* if that didn't work, use the built-in one */
		if (global.bitmap[player] == NULL)
		{
			/* allocate a blank bitmap to start with */
			global.bitmap[player] = bitmap_alloc(CROSSHAIR_RAW_SIZE, CROSSHAIR_RAW_SIZE, BITMAP_FORMAT_ARGB32);
			bitmap_fill(global.bitmap[player], NULL, MAKE_ARGB(0x00,0xff,0xff,0xff));

			/* extract the raw source data to it */
			for (y = 0; y < CROSSHAIR_RAW_SIZE / 2; y++)
			{
				/* assume it is mirrored vertically */
				UINT32 *dest0 = BITMAP_ADDR32(global.bitmap[player], y, 0);
				UINT32 *dest1 = BITMAP_ADDR32(global.bitmap[player], CROSSHAIR_RAW_SIZE - 1 - y, 0);

				/* extract to two rows simultaneously */
				for (x = 0; x < CROSSHAIR_RAW_SIZE; x++)
					if ((crosshair_raw_top[y * CROSSHAIR_RAW_ROWBYTES + x / 8] << (x % 8)) & 0x80)
						dest0[x] = dest1[x] = MAKE_ARGB(0xff,0x00,0x00,0x00) | color;
			}
		}

		/* create a texture to reference the bitmap */
		global.texture[player] = render_texture_alloc(render_texture_hq_scale, NULL);
		render_texture_set_bitmap(global.texture[player], global.bitmap[player], NULL, TEXFORMAT_ARGB32, NULL);
	}
}


/*-------------------------------------------------
    crosshair_init - initialize the crosshair
    bitmaps and such
-------------------------------------------------*/

void crosshair_init(running_machine *machine)
{
	const input_port_config *port;
	const input_field_config *field;

	/* request a callback upon exiting */
	add_exit_callback(machine, crosshair_exit);

	/* clear all the globals */
	memset(&global, 0, sizeof(global));

	/* determine who needs crosshairs */
	for (port = machine->portconfig; port != NULL; port = port->next)
		for (field = port->fieldlist; field != NULL; field = field->next)
			if (field->crossaxis != CROSSHAIR_AXIS_NONE)
			{
				int player = field->player;

				assert(player < MAX_PLAYERS);

				/* mark as used and visible */
				global.used[player] = TRUE;
				global.visible[player] = TRUE;

				/* for now, use the main screen */
				global.screen[player] = machine->primary_screen;

				create_bitmap(player);
			}

	/* register the animation callback */
	if (machine->primary_screen != NULL)
		video_screen_register_vblank_callback(machine->primary_screen, animate, NULL);
}


/*-------------------------------------------------
    crosshair_exit - free memory allocated for
    the crosshairs
-------------------------------------------------*/

static void crosshair_exit(running_machine *machine)
{
	int player;

	/* free bitmaps and textures for each player */
	for (player = 0; player < MAX_PLAYERS; player++)
	{
		if (global.texture[player] != NULL)
			render_texture_free(global.texture[player]);
		global.texture[player] = NULL;

		if (global.bitmap[player] != NULL)
			bitmap_free(global.bitmap[player]);
		global.bitmap[player] = NULL;
	}
}


/*-------------------------------------------------
    crosshair_toggle - toggle crosshair
    visibility
-------------------------------------------------*/

void crosshair_toggle(running_machine *machine)
{
	int player;
	int first_hidden_player = -1;

	/* find the first invisible crosshair */
	for (player = 0; player < MAX_PLAYERS; player++)
		if (global.used[player] && !global.visible[player])
		{
			first_hidden_player = player;
			break;
		}

	/* if all visible, turn all off */
	if (first_hidden_player == -1)
		for (player = 0; player < MAX_PLAYERS; player++)
			global.visible[player] = FALSE;

	/* otherwise, turn on the first one that isn't currently on */
	else
		global.visible[first_hidden_player] = TRUE;
}


/*-------------------------------------------------
    animate - animates the crosshair once a frame
-------------------------------------------------*/

static void animate(const device_config *device, void *param, int vblank_state)
{
	int player;

	/* increment animation counter */
	global.animation_counter += 0x04;

	/* compute a fade factor from the current animation value */
	if (global.animation_counter < 0x80)
		global.fade = 0xa0 + (0x60 * ( global.animation_counter & 0x7f) / 0x80);
	else
		global.fade = 0xa0 + (0x60 * (~global.animation_counter & 0x7f) / 0x80);

	/* read all the lightgun values */
	for (player = 0; player < MAX_PLAYERS; player++)
		if (global.used[player])
			input_port_get_crosshair_position(device->machine, player, &global.x[player], &global.y[player]);
}


/*-------------------------------------------------
    crosshair_render - render the crosshairs
    for the given screen
-------------------------------------------------*/

void crosshair_render(const device_config *screen)
{
	int player;

	for (player = 0; player < MAX_PLAYERS; player++)
		/* draw if visible and the right screen */
		if (global.visible[player] &&
			((global.screen[player] == screen) || (global.screen[player] == CROSSHAIR_SCREEN_ALL)))
		{
			/* add a quad assuming a 4:3 screen (this is not perfect) */
			render_screen_add_quad(screen,
						global.x[player] - 0.03f, global.y[player] - 0.04f,
						global.x[player] + 0.03f, global.y[player] + 0.04f,
						MAKE_ARGB(0xc0, global.fade, global.fade, global.fade),
						global.texture[player], PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
		}
}


/*-------------------------------------------------
    crosshair_set_screen - sets the screen(s) for a
    given player's crosshair
-------------------------------------------------*/

void crosshair_set_screen(running_machine *machine, int player, const device_config *screen)
{
	global.screen[player] = screen;
}
