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

    Atari Cloak & Dagger hardware

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

#include "emu.h"
#include "video/resnet.h"
#include "includes/cloak.h"


#define NUM_PENS	(0x40)

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

  CLOAK & DAGGER uses RAM to dynamically
  create the palette. The resolution is 9 bit (3 bits per gun). The palette
  contains 64 entries, but it is accessed through a memory windows 128 bytes
  long: writing to the first 64 bytes sets the MSB of the red component to 0,
  while writing to the last 64 bytes sets it to 1.

  Colors 0-15  Character mapped graphics
  Colors 16-31 Bitmapped graphics (2 palettes selected by 128H)
  Colors 32-47 Sprites
  Colors 48-63 not used

  These are the exact resistor values from the schematics:

  bit 8 -- diode |< -- pullup 1 kohm -- 2.2 kohm resistor -- pulldown 100 pf -- RED
        -- diode |< -- pullup 1 kohm -- 4.7 kohm resistor -- pulldown 100 pf -- RED
        -- diode |< -- pullup 1 kohm -- 10  kohm resistor -- pulldown 100 pf -- RED
        -- diode |< -- pullup 1 kohm -- 2.2 kohm resistor -- pulldown 100 pf -- GREEN
        -- diode |< -- pullup 1 kohm -- 4.7 kohm resistor -- pulldown 100 pf -- GREEN
        -- diode |< -- pullup 1 kohm -- 10  kohm resistor -- pulldown 100 pf -- GREEN
        -- diode |< -- pullup 1 kohm -- 2.2 kohm resistor -- pulldown 100 pf -- BLUE
        -- diode |< -- pullup 1 kohm -- 4.7 kohm resistor -- pulldown 100 pf -- BLUE
  bit 0 -- diode |< -- pullup 1 kohm -- 10  kohm resistor -- pulldown 100 pf -- BLUE

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

WRITE8_HANDLER( cloak_paletteram_w )
{
	cloak_state *state = space->machine().driver_data<cloak_state>();
	state->m_palette_ram[offset & 0x3f] = ((offset & 0x40) << 2) | data;
}


static void set_pens(running_machine &machine)
{
	cloak_state *state = machine.driver_data<cloak_state>();
	UINT16 *palette_ram = state->m_palette_ram;
	static const int resistances[3] = { 10000, 4700, 2200 };
	double weights[3];
	int i;

	/* compute the color output resistor weights */
	compute_resistor_weights(0,	255, -1.0,
							 3, resistances, weights, 0, 1000,
							 0, 0, 0, 0, 0,
							 0, 0, 0, 0, 0);

	for (i = 0; i < NUM_PENS; i++)
	{
		int r, g, b;
		int bit0, bit1, bit2;

		/* red component */
		bit0 = (~palette_ram[i] >> 6) & 0x01;
		bit1 = (~palette_ram[i] >> 7) & 0x01;
		bit2 = (~palette_ram[i] >> 8) & 0x01;
		r = combine_3_weights(weights, bit0, bit1, bit2);

		/* green component */
		bit0 = (~palette_ram[i] >> 3) & 0x01;
		bit1 = (~palette_ram[i] >> 4) & 0x01;
		bit2 = (~palette_ram[i] >> 5) & 0x01;
		g = combine_3_weights(weights, bit0, bit1, bit2);

		/* blue component */
		bit0 = (~palette_ram[i] >> 0) & 0x01;
		bit1 = (~palette_ram[i] >> 1) & 0x01;
		bit2 = (~palette_ram[i] >> 2) & 0x01;
		b = combine_3_weights(weights, bit0, bit1, bit2);

		palette_set_color(machine, i, MAKE_RGB(r, g, b));
	}
}


static void set_current_bitmap_videoram_pointer(cloak_state *state)
{
	state->m_current_bitmap_videoram_accessed  = state->m_bitmap_videoram_selected ? state->m_bitmap_videoram1 : state->m_bitmap_videoram2;
	state->m_current_bitmap_videoram_displayed = state->m_bitmap_videoram_selected ? state->m_bitmap_videoram2 : state->m_bitmap_videoram1;
}

WRITE8_HANDLER( cloak_clearbmp_w )
{
	cloak_state *state = space->machine().driver_data<cloak_state>();

	space->machine().primary_screen->update_now();
	state->m_bitmap_videoram_selected = data & 0x01;
	set_current_bitmap_videoram_pointer(state);

	if (data & 0x02)	/* clear */
		memset(state->m_current_bitmap_videoram_accessed, 0, 256*256);
}

