// ---------------------------------------------------------------------------
// 
//   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 "../hacks/winhacks.h"

#include <vector>
#include <string>
#include <map>

#include <SDL.h>

#include "../3rdparty/tinyxml/tinyxml.h"

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

#include "../math/vector2d.h"
#include "../math/vector3d.h"
#include "boundingbox.h"
#include "tilepane.h"
#include "soundresourcemanager.h"

union WidgetEvent;
class QuadTree;
class Entity;
class TextureManager;
class FontManager;
class EditorUI;
class BoundingBox;
class Renderer;

union SDL_Event;

/** @brief Class representing the entire game. */
class World {

    public:

        World(const std::string &gameName);
        virtual ~World(void);
		
        /** @brief Get a new name that isn't used by any Entity.
         * @param prefix A name prefix to use instead of "Unnamed_" if so desired.
         * @return A name that can be assigned without conflicts.
         * */
        std::string getUnusedName(const std::string &prefix = std::string("Unnamed_"));
		
        /** @brief Draw the game world. */
        void draw(void);
		
        /** @brief Update Entities in the game world and process
         * per-frame events. */
        void update(void);
		
        /// Does the smallest update possible. No actual game logic or
        /// anything. Just keep some streaming buffers full. Mainly
        /// for music.
        void minimumUpdate(void);

        /** @brief Take Entities that have the "dead" flag set to
         * true, deallocate them, and remove them from the list of
         * Entities. */
        void cleanUpDeadEntities(void);
		
        /** @brief Convert mouse coordinates on the screen to game
         * world coordinates.
         * @param x The x coordinate.
         * @param y The y coordinate.
         * @param camScale The camera location scale.
         * */
        Vector2D mouseToWorld(int x, int y, float camScale = 1) const;
		
        /** @brief Convert an SDL_Event's mouse position to world
         * coordinates.
         * @param event The SDL_Event (mouse motion, mouse down/up/whatever).
         * @param camScale The camera scale.
         * */
        Vector2D mouseEventToWorld(SDL_Event *event, float camScale = 1) const;
		
        /** @brief Convert mouse movement to world relative coordinates.
         * @param event The SDL_Event.
         * @return A Vector2D with the relative movement.
         * */
        Vector2D mouseRelToWorld(SDL_Event *event) const;
		
        /** @brief Push the OpenGL modelview matrix for a given camera
         * location scale.
         * @param cameraLocScale Camera location scale.
         * */
        void pushCamMatrix(float cameraLocScale = 1) const;
		
        /** @brief Push the OpenGL modelview matrix for a given layer.
         * @param layer The layer to scale the camera location for.
         * */
        void pushCamMatrixForLayer(int layer) const;
		
        /** @brief Pop the OpenGL matrix. */
        void popMatrix(void) const;
		
        /** @brief Get the QuadTree for a given layer.
         * @param layer The layer.
         * @return The QuadTree for the layer.
         * */
        QuadTree *getQuadTree(int layer);
		
        /** @brief Get the world lua_State.
         * @return The lua_State.
         * */
        lua_State *getLuaState(void);
		
        /** @brief Get the TextureManager for the whole game.
         * @return The TextureManager.
         * */
        TextureManager *getTextureManager(void) const;
		
        /** @brief Get the FontManager for the whole game.
         * @return The FontManager.
         * */
        FontManager *getFontManager(void) const;
		
        /** @brief Serialize all the objects in the world to an XML
         * tree.
         * @param exceptionList A list of names for Entities to exclude.
         * @return The tree of Entities that could be saved.
         * */
        TiXmlElement *toXml(const std::vector<std::string> &exceptionList = std::vector<std::string>());
		
        /** @brief Delete all Entity objects in the world. */
        void clearEntities(void);
		
        /** @brief Add all the Entities in an XML tree. */
        void addEntitiesFromXml(TiXmlElement *e);
		
        /** @brief Load and run the initial Lua code that sets up
         * global functions and classes. */
        void runInitLua(void);
		
        /** @brief Move new Entity objects from the newEntities list to
         * the main list of Entities. Entities are added to newEntities
         * initially instead of the main list so that iterating through
         * the Entity list isn't interrupted. */
        void addNewEntities(void);
		
        // This is editor-use only right now
        /** @brief Get the list of Spawners set up in the Lua code.
         * @return A vector<string> of names of spawners for the
         *   Spawner list in the editor. */
        std::vector<std::string> getSpawnList(void);
		
        /** @brief Add a spawn type to the spawner list.
         * @param spawnName The name that appears in the list of types
         *   that can be spawned. */
        void addSpawn(const std::string &spawnName);
		
        /** @brief Adds a chunk of scenery that can be added in the
         * editor.
         * @param sceneryName The name that appears in the editor.
         * @param textureName The filename of the texture to use.
         * @param area The area in the texture to use. */
        void addScenery(
            const std::string &sceneryName,
            const std::string &textureName,
            const BoundingBox &area);
		
