/* EINA - EFL data type library
 * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library;
 * if not, see <http://www.gnu.org/licenses/>.
 */

#ifndef EINA_TILER_H_
#define EINA_TILER_H_

#include "eina_types.h"
#include "eina_iterator.h"
#include "eina_rectangle.h"

/**
 * @page eina_tiler_example_01
 * @dontinclude eina_tiler_01.c
 *
 * This is an example that illustrates how Eina_Tiler works for a given set of
 * rectangles. The rectangles must be given in the command line in the form:
 * \<width\>x\<height\>+\<x offset\>+\<y offset\>
 * The example will show two panels, the first(input) will show the given
 * rectangles(in different colors) and in the seconds(output) it will show the
 * rectangles given by the tiler. The rectangles will be added one by one every
 * two seconds. A lot of the example deals with actually painting the rectangles
 * so we'll skip over quite a bit of code, but you can see all of it in @ref
 * eina_tiler_01.c "eina_tiler_01.c".
 *
 * The first thing of note in our example is the creation of the tiler:
 * @skipline eina_tiler_new
 * @note @p maxw and @p maxh are calculated such that the tiler's size will
 * fully encompass all given rectangles.
 *
 * We'll now look at the function that actually adds rectangles to our tiler. It
 * first checks if we added all rectangles already and if so stops right there:
 * @dontinclude eina_tiler_01.c
 * @skip static Eina_Bool
 * @until }
 *
 * Our function then clears all rectangles given to us by tiler from the last
 * execution. It does this because each rectangle we add may change everything
 * about the output of eina_tiler:
 * @until output_rects_reset
 *
 * Next we get another rectangle, print it and show it in the input panel:
 * @until add_input_rect
 *
 * We now come to the tiler stuff, we add our new rectangle to it and get a new
 * iterator for the tiler:
 * @until itr
 *
 * We now iterate over our tiler printing every rect it gives us and sowing it
 * in the output panel:
 * @until }
 *
 * We of course must remember to free our iterator and that's it for this
 * function:
 * @until }
 *
 * You should try many different inputs to see how the tiler works, here are a
 * few suggestions:
 * @li 100x100+0+0 100x100+200+200
 * @li 100x100+0+0 100x100+5+5 100x100+10+10 100x100+15+15 100x100+20+20
 * @li 100x100+0+0 100x100+100+100 100x100+200+0 100x100+0+200 100x100+200+200
 * @li 10x10+0+0 10x10+10+10 10x10+20+0 10x10+0+20 10x10+20+20
 *
 * @example eina_tiler_01.c
 */
/**
 * @addtogroup Eina_Data_Types_Group Data Types
 *
 * @{
 */

/**
 * @defgroup Eina_Tiler_Group Tiler
 *
 * @warning This is a very low level tool, in most situations (for example if
 * you're using evas) you won't need this.
 *
 * @section basic Basic usage
 *
 * Eina_Tiler is a tool to facilitate calculations of which areas are damaged
 * and thus need to be re-rendered. The basic usage of Eina_Tiler is to give it
 * the size of your canvas and a set of rectangular areas that need
 * re-rendering, from that and using heuristics it'll tell you an efficient way
 * to re-render in the form of a set of non-overlapping rectangles that covers
 * the whole area that needs re-rendering.
 *
 * The following is pseudo-code showing some simple use of Eina_Tiler:
 * @code
 * tiler = eina_tiler_new(MY_CANVAS_WIDTH, MY_CANVAS_HEIGHT);
 * EINA_LIST_FOREACH(list_of_areas_that_need_re_rendering, l, rect)
 *   eina_tiler_add(tiler, rect);
 * itr = eina_tiler_iterator_new(tiler);
 * EINA_ITERATOR_FOREACH(itr, rect)
 *   my_function_that_repaints_areas_of_the_canvas(rect);
 * @endcode
 *
 * @see eina_tiler_new()
 * @see eina_tiler_rect_add()
 * @see eina_tiler_iterator_new()
 *
 * @warning There are no guarantees that this will be the most efficient way to
 * re-render for any particular case.
 *
 * @section grid_slicer Grid Slicer
 *
 * Grid slicer and Eina_Tiler are usually used together, that is however @b not
 * necessary, they can be used independently. Grid slicer provides an easy API
 * to divide an area in tiles which is useful in certain applications to divide
 * the area that will be rendered into tiles. It's customary to, then create one
 * Eina_Tiler for each tile.
 *
 * The following is pseudo-code showing a very simplified use of grid slicer
 * together with Eina_Tiler:
 * @code
 * itr = eina_tile_grid_slicer_iterator_new(0, 0, MY_CANVAS_WIDTH, MY_CANVAS_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
 * EINA_ITERATOR_FOREACH(itr, grid_info)
 *   {
 *      tiler = eina_tiler_new(grid_info->rect.w, grid_info->rect.w);
 *      EINA_LIST_FOREACH(list_of_areas_that_need_re_rendering_in_this_tile, l, rect)
 *        eina_tiler_add(tiler, rect);
 *      itr = eina_tiler_iterator_new(tiler);
 *      EINA_ITERATOR_FOREACH(itr, rect)
 *      my_function_that_repaints_areas_of_the_canvas(rect);
 *   }
 * @endcode
 *
 * @see eina_tiler_new()
 * @see eina_tiler_rect_add()
 * @see eina_tile_grid_slicer_setup()
 * @see eina_tile_grid_slicer_next()
 * @see eina_tile_grid_slicer_iterator_new()
 *
 * @{
 */

