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

    tilemap.h

    Generic tilemap management system.

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

****************************************************************************

    Core concepts:

        Tilemap = a 2-dimensional array of tiles; each tile has its own
            independent characteristics which describe how that tile is
            rendered. A tilemap is described by the number of rows and
            columns in the map. Each tile in the tilemap is a fixed size
            specified as width and height in pixels.

        Tile = a single rectangular element in a tilemap; tiles can be
            any size, but all tiles in a tilemap are the same size. Each
            tile is described by the following parameters:

            pen_data (required): pointer to an array of 8bpp pen data
                describing the raw pixels to be rendered for a tile;
                for example, an 8x8 tile requires 64 bytes of pen_data

            palette_base (required): the base index in the global palette;
                each pixel fetched from pen_data will have this base added
                to it before it is stored in the pixmap

            mask_data (optional): pointer to an array of 1bpp mask data
                which controls which pixels are to be considered visible
                or transparent (part of no layer); data is packed MSB
                first with no padding, so an 8x8 tile requires 64 bits or
                8 bytes data

            category (optional): specifies one of 16 categories for the
                pixels in the tile; the category controls which tiles are
                rendered during a tilemap_draw() call

            group (optional): specifies one of 256 groups for pen mapping;
                each pen in the tile is looked up in a table to determine
                which layer(s) it belongs to, and the group selects one of
                256 different tables to use for this mapping

            flags (optional): specifies one or more of the following bits:

                TILE_FLIPX means render the tile flipped horizontally

                TILE_FLIPY means render the tile flipped vertically

                TILE_4BPP means pen_data is actually packed 4bpp pixels
                    (low 4 bits first, then upper 4 bits)

                TILE_FORCE_LAYERn means all pixels are forced to be in
                    layer n, where n = 0,1,2

        Pen = another name for pixel value; tilemap graphics are either
            4 or 8 bits per pixel, meaning that pen values range from
            0-15 (4bpp) or 0-255 (8bpp).

        Logical index = index of a tile in a tilemap, assuming perfect
            packing in row-major order; this is always equal to
            (rownum * tilemap_columns + colnum).

        Memory index = index of a tile in memory; this often does not
            map 1:1 to the logical index (though it can). A mapper
            function is provided when creating a new tilemap that can
            map from (column,row) to memory index.

        Layer = one of 3 categories each pixel can belong to, based on
            the pens in a tile; traditionally, layer 0 is the only
            one used. If a pixel does not belong to any layers, it is
            considered transparent.

        Group = one of 256 per-tile selectable means of mapping from
            pens to layers; traditionally only group 0 is used.

        Category = a 4-bit value specified per-tile which allows for
            separating tiles into categories which can be rendered
            independently.

        Pixmap = a 16bpp bitmap containing the full tilemap with
            all tiles rendered.

        Flagsmap = an 8bpp bitmap containing per-pixel flags,
            specifically the category (specified in bits 0-3) and
            the layer (specified in bits 4-6).

