// ---------------------------------------------------------------------------
// 
//   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 <string>

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

#include "../math/vector2d.h"
#include "boundingbox.h"
#include "polygon.h"
class QuadTree;
class World;
class EditorUI;
class MalFont;

/** @brief An interactible and/or displayable game object. */
class Entity {
public:
	
    /** @brief Constructor.
     * @param world World that the new Entity will be added to.
     * */
    Entity(World *world);
		
    /** @brief XML-loading constructor.
     * @param world World that the new Entity will be added to.
     * @param xml The TiXmlElement that represents this Entity.
     * */
    Entity(World *world, TiXmlElement *xml);
		
    virtual ~Entity(void);
		
    /** @brief Per-frame functionality for all Entities.
     * @note This is will not be called if the camera is very far
     *   away from the entity (two or more screens worth).
     * */
    virtual void update(void);
		
    /** @brief Per-frame update.
     * Like update(), but ALL postUpdate()s are called AFTER all
     * update()s. Use for post-physics-stage responses.
     * */
    virtual void postUpdate(void);
		
    /** @brief Draw the Entity on the screen.
     * Generally this will work with what's given (sprite frame,
     * scale, position, etc) and doesn't need to be overridden.
     * */
    virtual void draw(void) const;
		
    /** @brief Get this Entity's name.
     * @return The Entity's name.
     * */
    std::string getName(void) const;
		
    /** @brief Set this Entity's name.
     * @note In the name of keeping things consistent between loads
     *   and making sure the designers are conscious of this, the
     *   program will abort with an error if there's already
     *   something with the specified name!
     * @param name The new name.
     * */
    virtual void setName(const std::string &name);
		
    /** @brief Get the Entity's BoundingBox.
     * Some Entities (TerrainBlock, for example) have oddly defined
     * bounding boxes.
     * @return The BoundingBox.
     * */
    virtual BoundingBox getBoundingBox(void) const;
		
    /** @brief Get the actual collidable area for this Entity.
     * For most Entities it will just be a box. For others, like
     * the TerrainBlock, it will be a more complex polygon.
     * @return A Polygon of the area.
     * */
    virtual Polygon getCollisionArea(void) const;
		
    /** @brief Marks the Entity for cleanup at the end of the
     * frame. */
    virtual void die(void);
		
    /** @brief Blocker flag.
     * If true, then Entities with the blockable flag set to true
     * cannot move through this Entity's collision area and will be
     * pushed out of it during the collision testing.
     * */
    bool blocker;
		
    /** @brief Blockable flag.
     * If true, then this Entity will not be able to move through
     * the collision areas of Entities with the blocker flag set.
     * */
    bool blockable;
		
    /** @brief Amount of gravity effect on this Entity.
     * Lower values will make this Entity accelerate slower due to
     * gravity. 1 is full gravity acceleration. Values may be
     * greater for more or even negative for inverted gravity.
     * @note Gravity acceleration is, right now, 2 pixels per frame.
     * */
    float gravityInfluence;
		
    /** @brief Bounciness.
     * Amount of energy preserved in a bounce. 1 = no energy lost,
     * 0 = not bouncy at all. More than 1 will cause it to gain
     * energy with each collision (think: Flubber).
     * */
    float bounciness;
		
    /** @brief Whether or not the Entity is on the ground.
     * Used to restrict when a character can jump, etc.
     * */
    bool onGround;
		
    /** @brief If this is true, the Entity will be rendered as
     * white, but using the same alpha.
     * */
    bool flashing;
		
    /** @brief If this is set to true, this Entity will be invisible
     * unless the editor is open.
     * */
    bool editorVisibleOnly;
		
    /** @brief Get the Entity's position.
     * @return The location of the Entity.
     * */
    Vector2D getPos(void) const;
		
    /** @brief Set the Entity's position.
     * @note When overriding this, be sure to call Entity::setPos,
     *   because it handles reinsertion into the QuadTree. If you
     *   opt not to do this, be sure to manually reinsert it.
     * @param pos The new position.
     * */
    virtual void setPos(const Vector2D &pos);
		
    /** @brief Get the size of the Entity.
     * For most Entities, this will just be the size of the
     * bounding box.
     * @return The size of the Entity's bounding box.
     * */
    Vector2D getSize(void) const;
		
    /** @brief Set the size of the Entity.
     * For most Entities (the default behavior) this defines the
     * bounding box of the Entity and collision area.
     * @note Also affects clickable area in the editor. Be careful.
     * @param size The new size.
     * */
    virtual void setSize(const Vector2D &size);
		
    /** @brief Get the current velocity.
     * Current velocity is the number of pixels it will move each
     * frame on each axis as a 2D vector.
     * @return The velocity.
     * */
    Vector2D getVel(void) const;
		
    /** @brief Set the current velocity.
     * @note FIXME: This isn't called when the velocity is updated
     *   in Entity::update().
     * @param vel The new velocity.
     * */
    virtual void setVel(const Vector2D &vel);
		
