// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program 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
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#pragma once

#include <cstdio>

#include "zlib.h"

#include "boundingbox.h"

class World;
class SimpleBuffer;

#define NULLTILE -1

/** @brief Get a tile index based on coordinates and the size of the
 * TilePane.
 * @param x The x coordinate.
 * @param y The y coordinate.
 * @param width The width of the TilePane.
 * @param height The height of the TilePane.
 * */
inline int getTileIndex(int x, int y, int width, int height) {
	int rx = x + width / 2;
	int ry = y + height / 2;
	
	return rx + ry * width;
}

/** @brief A layer of tiles in the world. */
class TilePane {
	public:
		/** @brief Constructor. */
		TilePane(void);
		~TilePane(void);
		
		/** @brief Get a single tile from a given coordinate.
		 * If the tile is outside the grid, it just returns NULLTILE.
		 * @param x The x coordinate, in tile space.
		 * @param y The y coordinate, in tile space.
		 * */
		inline int getTile(int x, int y) const {
			if(tiles) {
				if(x > -width / 2 && y > -height / 2 && x < width / 2 && y < height / 2) {
					return tiles[getTileIndex(x, y, width, height)];
				}
			}
			return NULLTILE;
		}
		
		/** @brief Set a tile at a given point to a given value.
		 * Automatically resizes the tile pane if it's outside the
		 * boundaries.
		 * @param x X coordinate.
		 * @param y Y coordinate.
		 * @param tile The tile number to set.
		 * @param skipRebuild Set this to true if you want to skip rebuilding vertex buffers.
		 * */
		void setTile(int x, int y, int tile, bool skipRebuild = false);
		
		/** @brief Resize the tile pane. It will take the dimensions and
		 * raise them to the nearest power of two.
		 * @param width The new width.
		 * @param height The new height.
		 * */
		void resize(int width, int height);
		
		/** @brief Clear the tiles, display lists, and texture. */
		void clear(void);

		/// Save (serialize) to a SimpleBuffer object.
		void saveSimpleBuf(SimpleBuffer *buf);
		
		/// Load (unserialize) from a SimpleBuffer object.
		void loadSimpleBuf(SimpleBuffer *buf);

		// TODO: Remove deprecated gzip stuff.
		/** @brief Binary dump the tiles and tile pane information.
		 * @param out The stream to save it to (zlib).
		 * */
		void save(gzFile out);
		
		// TODO: Remove deprecated gzip stuff.
		/** @brief Binary load the tiles and tile pane information.
		 * @param in The stream to load from (zlib).
		 * */
		void load(gzFile in);

		/** @brief Draw this TilePane.
		 * @param visibleArea The area of the world that's currently
		 *   visible.
		 * @param isActiveLayer This is for shading inactive layers in
		 *   the editors. Set to true if it's to be rendered like the
		 *   current layer.
		 * */
		void draw(const BoundingBox &visibleArea, bool isActiveLayer);
		
		/** @brief Shrink the TilePane down so that it's as small as it
		 * can be without erasing any non-NULLTILE spaces.
		 * */
		void shrinkToFit(void);
	
		/** @brief Set which World this TilePane belongs to.
		 * @param world The World it belongs to.
		 * */
		void setWorld(World *world);

		/** @brief Set the size of a single tile. Tiles are squares, so
		 * this value is the width and height of all tiles on this
		 * layer.
		 * @param tileSize The width/height of a single tile, in pixels.
		 * */
		void setTileSize(int tileSize);
		
		/** @brief Get the size of a single tile.
		 * @return The width/height of a tile, in pixels.
		 * */
		int getTileSize(void) const;
		
		/** @brief Set the texture number to use for tiles.
		 * @param textureNum The texture number.
		 * */
		void setTextureNum(int textureNum);
		
		/** @brief Get the texture number.
		 * @return The texture number.
		 * */
		int getTextureNum(void) const;
	
	
		void resetListColors(void);
		int layer;
		void shiftAllTiles(int sx, int sy);

	private:
		int textureNum;
		int tileSize;
	
		World *world;
	
		int width;
		int height;
		int *tiles;
		
		int displayListSize;
		int *displayLists;
		
		void rebuildLists(void);
		void clearLists(void);
		void createLists(void);
		void buildSingleList(int x, int y);
		void drawSingleList(int x, int y);
		int getListNumber(int x, int y);
		int getListIndex(int x, int y);
			
};