****************************************************************************

    How to use a tilemap:

    1. First create a new tilemap by calling tilemap_create(). The
        parameters are as follows:

        tile_get_info = pointer to a callback function which accepts a
            memory index and in return fills in a tile_info structure
            that describes the characteristics of a tile; this function
            will be called whenever a dirty tile needs to be rendered

        mapper = pointer to a callback function which maps the logical
            column and row to a memory index; several standard mappers
            are provided, with tilemap_scan_rows being the most common

        type = the type of tilemap this is;

            TILEMAP_TYPE_PEN - this is the most common type,
                and means that transparency is determined by taking the
                raw pen value and looking it up in a table to determine
                which layer(s) each pixel belongs to

            TILEMAP_TYPE_COLORTABLE - this is less common and
                will eventually be deprecated; it means that transparency
                is determined by taking the raw pen value, looking it up
                in the machine's colortable, and then looking that value
                up in a table to determine which layer(s) each pixel
                belongs to

        tilewidth = the width, in pixels, of each individual tile

        tileheight = the height, in pixels, of each individual tile

        cols = the number of columns in the tilemap

        rows = the number of rows in the tilemap

    2. Once you have created your tilemap, you need to configure it.
        Common configuration tasks include:

            * marking one of the pens as transparent via
                tilemap_set_transparent_pen()

            * performing more complex pen-to-layer mapping via
                tilemap_map_pen_to_layer() or
                tilemap_map_pens_to_layer()

            * configuring global scroll offsets via
                tilemap_set_scrolldx() and tilemap_set_scrolldy()

            * specifying a pointer that is passed to your tile_get_info
                callback via tilemap_set_user_data()

            * setting a global palette offset via
                tilemap_set_palette_offset()

    3. In your memory write handlers for the tile memory, anytime tile
        data is modified, you need to mark the tile dirty so that it is
        re-rendered with the new data the next time the tilemap is drawn.
        Use tilemap_mark_tile_dirty() and pass in the memory index.

    4. In your handlers for scrolling, update the scroll values for the
        tilemap via tilemap_set_scrollx() and tilemap_set_scrolly().

    5. If any other major characteristics of the tilemap change (generally
        any global state that is used by the tile_get_info callback but
        which is not reported via other calls to the tilemap code), you
        should invalidate the entire tilemap. You can do this by calling
        tilemap_mark_all_tiles_dirty().

    6. In your VIDEO_UPDATE callback, render the tiles by calling
        tilemap_draw() or tilemap_draw_roz(). If you need to do custom
        rendering and want access to the raw pixels, call
        tilemap_get_pixmap() to get a pointer to the updated mame_bitmap
        containing the tilemap graphics.

****************************************************************************

    The following example shows how to use the tilemap system to create
    a simple tilemap where pen 0 is transparent. Each tile is 8x8 pixels
    in size, and there are 32 rows and 64 columns, stored in row-major
    order. Based on bits in the tile memory, tiles can be drawn either
    behind or in front of sprites.

        tilemap *tmap;
        UINT16 *my_tmap_memory;

        TILE_GET_INFO( my_get_info )
        {
            UINT8 tiledata = my_tmap_memory[tile_index];
            UINT8 code = tiledata & 0xff;
            UINT8 color = (tiledata >> 8) & 0x1f;
            UINT8 flipx = (tiledata >> 13) & 1;
            UINT8 flipy = (tiledata >> 14) & 1;
            UINT8 category = (tiledata >> 15) & 1;

            // set the common info for the tile
            SET_TILE_INFO(
                1,              // use machine->gfx[1] for tile graphics
                code,           // the index of the graphics for this tile
                color,          // the color to use for this tile
                (flipx ? TILE_FLIPX : 0) |  // flags for this tile; also
                (flipy ? TILE_FLIPY : 0);   // see the FLIP_YX macro
            );

            // set the category of each tile based on the high bit; this
            // allows us to draw each category independently
            tileinfo->category = category;
        }

        VIDEO_START( mydriver )
        {
            // first create the tilemap
            tmap = tilemap_create(
                    my_get_info,            // pointer to your get_info
                    tilemap_scan_rows,      // standard row-major mapper
                    TILEMAP_TYPE_PEN, // transparency from pens
                    8,8,                    // 8x8 tiles
                    64,32);                 // 64 columns, 32 rows

            // then set the transparent pen; all other pens will default
            // to being part of layer 0
            tilemap_set_transparent_pen(tmap, 0);
        }

        VIDEO_UPDATE( mydriver )
        {
            // draw the tilemap first, fully opaque since it needs to
            // erase all previous pixels
            tilemap_draw(
                bitmap,                 // destination bitmap
                cliprect,               // clipping rectangle
                tmap,                   // tilemap to draw
                TILEMAP_DRAW_OPAQUE,    // flags
                0);                     // don't use priority_bitmap

            // next draw the sprites
            my_draw_sprites();

            // then draw the tiles which have priority over sprites
            tilemap_draw(
                bitmap,                 // destination bitmap
                cliprect,               // clipping rectangle
                tmap,                   // tilemap to draw
                TILEMAP_DRAW_CATEGORY(1),// flags: draw category 1
                0);                     // don't use priority_bitmap

            return 0;
        }