    /** @brief Set the area of the texture to use to draw this.
     * @param box The BoundingBox of the area to draw. Coordinates
     *   are in pixels on the texture, with 0,0 at the top left.
     * */
    virtual void setSpriteFrame(const BoundingBox &box);
		
    /** @brief Get the area of the texture to use to draw this.
     * @return The BoundingBox of the area to draw.
     * */
    BoundingBox getSpriteFrame(void) const;
		
    /** @brief Called every time this Entity overlaps another.
     * Will not be called multiple times in a frame for the same
     * pair of Entities.
     * @param otherEnt The other Entity.
     * */
    virtual void collided(Entity *otherEnt);
		
    /** @brief Called every time this Entity bumps into the world
     * (tiles, collision areas, etc).
     * */
    virtual void collidedWithWorld(void);
		
    /** @brief Update the inspector in the EditorUI with values
     * from this Entity.
     * @note Child classes that override this should call this
     *   first, then add all their own values.
     * @param ui The UI to update.
     * */
    virtual void updateEditor(EditorUI *ui);
		
    /** @brief Update this Entity with values from the inspector.
     * @note Child classes should call this in overridden versions
     *   of this.
     * @param ui The UI to update from.
     * */
    virtual void updateFromEditor(EditorUI *ui);

    /** @brief Get the world that this Entity belongs to.
     * @return The World.
     * */
    World *getWorld(void) const;
		
    /** @brief Serialize this Entity to XML.
     * @note Child classes that override this should call this,
     *   then add attributes to the returned TiXmlElement.
     * @return A TiXmlElement describing the object.
     * */
    virtual TiXmlElement *toXml(void) const;
		
    /** @brief Set the name of the texture that this Entity will
     * use when drawing to the screen.
     * @param fileName The file name of the texture. Must be a
     *   32-bit PNG with alpha and height and width equal to powers
     *   of two.
     * */
    void setTexture(const std::string &fileName);
		
    /** @brief Get the name of the texture used to draw this
     * Entity.
     * */
    std::string getTexture(void) const;
		
    /** @brief Get the alpha value.
     * @return The alpha value used when drawing this.
     * */
    float getAlpha(void) const;
		
    /** @brief Set the alpha value.
     * @note If this isn't simply one or zero, be prepared to deal
     *   with drawing order issues.
     * @param alpha The new alpha value.
     * */
    void setAlpha(float alpha);
		
    /** @brief Get the layer that this Entiy is on.
     * @return The layer.
     * */
    int getLayer(void) const;
		
    /** @brief Set the layer that this Entity is on.
     * @note This function will automatically force the layer to be
     *   one of the existing layers if it isn't.
     * @note Things don't collide with things on other layers.
     * @param layer The layer.
     * */
    void setLayer(int layer, bool keepPositionForCamera = true);
		
    /** @brief Get the ignoreBlockDown flag.
     * @return True if this Entity is ignoring downward-only
     *   blockers.
     * */
    bool getIgnoreBlockDown(void) const;
		
    /** @brief Set the ignoreBlockDown flag.
     * @param ignoreBlockDown Set this to true if you want the
     *   Entity to ignore collisions with downward-blocking-only
     *   TerrainBlocks. Use to drop down from platforms and such.
     * */
    void setIgnoreBlockDown(bool ignoreBlockDown);
		
    /** @brief Get the scale that this Entity is drawn at.
     * @return The scale of the drawn sprite.
     * */
    float getSpriteScale(void) const;
		
    /** @brief Set the scale that this Entity is drawn at.
     * @note Doesn't affect anything else, including clickable area
     *   in the editor!
     * @param spriteScale The scale (1 = normal).
     * */
    void setSpriteScale(float spriteScale);
		
    /** @brief Set the offset from the center of the sprite to the
     * actual center of the Entity.
     * @param spriteOffset The new offset.
     * */
    void setSpriteOffset(const Vector2D &spriteOffset);
		
    /** @brief Set the bottom-center of the sprite in the texture
     * and calculate the offset from that. This is just a
     * convenience function to make it easier to set the offset.
     * @note You need the texture to be set and loaded for this to
     *   do anything and everything will get screwed up if you
     *   switch to a differently sized texture.
     * @param bottomCenter The bottom center pixel of the sprite in
     *   the texture, in pixel coordinates.
     * */
    void setSpriteBottomCenter(const Vector2D &bottomCenter);
		
    /** @brief Get the offset from the center of the sprite to the
     * actual center of the Entity.
     * @return The offset.
     * */
    Vector2D getSpriteOffset(void) const;

    /** @brief Get the number of frames that this Entity has been
     * in existence.
     * @note Actually, only returns the number of frames in which
     *   its update() has been called.
     * */
    int getAge(void) const;
		