        /** @brief Get the editor interface.
         * @return The EditorUI.
         * */
        EditorUI *getEditor(void) const;
		
        /** @brief Get the visible area on the screen for a given layer.
         * @param layer The layer number.
         * @param includeZoom If set to true, will include the zoom
         *   level in the calculation.
         * @return A BoundingBox with the visible area. */
        BoundingBox getVisibleArea(int layer = 3, bool includeZoom = false) const;
		
        /** @brief Get the state of a given joystick axis (with some
         * keyboard overrides for the first two axes).
         * @param axis The axis number (starting at 0).
         * @return -32768 to 32767, indicating the status of the axis.
         * */
        int getControlAxisState(int axis);
		
        /** @brief Get the state of a given joystick button (with some
         * keyboard overrides for the first three buttons).
         * @param button The button number (starting at 0).
         * @return true if the button is held, false if not. */
        bool getControlButtonState(int button);
		
        /** @brief Add an Entity to or remove an Entity from the forced
         * update list. Forced update Entities will be updated
         * regardless off proximity to the visual area.
         * @param ent The Entity.
         * @param forceUpdate true to add it to the list, false to
         *   remove. */
        void setForceUpdate(Entity *ent, bool forceUpdate);

        /** @brief Get a list of temporary TileEntities representing
         * the collidable tiles of a given area.
         * @param bb The area. All tiles touching this box will be added.
         * @param layer The layer to get tiles from.
         * @param tileEnts A list to fill with pointers to the
         *   TileEntity objects. */
        void getTileEntities(const BoundingBox &bb, int layer, std::vector<Entity*> &tileEnts) const;
	
        /** @brief Binary load tiles from a given filename.
         * @param fileName The tiles file to load from. */
        void loadTiles(const std::string &fileName);

        /// Binary save tiles to a given filename.
        void saveTiles(const std::string &fileName);
		
        /** @brief Load Entities from a given file (XML).
         * @param fileName The filename of the XML file. */
        void loadEntities(const std::string &fileName, bool forceAbsolutePath = false);
		
        /** @brief Save Entities to a given file (XML).
         * @note This is for saving the game during play. As such, it'll save in the save games directory.
         * @param fileName The filename to save as.
         * @param exceptionList A list of names of Entities to exclude from saving.
         * */
        void saveEntities(const std::string &fileName, const std::vector<std::string> &exceptionList = std::vector<std::string>());
		
        /** @brief Clear all tiles on all layers. */
        void clearAllTiles(void);
	
        /** A huge, ugly map of maps to keep track of what's bumped
         * into what this frame. Used to prevent collision handlers
         * from being called more than once for a given collision. */
        std::map<std::string, std::map<std::string, bool> > collisionsThisFrame;
		
        // These are manipulated a lot by callbacks, so they're not
        // private.
        /// Entities, indexed by their names.
        std::map<std::string, Entity *> entitiesByName;
		
        /// The big list of Entities.
        std::vector<Entity *> entities;
				
        /// New Entities waiting to be added to the game world.
        std::vector<Entity *> newEntities;

        /// Camera location in the world. (Centered on this point.)
        Vector2D cameraLoc;
		
        /// Size of the screen.
        Vector2D screenSize;
		
        /// Current zoom level (mostly useful for editor).
        float zoomLevel;

        /// Maximum number of layers.
        static const int maxLayers = 7;
		
        /// Names for each layer (HUD, background, etc).
        //static const char layerNames[maxLayers][256];

        /** @brief Get a tile value at a given point for a given layer.
         * @param x The x location of the tile (tile space).
         * @param y The y location of the tile (tile space).
         * @param layer The layer number.
         * @return The value of the tile at the given point.
         * */
        int getTile(int x, int y, int layer) const;
		
        /** @brief Set a tile value at a given point for a given layer.
         * @param x The x location of the tile (tile space).
         * @param y The y location of the tile (tile space).
         * @param layer The layer number.
         * @param tile The value of the tile to set.
         * */
        void setTile(int x, int y, int layer, int tile);

        /** @brief Get the size of tiles on a given layer.
         * @param layer The layer.
         * @return The width/height of tiles on that layer, in pixels.
         * */
        int getTileSize(int layer);

        /// Keeps track of sounds.
        SoundResourceManager soundResourceManager;

        /// Gravity scaling amount (default = 2).
        float gravity;

        /// Set the game name. You should only do this once.
        void setGameName(const std::string &gameName);

        /// Get the game name.
        std::string getGameName(void) const;
		
        /// Set the current save slot name. Can be done multiple times
        /// with appropriate clearing or reinitialization of data.
        void setSaveName(const std::string &saveName);

        /// Get the save slot name.
        std::string getSaveName(void) const;

        /// Get the actual name of a file after searching through the
        /// current save slot, the game directory, the common directory,
        /// and finally the current directory.

        /// Use the saveOnly parameter to specify that it should only
        /// return a name for something in a save slot directory, whether
        /// it really exists or not.
        std::string getRealFile(const std::string &inFileName, bool saveOnly = false) const;