****************************************************************************

    Tilemap techniques:

    * The previous tilemap code specified a number of different tilemap
        types that were fundamentally similar. This has been collapsed
        into just two types now. Here is how to replicate that
        functionality with the new system:

        TILEMAP_OPAQUE: If you want a tilemap with no transparency,
            you don't need to do anything; by default all pens map to
            layer 0 and are thus non-transparent. Note that a lot of
            code used to create OPAQUE tilemaps and then set the
            transparent pen for them; this no longer works.

        TILEMAP_TRANSPARENT: This described a tilemap with a single
            transparent pen. To create the same effect, make a new tilemap
            of type TILEMAP_TYPE_PEN, and then call
            tilemap_set_transparent_pen() to specify which pen is
            transparent; all other pens will map to layer 0.

        TILEMAP_TRANSPARENT_COLOR: This works just like before and
            even has the same type name. Create a tilemap of type
            TILEMAP_TYPE_COLORTABLE and call
            tilemap_set_transparent_pen() to specify which remapped pen
            is transparent.

        TILEMAP_BITMASK: This type is no longer special; with the new
            code, any tile_get_info callback can specify a bitmask which
            will be processed after rendering to make some pixels
            transparent.

        TILEMAP_SPLIT: This type used to let you map pens into two
            layers (called "front" and "back") based on their pens. It
            also allowed for you to choose one of 4 mappings on a per-tile
            basis. All of this functionality is now expanded: you can
            specify one of 3 layers and can choose from one of 256 mappings
            on a per-tile basis. You just create a tilemap of type
            TILEMAP_TYPE_PEN and call tilemap_set_transmask(),
            which still exists but maps onto the new behavior. The "front"
            layer is now "layer 0" and the "back" layer is now "layer 1".

        TILEMAP_SPLIT_PENBIT: This type was only used in one driver
            and is not worth describing in detail how the new mapping
            works. :)

    * By far the most common usage of layers is the simplest: layer 0 is
        the only one used and rendered. If you want a particular tile to
        render opaque, you can set the TILE_FORCE_LAYER0 file in your
        tile_get_info callback.

    * When drawing, you can specify in the flags which classes of pixels
        to draw. In the low 4 bits, you specify the category; this must
        match exactly the category specified for the tile in order to
        render. In addition, the flags TILEMAP_DRAW_LAYERn allow you to
        control which layer pattern to draw; note that combining these
        flags may produce interesting results. Specifying both layer 0
        and layer 1 will render only pixels that are in *both* layers.
        If you want to render everything, regardless of layer, specify
        the TILEMAP_DRAW_OPAQUE flag. If you don't specify any layers,
        TILEMAP_DRAW_LAYER0 is assumed.

    * If you want to render with alpha blending, you can call
        tilemap_draw() with the TILEMAP_DRAW_ALPHA flag. To configure the
        amount of alpha blending, call alpha_set_level() ahead of time.

    * To configure more complex pen-to-layer mapping, use the
        tilemap_map_pens_to_layer() call. This call takes a group number
        so that you can configure 1 of the 256 groups independently.
        It also takes a pen and a mask; the mapping is updated for all
        pens where ((pennum & mask) == pen). To set all the pens in a
        group to the same value, pass a mask of 0. To set a single pen in
        a group, pass a mask of ~0. The helper function
        tilemap_map_pen_to_layer() does this for you.

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

#pragma once

#ifndef __TILEMAP_H__
#define __TILEMAP_H__

#include "mamecore.h"


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

/* ALL_TILEMAPS may be used with: tilemap_set_flip, tilemap_mark_all_tiles_dirty */
#define ALL_TILEMAPS					NULL


/* maximum number of groups */
#define TILEMAP_NUM_GROUPS				256


