// license:BSD-3-Clause
// copyright-holders:Zsolt Vasvari
/***************************************************************************

  video.c

  Functions to emulate the video hardware of the machine.

  Convention: "sl" stands for "searchlight"

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

#include "emu.h"
#include "sound/ay8910.h"
#include "dday.h"


/* Note: There seems to be no way to reset this timer via hardware.
         The game uses a difference method to reset it to 99.

  Thanks Zwaxy for the timer info. */

uint8_t dday_state::dday_countdown_timer_r()
{
	return ((m_timer_value / 10) << 4) | (m_timer_value % 10);
}

TIMER_CALLBACK_MEMBER(dday_state::countdown_timer_callback)
{
	m_timer_value--;

	if (m_timer_value < 0)
		m_timer_value = 99;
}

void dday_state::start_countdown_timer()
{
	m_timer_value = 0;

	m_countdown_timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1));
}


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

  Convert the color PROMs into a more useable format.

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

void dday_state::dday_palette(palette_device &palette) const
{
	uint8_t const *const color_prom = memregion("proms")->base();

	palette.set_shadow_factor(1.0 / 8);

	// create a lookup table for the palette
	for (int i = 0; i < 0x100; i++)
	{
		int const r = pal4bit(color_prom[i + 0x000]);
		int const g = pal4bit(color_prom[i + 0x100]);
		int const b = pal4bit(color_prom[i + 0x200]);

		palette.set_indirect_color(i, rgb_t(r, g, b));
	}

	for (int i = 0; i < 0x100; i++)
		palette.set_pen_indirect(i, i);

	// HACK!!! This table is handgenerated, but it matches the screenshot.  I have no clue how it really works
	palette.set_pen_indirect(0*8+0+0, 0x00);
	palette.set_pen_indirect(0*8+0+1, 0x01);
	palette.set_pen_indirect(0*8+0+2, 0x15);
	palette.set_pen_indirect(0*8+0+3, 0x02);
	palette.set_pen_indirect(0*8+4+0, 0x00);
	palette.set_pen_indirect(0*8+4+1, 0x01);
	palette.set_pen_indirect(0*8+4+2, 0x15);
	palette.set_pen_indirect(0*8+4+3, 0x02);

	palette.set_pen_indirect(1*8+0+0, 0x04);
	palette.set_pen_indirect(1*8+0+1, 0x05);
	palette.set_pen_indirect(1*8+0+2, 0x03);
	palette.set_pen_indirect(1*8+0+3, 0x07);
	palette.set_pen_indirect(1*8+4+0, 0x04);
	palette.set_pen_indirect(1*8+4+1, 0x05);
	palette.set_pen_indirect(1*8+4+2, 0x03);
	palette.set_pen_indirect(1*8+4+3, 0x07);

	palette.set_pen_indirect(2*8+0+0, 0x08);
	palette.set_pen_indirect(2*8+0+1, 0x15);
	palette.set_pen_indirect(2*8+0+2, 0x0a);
	palette.set_pen_indirect(2*8+0+3, 0x03);
	palette.set_pen_indirect(2*8+4+0, 0x08);
	palette.set_pen_indirect(2*8+4+1, 0x15);
	palette.set_pen_indirect(2*8+4+2, 0x0a);
	palette.set_pen_indirect(2*8+4+3, 0x03);

	palette.set_pen_indirect(3*8+0+0, 0x08);
	palette.set_pen_indirect(3*8+0+1, 0x15);
	palette.set_pen_indirect(3*8+0+2, 0x0a);
	palette.set_pen_indirect(3*8+0+3, 0x03);
	palette.set_pen_indirect(3*8+4+0, 0x08);
	palette.set_pen_indirect(3*8+4+1, 0x15);
	palette.set_pen_indirect(3*8+4+2, 0x0a);
	palette.set_pen_indirect(3*8+4+3, 0x03);

	palette.set_pen_indirect(4*8+0+0, 0x10);
	palette.set_pen_indirect(4*8+0+1, 0x11);
	palette.set_pen_indirect(4*8+0+2, 0x12);
	palette.set_pen_indirect(4*8+0+3, 0x07);
	palette.set_pen_indirect(4*8+4+0, 0x10);
	palette.set_pen_indirect(4*8+4+1, 0x11);
	palette.set_pen_indirect(4*8+4+2, 0x12);
	palette.set_pen_indirect(4*8+4+3, 0x07);

	palette.set_pen_indirect(5*8+0+0, 0x1d);
	palette.set_pen_indirect(5*8+0+1, 0x15);
	palette.set_pen_indirect(5*8+0+2, 0x16);
	palette.set_pen_indirect(5*8+0+3, 0x1b);
	palette.set_pen_indirect(5*8+4+0, 0x1d);
	palette.set_pen_indirect(5*8+4+1, 0x15);
	palette.set_pen_indirect(5*8+4+2, 0x16);
	palette.set_pen_indirect(5*8+4+3, 0x1b);

	palette.set_pen_indirect(6*8+0+0, 0x1d);
	palette.set_pen_indirect(6*8+0+1, 0x15);
	palette.set_pen_indirect(6*8+0+2, 0x1a);
	palette.set_pen_indirect(6*8+0+3, 0x1b);
	palette.set_pen_indirect(6*8+4+0, 0x1d);
	palette.set_pen_indirect(6*8+4+1, 0x15);
	palette.set_pen_indirect(6*8+4+2, 0x1a);
	palette.set_pen_indirect(6*8+4+3, 0x1b);

	palette.set_pen_indirect(7*8+0+0, 0x1d);
	palette.set_pen_indirect(7*8+0+1, 0x02);
	palette.set_pen_indirect(7*8+0+2, 0x04);
	palette.set_pen_indirect(7*8+0+3, 0x1b);
	palette.set_pen_indirect(7*8+4+0, 0x1d);
	palette.set_pen_indirect(7*8+4+1, 0x02);
	palette.set_pen_indirect(7*8+4+2, 0x04);
	palette.set_pen_indirect(7*8+4+3, 0x1b);
}


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

  Callbacks for the TileMap code

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