static void adjust_xy(cloak_state *state, int offset)
{
	switch (offset)
	{
		case 0x00:  state->m_bitmap_videoram_address_x--; state->m_bitmap_videoram_address_y++; break;
		case 0x01:								 state->m_bitmap_videoram_address_y--; break;
		case 0x02:  state->m_bitmap_videoram_address_x--;							  break;
		case 0x04:  state->m_bitmap_videoram_address_x++; state->m_bitmap_videoram_address_y++; break;
		case 0x05:								 state->m_bitmap_videoram_address_y++; break;
		case 0x06:  state->m_bitmap_videoram_address_x++;							  break;
	}
}

READ8_HANDLER( graph_processor_r )
{
	cloak_state *state = space->machine().driver_data<cloak_state>();
	UINT8 ret = state->m_current_bitmap_videoram_displayed[(state->m_bitmap_videoram_address_y << 8) | state->m_bitmap_videoram_address_x];

	adjust_xy(state, offset);

	return ret;
}

WRITE8_HANDLER( graph_processor_w )
{
	cloak_state *state = space->machine().driver_data<cloak_state>();

	switch (offset)
	{
		case 0x03: state->m_bitmap_videoram_address_x = data; break;
		case 0x07: state->m_bitmap_videoram_address_y = data; break;
		default:
			state->m_current_bitmap_videoram_accessed[(state->m_bitmap_videoram_address_y << 8) | state->m_bitmap_videoram_address_x] = data & 0x0f;

			adjust_xy(state, offset);
			break;
	}
}

WRITE8_HANDLER( cloak_videoram_w )
{
	cloak_state *state = space->machine().driver_data<cloak_state>();
	UINT8 *videoram = state->m_videoram;

	videoram[offset] = data;
	state->m_bg_tilemap->mark_tile_dirty(offset);
}

WRITE8_HANDLER( cloak_flipscreen_w )
{
	flip_screen_set(space->machine(), data & 0x80);
}

static TILE_GET_INFO( get_bg_tile_info )
{
	cloak_state *state = machine.driver_data<cloak_state>();
	UINT8 *videoram = state->m_videoram;
	int code = videoram[tile_index];

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

VIDEO_START( cloak )
{
	cloak_state *state = machine.driver_data<cloak_state>();

	state->m_bg_tilemap = tilemap_create(machine, get_bg_tile_info, tilemap_scan_rows, 8, 8, 32, 32);

	state->m_bitmap_videoram1 = auto_alloc_array(machine, UINT8, 256*256);
	state->m_bitmap_videoram2 = auto_alloc_array(machine, UINT8, 256*256);
	state->m_palette_ram = auto_alloc_array(machine, UINT16, NUM_PENS);

	set_current_bitmap_videoram_pointer(state);

	state->save_item(NAME(state->m_bitmap_videoram_address_x));
	state->save_item(NAME(state->m_bitmap_videoram_address_y));
	state->save_item(NAME(state->m_bitmap_videoram_selected));
	state->save_pointer(NAME(state->m_bitmap_videoram1), 256*256);
	state->save_pointer(NAME(state->m_bitmap_videoram2), 256*256);
	state->save_pointer(NAME(state->m_palette_ram), NUM_PENS);
	machine.save().register_postload(save_prepost_delegate(FUNC(set_current_bitmap_videoram_pointer), state));
}

static void draw_bitmap(running_machine &machine, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	cloak_state *state = machine.driver_data<cloak_state>();
	int x, y;

	for (y = cliprect.min_y; y <= cliprect.max_y; y++)
		for (x = cliprect.min_x; x <= cliprect.max_x; x++)
		{
			pen_t pen = state->m_current_bitmap_videoram_displayed[(y << 8) | x] & 0x07;

			if (pen)
				bitmap.pix16(y, (x - 6) & 0xff) = 0x10 | ((x & 0x80) >> 4) | pen;
		}
}

static void draw_sprites(running_machine &machine, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	cloak_state *state = machine.driver_data<cloak_state>();
	UINT8 *spriteram = state->m_spriteram;
	int offs;

	for (offs = (0x100 / 4) - 1; offs >= 0; offs--)
	{
		int code = spriteram[offs + 64] & 0x7f;
		int flipx = spriteram[offs + 64] & 0x80;
		int flipy = 0;
		int sx = spriteram[offs + 192];
		int sy = 240 - spriteram[offs];

		if (flip_screen_get(machine))
		{
			sx -= 9;
			sy = 240 - sy;
			flipx = !flipx;
			flipy = !flipy;
		}

		drawgfx_transpen(bitmap, cliprect, machine.gfx[1], code, 0, flipx, flipy,	sx, sy, 0);
	}
}

SCREEN_UPDATE_IND16( cloak )
{
	cloak_state *state = screen.machine().driver_data<cloak_state>();
	set_pens(screen.machine());
	state->m_bg_tilemap->draw(bitmap, cliprect, 0, 0);
	draw_bitmap(screen.machine(), bitmap, cliprect);
	draw_sprites(screen.machine(), bitmap, cliprect);
	return 0;
}