/* these flags control tilemap_draw() behavior */
#define TILEMAP_DRAW_CATEGORY_MASK		0x0f		/* specify the category to draw */
#define TILEMAP_DRAW_CATEGORY(x)		(x)			/* specify category to draw */
#define TILEMAP_DRAW_LAYER0				0x10		/* draw layer 0 */
#define TILEMAP_DRAW_LAYER1				0x20		/* draw layer 1 */
#define TILEMAP_DRAW_LAYER2				0x40		/* draw layer 2 */
#define TILEMAP_DRAW_OPAQUE				0x80		/* draw everything, even transparent stuff */
#define TILEMAP_DRAW_ALPHA				0x100		/* draw with alpha blending */


/* per-pixel flags in the transparency_bitmap */
#define TILEMAP_PIXEL_CATEGORY_MASK		0x0f		/* category is stored in the low 4 bits */
#define TILEMAP_PIXEL_TRANSPARENT		0x00		/* transparent if in none of the layers below */
#define TILEMAP_PIXEL_LAYER0			0x10		/* pixel is opaque in layer 0 */
#define TILEMAP_PIXEL_LAYER1			0x20		/* pixel is opaque in layer 1 */
#define TILEMAP_PIXEL_LAYER2			0x40		/* pixel is opaque in layer 2 */


/* per-tile flags, set by get_tile_info callback */
#define TILE_FLIPX						0x01		/* draw this tile horizontally flipped */
#define TILE_FLIPY						0x02		/* draw this tile vertically flipped */
#define TILE_4BPP						0x04		/* tile data is packed 4bpp */
#define TILE_FORCE_LAYER0				TILEMAP_PIXEL_LAYER0 /* force all pixels to be layer 0 (no transparency) */
#define TILE_FORCE_LAYER1				TILEMAP_PIXEL_LAYER1 /* force all pixels to be layer 1 (no transparency) */
#define TILE_FORCE_LAYER2				TILEMAP_PIXEL_LAYER2 /* force all pixels to be layer 2 (no transparency) */


/* tilemap global flags, used by tilemap_set_flip() */
#define TILEMAP_FLIPX					TILE_FLIPX	/* draw the tilemap horizontally flipped */
#define TILEMAP_FLIPY					TILE_FLIPY	/* draw the tilemap vertically flipped */


/* set this value for a scroll row/column to fully disable it */
#define TILE_LINE_DISABLED				0x80000000



/***************************************************************************
    MACROS
***************************************************************************/

/* function definition for a get info callback */
#define TILE_GET_INFO(_name)			void _name(running_machine *machine, tile_data *tileinfo, tilemap_memory_index tile_index, void *param)

/* function definition for a logical-to-memory mapper */
#define TILEMAP_MAPPER(_name)			tilemap_memory_index _name(UINT32 col, UINT32 row, UINT32 num_cols, UINT32 num_rows)

/* useful macro inside of a TILE_GET_INFO callback to set tile information  */
#define SET_TILE_INFO(GFX,CODE,COLOR,FLAGS) tileinfo_set(machine, tileinfo, GFX, CODE, COLOR, FLAGS)

/* Macros for setting tile attributes in the TILE_GET_INFO callback: */
/*   TILE_FLIP_YX assumes that flipy is in bit 1 and flipx is in bit 0 */
/*   TILE_FLIP_XY assumes that flipy is in bit 0 and flipx is in bit 1 */
/*   TILE_GROUP shifts a split group number appropriately to OR into the tile flags */
#define TILE_FLIPYX(YX)					((YX) & 3)
#define TILE_FLIPXY(XY)					((((XY) & 2) >> 1) | (((XY) & 1) << 1))



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

/* memory indexes are unsigned integers */
typedef UINT32 tilemap_memory_index;

/* opaque tilemap definition */
typedef struct _tilemap tilemap;


/* tilemap types */
enum _tilemap_type
{
	TILEMAP_TYPE_PEN,				/* pen-to-layer mapping is determined by pen lookup */
	TILEMAP_TYPE_COLORTABLE			/* pen-to-layer mapping is determined by colortable[pen] lookup */
};
typedef enum _tilemap_type tilemap_type;