TILE_GET_INFO_MEMBER(dday_state::get_bg_tile_info)
{
	int code;

	code = m_bgvideoram[tile_index];
	tileinfo.set(0, code, code >> 5, 0);
}

TILE_GET_INFO_MEMBER(dday_state::get_fg_tile_info)
{
	int code, flipx;

	flipx = m_colorram[tile_index & 0x03e0] & 0x01;
	code = m_fgvideoram[flipx ? tile_index ^ 0x1f : tile_index];
	tileinfo.set(2, code, code >> 5, flipx ? TILE_FLIPX : 0);
}

TILE_GET_INFO_MEMBER(dday_state::get_text_tile_info)
{
	int code;

	code = m_textvideoram[tile_index];
	tileinfo.set(1, code, code >> 5, 0);
}

TILE_GET_INFO_MEMBER(dday_state::get_sl_tile_info)
{
	int code, sl_flipx, flipx;
	uint8_t* sl_map;

	sl_map = &memregion("user1")->base()[(m_sl_image & 0x07) * 0x0200];

	flipx = (tile_index >> 4) & 0x01;
	sl_flipx = (m_sl_image >> 3) & 0x01;

	/* bit 4 is really a flip indicator.  Need to shift bits 5-9 to the right by 1 */
	tile_index = ((tile_index & 0x03e0) >> 1) | (tile_index & 0x0f);

	code = sl_map[flipx ? tile_index ^ 0x0f : tile_index];

	if ((sl_flipx != flipx) && (code & 0x80))
		/* no mirroring, draw dark spot */
		code = 1;

	tileinfo.set(3, code & 0x3f, 0, flipx ? TILE_FLIPX : 0);
}


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

  Start the video hardware emulation.

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

void dday_state::video_start()
{
	m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(dday_state::get_bg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
	m_fg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(dday_state::get_fg_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
	m_text_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(dday_state::get_text_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
	m_sl_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(dday_state::get_sl_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);

	m_screen->register_screen_bitmap(m_main_bitmap);

	m_bg_tilemap->set_transmask(0, 0x00f0, 0xff0f); /* pens 0-3 have priority over the foreground layer */
	m_fg_tilemap->set_transparent_pen(0);
	m_text_tilemap->set_transparent_pen(0);

	m_countdown_timer = timer_alloc(FUNC(dday_state::countdown_timer_callback), this);

	start_countdown_timer();
}

void dday_state::dday_bgvideoram_w(offs_t offset, uint8_t data)
{
	m_bgvideoram[offset] = data;
	m_bg_tilemap->mark_tile_dirty(offset);
}

void dday_state::dday_fgvideoram_w(offs_t offset, uint8_t data)
{
	m_fgvideoram[offset] = data;
	m_fg_tilemap->mark_tile_dirty(offset);
	m_fg_tilemap->mark_tile_dirty(offset ^ 0x1f);  /* for flipx case */
}

void dday_state::dday_textvideoram_w(offs_t offset, uint8_t data)
{
	m_textvideoram[offset] = data;
	m_text_tilemap->mark_tile_dirty(offset);
}

void dday_state::dday_colorram_w(offs_t offset, uint8_t data)
{
	offset &= 0x03e0;

	m_colorram[offset & 0x3e0] = data;

	for (int i = 0; i < 0x20; i++)
		m_fg_tilemap->mark_tile_dirty(offset + i);
}

uint8_t dday_state::dday_colorram_r(offs_t offset)
{
	return m_colorram[offset & 0x03e0];
}


void dday_state::dday_sl_control_w(uint8_t data)
{
	if (m_sl_image != data)
	{
		m_sl_image = data;
		m_sl_tilemap->mark_all_dirty();
	}
}


void dday_state::dday_control_w(uint8_t data)
{
	//if (data & 0xac)  logerror("Control = %02X\n", data & 0xac);

	/* bit 0 is coin counter 1 */
	machine().bookkeeping().coin_counter_w(0, data & 0x01);

	/* bit 1 is coin counter 2 */
	machine().bookkeeping().coin_counter_w(1, data & 0x02);

	/* bit 4 is sound enable */
	if (!(data & 0x10) && (m_control & 0x10))
		m_ay1->reset();

	machine().sound().system_mute(!BIT(data, 4));

	/* bit 6 is search light enable */
	m_sl_enable = data & 0x40;

	m_control = data;
}

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

  Display refresh

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

uint32_t dday_state::screen_update_dday(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	m_bg_tilemap->draw(screen, m_main_bitmap, cliprect, TILEMAP_DRAW_LAYER1, 0);
	m_fg_tilemap->draw(screen, m_main_bitmap, cliprect, 0, 0);
	m_bg_tilemap->draw(screen, m_main_bitmap, cliprect, TILEMAP_DRAW_LAYER0, 0);
	m_text_tilemap->draw(screen, m_main_bitmap, cliprect, 0, 0);

	if (m_sl_enable)
	{
		/* apply shadow */
		bitmap_ind16 &sl_bitmap = m_sl_tilemap->pixmap();

		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
			for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
			{
				uint16_t src_pixel = m_main_bitmap.pix(y, x);

				if (sl_bitmap.pix(y, x) == 0xff)
					src_pixel += m_palette->entries();

				bitmap.pix(y, x) = src_pixel;
			}
	}
	else
		copybitmap(bitmap, m_main_bitmap, 0, 0, 0, 0, cliprect);

	return 0;
}