/**
 * @typedef Eina_Tiler
 * Tiler type.
 */
typedef struct _Eina_Tiler Eina_Tiler;

/**
 * @typedef Eina_Tile_Grid_Info
 * Grid type of a tiler.
 */
typedef struct Eina_Tile_Grid_Info Eina_Tile_Grid_Info;

/**
 * @struct Eina_Tile_Grid_Info
 * Grid type of a tiler.
 */
struct Eina_Tile_Grid_Info
{
   unsigned long  col; /**< column of the tile grid */
   unsigned long  row; /**< row of the tile grid*/
   Eina_Rectangle rect; /**< rectangle of the tile grid, coordinates are
                             relative to tile*/
   Eina_Bool      full; /**< whether the grid is full or not */
};

typedef struct _Eina_Tile_Grid_Slicer Eina_Tile_Grid_Slicer;

/**
 * @brief Creates a new tiler with @p w width and @p h height.
 *
 * @param[in] w Width of the tiler
 * @param[in] h Height of the tiler
 * @return The newly created tiler
 *
 * @see eina_tiler_free()
 */
EINA_API Eina_Tiler        *eina_tiler_new(int w, int h);

/**
 * @brief Frees a tiler.
 *
 * @param[in] t The tiler to free.
 *
 * This function frees @p t. It does not free the memory allocated for the
 * elements of @p t.
 */
EINA_API void               eina_tiler_free(Eina_Tiler *t);

/**
 * @brief Sets the size of tiles for a tiler.
 *
 * @param[in,out] t The tiler whose tile size will be set.
 * @param[in] w Width of the tiles.
 * @param[in] h Height of the tiles.
 *
 * @warning @p w and @p h @b must be greater than zero, otherwise tile size
 * won't be changed.
 * @warning Tile size is not used!
 */
EINA_API void               eina_tiler_tile_size_set(Eina_Tiler *t, int w, int h);

/**
 * @brief Changes the size of the area covered by the tiler.
 *
 * @param[in,out] t The tiler whose area size will be set.
 * @param[in] w Width of the area.
 * @param[in] h Height of the area.
 *
 * @since 1.8
 *
 * @warning Must clear the tiler before changing its size.
 */
EINA_API void               eina_tiler_area_size_set(Eina_Tiler *t, int w, int h);

/**
 * @brief Gets the size of the area covered by the tiler.
 *
 * @param[in] t The tiler whose area size will be fetched.
 * @param[out] w Width of the area.
 * @param[out] h Height of the area.
 *
 * @since 1.8
 */
EINA_API void               eina_tiler_area_size_get(const Eina_Tiler *t, int *w, int *h);

/**
 * @brief Defines if we need to follow a strict grid of tile or a loose one.
 *
 * @param[in,out] t The tiler to apply the strict rules to.
 * @param[in] strict Define if it will be strict or loose
 *
 * By default it will be loose.
 *
 * @since 1.8
 */
EINA_API void               eina_tiler_strict_set(Eina_Tiler *t, Eina_Bool strict);

/**
 * @brief Tells if a tiler is empty or not.
 *
 * @param[in] t The tiler to apply the strict rules to.
 * @return #EINA_TRUE when empty, #EINA_FALSE when not.
 *
 * @since 1.8
 */
EINA_API Eina_Bool          eina_tiler_empty(const Eina_Tiler *t);

/**
 * @brief Adds a rectangle to a tiler.
 *
 * @param[in,out] t The tiler in which to add a container.
 * @param[in] r The rectangle to be added.
 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
 *
 * @see eina_tiler_rect_del()
 */
EINA_API Eina_Bool          eina_tiler_rect_add(Eina_Tiler *t, const Eina_Rectangle *r);

/**
 * @brief Removes a rectangle from a tiler.
 *
 * @param[in,out] t The tiler in which to add a container.
 * @param[in] r The rectangle to be removed.
 *
 * @see eina_tiler_rect_add()
 * @see eina_tiler_clear()
 */
EINA_API void               eina_tiler_rect_del(Eina_Tiler *t, const Eina_Rectangle *r);

/**
 * @brief Removes all rectangles from tiles.
 *
 * @param[in,out] t The tiler to clear.
 *
 * @see eina_tiler_rect_del()
 */
EINA_API void               eina_tiler_clear(Eina_Tiler *t);

/**
 * @brief Creates a iterator to access the tilers calculated rectangles.
 *
 * @param[in] t The tiler to iterate over.
 * @return A iterator containing Eina_Rectangle.
 */
EINA_API Eina_Iterator     *eina_tiler_iterator_new(const Eina_Tiler *t);