    /** @brief Set the Z depth value to draw this with.
     * @note The depth buffer is cleared between drawing layers, so
     *   things on lower layers are still ALWAYS going to be behind
     *   things on higher layers.
     * @param depth The new Z value. The only good values here are
     *   -1 to 1 because of the way the depth buffer is set up.
     *   Other values will be clamped to that.
     * */
    void setDepth(float depth);
		
    /** @brief Get the depth value.
     * @return The Z depth value that this Entity is drawn at.
     * */
    float getDepth(void) const;
		
    /** @brief Set the bitmap font (sfont-style) to use for
     * displaying a label on this Entity.
     * @param fontName The filename.
     * */
    void setLabelFont(const std::string &fontName);
		
    /** @brief Get the font file name for labels on this.
     * @return The filename.
     * */
    std::string getLabelFont(void) const;
		
    /** @brief Set the text to be displayed as a label on this.
     * @param labelText The new text.
     * */
    void setLabel(const std::string &labelText);
		
    /** @brief Get the label text.
     * @return The text.
     * */
    std::string getLabel(void) const;
		
    /** @brief Set the force update flag for this Entity.
     * Entities with this flag set to true will update even when
     * not visible or near the screen.
     * @param forceUpdate The flag value.
     * */
    void setForceUpdate(bool forceUpdate);
		
    /** @brief Get the force update flag.
     * @return The flag value.
     * */
    bool getForceUpdate(void) const;
		
    /** @brief Set the rotation for the sprite when drawn.
     * This is just a visual thing.
     * @param angle The rotation angle, in radians.
     * */
    void setSpriteAngle(float angle);
		
    /** @brief Get the sprite rotation.
     * @return The sprite rotation angle, in radians.
     * */
    float getSpriteAngle(void);
		
    /** @brief Set the color for the sprite to be multiplied with.
     * Defaults to white (1, 1, 1). Weird effects when combined 
     * with flashing.
     * @param red The red value (0-1).
     * @param green The green value (0-1).
     * @param blue The blue value (0-1).
     * */
    void setColor(float red, float green, float blue);
		
    /** @brief Get the color for the sprite to be multiplied with.
     * @param red Pointer to store the red value.
     * @param green Pointer to store the green value.
     * @param blue Pointer to store the blue value.
     * */
    void getColor(float *red, float *green, float *blue);
		
    /** @brief Get permanence status of the Entity. Permanent
     * Entities will not be deleted during regular clears. Permanent
     * Entity subclasses should override this function.
     * @return true if this should never be deleted.
     * */
    virtual bool getIsPermanent(void);
		
    /** @brief Get Entity deadness.
     * @return true if dead and waiting for cleanup.
     * */
    bool getDead(void);
		
    /// Get flipped horizontally flag.
    bool getFlipHorizontal(void);

    /// Flip the drawn sprite horizontally.
    void setFlipHorizontal(bool flip);

    /// Get flipped vertically flag.
    bool getFlipVertical(void);

    /// Flip the drawn sprite vertically.
    void setFlipVertical(bool flip);

    /// Set linked target.
    void setLinkTarget(const std::string &target);

    /// Add a link target to the list of multiple targets.
    void addLinkTarget(const std::string &target);

    /// Get linked target.
    std::string getLinkTarget(void);

    /// Get multiple linked targets.
    void getAllLinkTargets(std::vector<std::string> &vecTargets);

    /// Get whether or not this is for lighting only.
    bool getIsLight(void);

    /// Set lighting only. If true, will only be drawn in the
    /// light pass, lighting up a given area. (Or darkening it, I
    /// guess.)
    void setIsLight(bool isLight);

    /// Set fullbright (ignores lighting).
    void setFullbright(bool fullbright);

    /// Get fullbright.
    bool getFullbright(void) const;

protected:
		
    /** @brief Entity deadness.
     * If this is true, then the Entity will be purged from the list
     * of active Entities at the end of the frame and deallocated.
     * For the remainder of the current frame, it will act normally,
     * however.
     * */
    bool dead;
		
private:
		
    World *world;
		
    QuadTree *quadTreeNode;
    int quadTreeListPosition;
    friend class QuadTree;
		
    int textureNum;
    BoundingBox spriteFrame;
    Vector2D spriteOffset;
    float alpha;
    int layer;
    bool ignoreBlockDown;
    float spriteScale;
    float spriteAngle;
    float depth;
		
    std::string name;
    Vector2D loc;
    Vector2D size;
    Vector2D vel;
    int age;
		
    std::string labelText;
    MalFont *labelFont;
		
    /** @brief Force updates even when off-screen.
     * @note Currently broken. (FIXME)
     * @note This needs to be kept up-to-date with the World's list of Entities that get forced updates.
     * */
    bool forceUpdate;
		
    float red;
    float green;
    float blue;
		
    bool hFlip;
    bool vFlip;
		
    std::string linkTarget;

    bool isLight;
    bool fullbright;
            
    friend class EditorUI;
    friend class World;

};

// TODO: Document this.
bool compareDepth(const Entity *ent1, const Entity *ent2);