        /// Set exclusive Entity updates for the frame. Everything else
        /// skips updating.
        void setExclusiveUpdatesThisFrame(const std::vector<std::string> &exclusiveNames);

        /// Check to see if an Entity is on the exclusive update list.
        bool checkExclusive(const Entity *ent) const;

        /// Change the names of dead Entities to something unused and that
        /// won't be used. Do this if you need to make sure stuff is out
        /// of the way before loading more Entities in, for example,
        /// without disrupting the order of Entity updates.
        void unnameDeadEntities(void);

        /// Get the renderer object attached to this.
        Renderer *getRenderer(void);
		
        void resetListColors(void);

        /// Get the name of the last map that Entities were loaded from.
        std::string getLastLoadedMapName(void);

        /// Reload display lists used for tiles.
        void reloadTileLists(void);

        void setSubtractiveFade(float r, float g, float b);
        void setAdditiveFade(float r, float g, float b);

        /// Clean up off-screen buffers.
        void killLayerBuffers(void);
    
        /// Turn lighting on or off.
        void setLighting(bool lighting);

        /// Get lighting on/off.
        bool getLighting(void);

        // Mouse stuff.
        bool getMouseButtonState(int button);
        Vector2D getMousePos(void);
        void setMousePos(const Vector2D &pos);
        void handleMouseEvent(SDL_Event *event);
        bool getIsInMouseEvent(void);

        // Layer manipulation junk.
        int getNumLayers(void);

        std::string getLayerName(int layerNum);
        void setLayerName(int layerNum, const std::string &layerName);

        bool getLayerVisible(int layerNum);
        void setLayerVisible(int layerNum, bool visible);

        /** @brief Get the camera location scale for a given layer.
         * @param layer The layer to get the scale for.
         * @return A scaling factor for the camera location to give
         *   faster or slower scrolling to different layers for a
         *   parallax effect.
         * */
        float getCamScaleForLayer(int layer) const;
        void setCamScaleForLayer(int layer, float cameraPosScale);

        void setupDefaultLayers(void);

        void swapLayers(int l1, int l2);

        void createLayer(void);
        void deleteLayer(int layerNum);

        int getLayerTileSize(int layerNum);
        std::string getLayerTileSet(int layerNum);

        void forceEditorUpdateLayerList(void);

        void setLayerFullbrightLights(int layerNum, bool enabled);
        void setLayerLighting(int layerNum, bool enabled);
        bool getLayerFullbrightLights(int layerNum);
        bool getLayerLighting(int layerNum);
        
        bool getLayerCombineWithPrevious(int layerNum);
        void setLayerCombineWithPrevious(int layerNum, bool enabled);

        bool getLayerTilesAsLightMap(int layerNum);
        void setLayerTilesAsLightMap(int layerNum, bool enabled);

    private:

        void fixupLayerNumbers(void);

        Renderer *renderer;

        unsigned int frameCounter;
	
        SDL_Joystick *joystick;
	
        friend void getJoystickEventsForUI(World *world, std::vector<WidgetEvent> &events);

        friend class EditorUI;
        friend class Entity;
	
        EditorUI *editor;
	
        enum spawnMapTypes {
            SPAWNTYPE_NONE = 0,
            SPAWNTYPE_SPAWNER = 1,
            SPAWNTYPE_SCENERY = 2,
        };
        std::map<std::string, spawnMapTypes> spawnMap;
		
        struct SceneryDefinition {
                std::string textureName;
                BoundingBox area;
        };
		
        std::map<std::string, SceneryDefinition> sceneryDefs;
		
        lua_State *L;
	
        unsigned int unnamedCounter;

        TextureManager *textureManager;
        FontManager *fontManager;
		
        std::map<Entity*, bool> forceUpdateList;
		
        std::string gameName;
        std::string saveName;
		
        std::map<std::string, bool> exclusiveUpdateList;
		
        std::string lastLoadedMapName;

        float subtractiveFade[3];
        float additiveFade[3];
        int fadeCountdown;

        void setDefaultAmbient(void);
        bool lightingEnabled;
        bool retroEffects;

        Vector2D lastMousePos;
        bool mouseButtonStates[5];

        bool handlingMouseEventNow;

        class Layer {

            public:

                Layer(void);
                ~Layer(void);

                //Vector3D ambientLight;
                std::string name;
                bool visible;
                float cameraPosScale;

                TilePane tilePane;
                QuadTree *quadTree;

                unsigned int drawBuffer;
                unsigned int lightBuffer;

                Vector3D ambientLight;

                bool lightingEnabled;
                bool fullbrightLightsEnabled;
                bool combineWithPreviousLayer;
                bool tilesAreLightMap;
        };

        std::vector<Layer*> layers;
};

extern World *activeWorld; // This is so that it'll be easy to re-enter from Lua. 

std::string fixDeprecatedPath(const std::string &str);

// When this is true, the camera will only scroll by full
// pixels. (Clamped at draw time.)
#define CAMERA_OFFSET_WHOLE_PIXELS