/**
 * @brief Creates a new Eina_Iterator that iterates over a list of tiles.
 *
 * @param[in] x X axis coordinate.
 * @param[in] y Y axis coordinate.
 * @param[in] w Width.
 * @param[in] h Height.
 * @param[in] tile_w Tile width.
 * @param[in] tile_h Tile height.
 * @return  A pointer to the Eina_Iterator. @c NULL on failure.
 *
 * The region defined by @a x, @a y, @a w, @a h will be divided in to a grid of
 * tiles of width @a tile_w and height @p tile_h, the returned iterator will
 * iterate over every tile in the grid having as its data a #Eina_Tile_Grid_Info.
 *
 * @note This is a convenience function, iterating over the returned iterator is
 * equivalent to calling eina_tile_grid_slicer_setup() and calling
 * eina_tile_grid_slicer_next() until it returns #EINA_FALSE.
 */
EINA_API Eina_Iterator     *eina_tile_grid_slicer_iterator_new(int x, int y, int w, int h, int tile_w, int tile_h);

/**
 * @brief Gets the union of two tilers.
 *
 * @param[in,out] dst The first tiler, will store the result.
 * @param[in] src The second tiler.
 * @return  #EINA_TRUE on success, #EINA_FALSE otherwise.
 *
 * This function gets the union of tilers @p dst and @p src.
 * The result is stored in @p dst. It returns #EINA_TRUE if it succeeds.
 * @since 1.11
 */
EINA_API Eina_Bool          eina_tiler_union(Eina_Tiler *dst, Eina_Tiler *src);

/**
 * @brief Subtracts two tilers.
 *
 * @param[in,out] dst The first tiler, will store the result.
 * @param[in] src The second tiler.
 * @return  #EINA_TRUE on success, #EINA_FALSE otherwise.
 *
 * This function subtracts two tilers @p dst and @p src.
 * The result is stored in @p dst. It returns #EINA_TRUE if it succeeds.
 * @since 1.11
 */
EINA_API Eina_Bool          eina_tiler_subtract(Eina_Tiler *dst, Eina_Tiler *src);

/**
 * @brief Gets the intersection of two tilers.
 *
 * @param[in] t1 The first tile.
 * @param[in] t2 The second tiler.
 * @return  A pointer of intersection result. @c NULL if intersection doesn't exist.
 *
 * This function gets the intersection of two tilers @p t1 and @p t2.
 * It returns a pointer of result if intersection of two tilers exists., otherwise returns NULL.
 * @since 1.11
 */
EINA_API Eina_Tiler        *eina_tiler_intersection(Eina_Tiler *t1, Eina_Tiler *t2);

/**
 * @brief Gets whether two tilers are equal in rects or not.
 *
 * @param[in] t1 The first tiler.
 * @param[in] t2 The second tiler.
 * @return  #EINA_TRUE if equal, #EINA_FALSE if unequal.
 *
 * This function gets result of comparison for @p t1 and @p t2.
 * It returns #EINA_TRUE if tilers are equal.
 * @since 1.11
 */
EINA_API Eina_Bool           eina_tiler_equal(const Eina_Tiler *t1, const Eina_Tiler *t2);

/**
 * @brief Iterates over the tiles set by eina_tile_grid_slicer_setup().
 *
 * @param[in,out] slc Pointer to an Eina_Tile_Grid_Slicer struct.
 * @param[out] rect Pointer to a struct Eina_Tile_Grid_Info *.
 * @return  #EINA_TRUE if the current rect is valid. #EINA_FALSE if there
 * are no more rects to iterate over (and thus the current one isn't valid).
 *
 * This functions iterates over each Eina_Tile_Grid_Info *rect of the grid.
 * eina_tile_grid_slicer_setup() must be called first, and *rect is only valid
 * if this function returns #EINA_TRUE. Its content shouldn't be modified.
 *
 * @note Consider using eina_tile_grid_slicer_iterator_new() instead.
 */
static inline Eina_Bool eina_tile_grid_slicer_next(Eina_Tile_Grid_Slicer *slc, const Eina_Tile_Grid_Info **rect);

/**
 * @brief Sets up an Eina_Tile_Grid_Slicer struct.
 *
 * @param[out] slc Pointer to an Eina_Tile_Grid_Slicer struct.
 * @param[in] x X axis coordinate.
 * @param[in] y Y axis coordinate.
 * @param[in] w Width.
 * @param[in] h Height.
 * @param[in] tile_w Tile width.
 * @param[in] tile_h Tile height.
 * @return  A pointer to the Eina_Iterator, @c NULL on failure.
 *
 * The region defined by @a x, @a y, @a w, @a h will be divided into a grid of
 * tiles of width @a tile_w and height @p tile_h, @p slc can then be used with
 * eina_tile_grid_slicer_next() to access each tile.
 *
 * @note Consider using eina_tile_grid_slicer_iterator_new() instead.
 */
static inline Eina_Bool eina_tile_grid_slicer_setup(Eina_Tile_Grid_Slicer *slc, int x, int y, int w, int h, int tile_w, int tile_h);

#include "eina_inline_tiler.x"

/**
 * @}
 */

/**
 * @}
 */

#endif /* EINA_TILER_H_ */