/* tile_data is filled in by the get_tile_info callback */
typedef struct _tile_data tile_data;
struct _tile_data
{
	const UINT8 *	pen_data;		/* required */
	const UINT8 *	mask_data;		/* required for TILEMAP_TYPE_PEN */
	pen_t			palette_base;	/* defaults to 0 */
	UINT8 			category;		/* defaults to 0; range from 0..15 */
	UINT8			group;			/* defaults to 0; range from 0..TILEMAP_NUM_GROUPS */
	UINT8 			flags;			/* defaults to 0; one or more of TILE_* flags above */
};


/* callback function to get info about a tile */
typedef void (*tile_get_info_callback)(running_machine *machine, tile_data *tileinfo, tilemap_memory_index tile_index, void *param);

/* callback function to map a column,row pair to a memory index */
typedef tilemap_memory_index (*tilemap_mapper_callback)(UINT32 col, UINT32 row, UINT32 num_cols, UINT32 num_rows);



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

extern mame_bitmap *priority_bitmap;



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


/* ----- system-wide management ----- */

/* initialize the tilemap system -- not for use by drivers */
void tilemap_init(running_machine *machine);



/* ----- tilemap creation and configuration ----- */

/* create a new tilemap; note that tilemaps are tracked by the core so there is no dispose */
tilemap *tilemap_create(tile_get_info_callback tile_get_info, tilemap_mapper_callback mapper, tilemap_type type, int tilewidth, int tileheight, int cols, int rows);

/* specify a parameter to be passed into the tile_get_info callback */
void tilemap_set_user_data(tilemap *tmap, void *user_data);

/* specify an offset to be added to each pixel before looking up the palette */
void tilemap_set_palette_offset(tilemap *tmap, UINT32 offset);

/* set an enable flag for the tilemap; if 0, requests to draw the tilemap are ignored */
void tilemap_set_enable(tilemap *tmap, int enable);

/* set a global flip for the tilemap; ALL_TILEMAPS can be passed here as well */
void tilemap_set_flip(tilemap *tmap, UINT32 attributes);



/* ----- dirty tile marking ----- */

/* mark a single tile dirty based on its memory index */
void tilemap_mark_tile_dirty(tilemap *tmap, tilemap_memory_index memory_index);

/* mark all the tiles in a tilemap dirty; ALL_TILEMAPS can be passed here as well */
void tilemap_mark_all_tiles_dirty(tilemap *tmap);



/* ----- pen-to-layer mapping ----- */

/* specify the mapping of one or more pens (where (<pen> & mask == pen) to a layer */
void tilemap_map_pens_to_layer(tilemap *tmap, int group, pen_t pen, pen_t mask, UINT8 layermask);

/* set a single transparent pen into the tilemap, mapping all other pens to layer 0 */
void tilemap_set_transparent_pen(tilemap *tmap, pen_t pen);

/* set up the first 32 pens using a foreground (layer 0) mask and a background (layer 1) mask */
void tilemap_set_transmask(tilemap *tmap, int group, UINT32 fgmask, UINT32 bgmask);



/* ----- tilemap scrolling ----- */

/* specify the number of independently scrollable row units; each unit covers height/scroll_rows pixels */
void tilemap_set_scroll_rows(tilemap *tmap, UINT32 scroll_rows);

/* specify the number of independently scrollable column units; each unit covers width/scroll_cols pixels */
void tilemap_set_scroll_cols(tilemap *tmap, UINT32 scroll_cols);

/* specify global horizontal and vertical scroll offsets, for non-flipped and flipped cases */
void tilemap_set_scrolldx(tilemap *tmap, int dx, int dx_if_flipped);
void tilemap_set_scrolldy(tilemap *tmap, int dy, int dy_if_flipped);

/* return the global horizontal or vertical scroll offset, based on current flip state */
int tilemap_get_scrolldx(tilemap *tmap);
int tilemap_get_scrolldy(tilemap *tmap);

/* specify the scroll value for a row/column unit */
void tilemap_set_scrollx(tilemap *tmap, int row, int value);
void tilemap_set_scrolly(tilemap *tmap, int col, int value);

/* return the scroll value for a row/column unit */
int tilemap_get_scrollx(tilemap *tmap, int row);
int tilemap_get_scrolly(tilemap *tmap, int col);



/* ----- internal map access ----- */

/* return a pointer to the (updated) internal pixmap for a tilemap */
mame_bitmap *tilemap_get_pixmap(tilemap *tmap);

/* return a pointer to the (updated) internal flagsmap for a tilemap */
mame_bitmap *tilemap_get_flagsmap(tilemap *tmap);

/* return a pointer to the (updated) internal per-tile flags for a tilemap */
UINT8 *tilemap_get_tile_flags(tilemap *tmap);



/* ----- tilemap rendering ----- */

/* draw a tilemap to the destination with clipping; pixels apply priority/priority_mask to the priority bitmap */
void tilemap_draw_primask(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap, UINT32 flags, UINT8 priority, UINT8 priority_mask);

/* draw a tilemap to the destination with clipping and arbitrary rotate/zoom; */
/* pixels apply priority/priority_mask to the priority bitmap */
void tilemap_draw_roz_primask(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap,
		UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy,
		int wraparound, UINT32 flags, UINT8 priority, UINT8 priority_mask);



/* ----- indexed tilemap handling ----- */

/* return the number of tilemaps */
int tilemap_count(void);

/* return the size of an indexed tilemap */
void tilemap_size_by_index(int number, UINT32 *width, UINT32 *height);

/* render an indexed tilemap with fixed characteristics (no priority) */
void tilemap_draw_by_index(mame_bitmap *dest, int number, UINT32 scrollx, UINT32 scrolly);



/* ----- common logical-to-memory mappers ----- */

/* scan in row-major order with optional flipping */
TILEMAP_MAPPER( tilemap_scan_rows );
TILEMAP_MAPPER( tilemap_scan_rows_flip_x );
TILEMAP_MAPPER( tilemap_scan_rows_flip_y );
TILEMAP_MAPPER( tilemap_scan_rows_flip_xy );

/* scan in column-major order with optional flipping */
TILEMAP_MAPPER( tilemap_scan_cols );
TILEMAP_MAPPER( tilemap_scan_cols_flip_x );
TILEMAP_MAPPER( tilemap_scan_cols_flip_y );
TILEMAP_MAPPER( tilemap_scan_cols_flip_xy );



/***************************************************************************
    INLINE FUNCTIONS
***************************************************************************/

/*-------------------------------------------------
    tileinfo_set - set the values of a tileinfo
    structure
-------------------------------------------------*/

INLINE void tileinfo_set(running_machine *machine, tile_data *tileinfo, int gfxnum, int rawcode, int rawcolor, int flags)
{
	const gfx_element *	gfx = machine->gfx[gfxnum];
	int code = rawcode % gfx->total_elements;
	tileinfo->pen_data = gfx->gfxdata + code * gfx->char_modulo;
	tileinfo->palette_base = gfx->color_base + gfx->color_granularity * rawcolor;
	tileinfo->flags = flags;
	if (gfx->flags & GFX_ELEMENT_PACKED)
		tileinfo->flags |= TILE_4BPP;
}


/*-------------------------------------------------
    tilemap_map_pen_to_layer - map a single pen
    to a layer
-------------------------------------------------*/

INLINE void tilemap_map_pen_to_layer(tilemap *tmap, int group, pen_t pen, UINT8 layermask)
{
	tilemap_map_pens_to_layer(tmap, group, pen, ~0, layermask);
}


/*-------------------------------------------------
    tilemap_draw - shortcut to
    tilemap_draw_primask
-------------------------------------------------*/

INLINE void tilemap_draw(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap, UINT32 flags, UINT8 priority)
{
	tilemap_draw_primask(dest, cliprect, tmap, flags, priority, 0xff);
}


/*-------------------------------------------------
    tilemap_draw_roz - shortcut to
    tilemap_draw_roz_primask
-------------------------------------------------*/

INLINE void tilemap_draw_roz(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap,
		UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy,
		int wraparound, UINT32 flags, UINT8 priority)
{
	tilemap_draw_roz_primask(dest, cliprect, tmap, startx, starty, incxx, incxy, incyx, incyy, wraparound, flags, priority, 0xff);
}



#endif	/* __TILEMAP_H__ */
