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

#include "../hacks/winhacks.h"

#include <assert.h>
#include <cstdlib>
#include <vector>
#include <iostream>
#include <sstream>
#include <cmath>
using namespace std;

#include "../console/console.h"
using namespace Console;

#include <sys/stat.h>

#include "../texturemanager/texturemanager.h"
#include "../texturemanager/texture.h"
#include "../malfont/fontmanager.h"
#include "../tinyxml/xmlstuff.h"
#include "../image/image.h"
#include "../malfont/malfont.h"
#include "../string/string.h"
#include "../filesystem/filesystem.h"

#include "world.h"
#include "entity.h"
#include "quadtree.h"
#include "terrainblock.h"
#include "polygon.h"
#include "editorui.h"
#include "tileent.h"
#include "renderer.h"

#include <GL/glew.h>
#include <GL/gl.h>

Entity::Entity(World *world) : spriteFrame(Vector2D(0, 0), Vector2D(16, 16)), loc(0,0), size(16, 16), vel(0, 0) {
	if(world) {
		this->world = world;
		name = world->getUnusedName();
		world->entitiesByName[name] = this;
		world->newEntities.push_back(this);
	} else {
		this->world = NULL;
		name = "Unnamed";
	}
	
	quadTreeNode = NULL;
	quadTreeListPosition = 0;
	dead = false;
	
	gravityInfluence = 0;
	bounciness = 0;
	blockable = false;
	blocker = false;
	onGround = false;
	age = 0;
	
	textureNum = -1;
	alpha = 1;
	layer = 0;
	ignoreBlockDown = false;
	spriteScale = 1;
	
	depth = 0;
	
	forceUpdate = false;
	flashing = false;
	
	labelFont = NULL;
	editorVisibleOnly = false;
	
	spriteAngle = 0;
	red = green = blue = 1;

	hFlip = false;
	vFlip = false;

    linkTarget = "";

    isLight = false;
    fullbright = false;
}

Entity::Entity(World *world, TiXmlElement *xml) : spriteFrame(Vector2D(0, 0), Vector2D(16, 16)), loc(0,0), size(16, 16), vel(0, 0) {
	this->world = world;
	
	// Defaults, in case some of these fail to load.
	gravityInfluence = 0;
	bounciness = 0.5;
	age = 0;
	alpha = 1;
	layer = 3;
	spriteScale = 1;
	depth = 0;
	labelFont = NULL;
	spriteAngle = 0;
	red = green = blue = 1;
	
	loc = getVectorFromXml(xml, "pos");
	size = getVectorFromXml(xml, "size");
	vel = getVectorFromXml(xml, "vel");
	
	if(xml->Attribute("name")) {
		setName(xml->Attribute("name"));
	} else {
		setName(world->getUnusedName());
	}
	//assert(!world->entitiesByName[name]); // If there's an entity by this name already, that would be bad.

	if(xml->Attribute("linkTarget")) {
        setLinkTarget(xml->Attribute("linkTarget"));
    }

	xml->QueryFloatAttribute("gravityInfluence", &gravityInfluence);
	xml->QueryIntAttribute("age", &age);
	xml->QueryFloatAttribute("bounciness", &bounciness);
	xml->QueryFloatAttribute("alpha", &alpha);
	xml->QueryFloatAttribute("spriteScale", &spriteScale);
	xml->QueryFloatAttribute("spriteAngle", &spriteAngle);
	xml->QueryFloatAttribute("depth", &depth);
	xml->QueryIntAttribute("layer", &layer);
	
	xml->QueryFloatAttribute("red", &red);
	xml->QueryFloatAttribute("green", &green);
	xml->QueryFloatAttribute("blue", &blue);
	
	// All these default to being false, and getBoolAttributeFromXml() will return false if it finds nothing.
	blockable = getBoolAttributeFromXml(xml, "blockable");
	blocker = getBoolAttributeFromXml(xml, "blocker");
	onGround = getBoolAttributeFromXml(xml, "onGround");
	ignoreBlockDown = getBoolAttributeFromXml(xml, "ignoreBlockDown");
	flashing = getBoolAttributeFromXml(xml, "flashing");

	isLight    = getBoolAttributeFromXml(xml, "isLight");
	fullbright = getBoolAttributeFromXml(xml, "fullbright");

	hFlip = getBoolAttributeFromXml(xml, "hFlip");
	vFlip = getBoolAttributeFromXml(xml, "vFlip");
	
	if(xml->Attribute("texture")) {
		
		// Ugly hack alert!
		string fixedName = world->getRealFile(string("textures/") + fixDeprecatedPath(xml->Attribute("texture")));
		
		textureNum = world->getTextureManager()->getTextureIndexByName(fixedName);
		
		if(textureNum != -1) {
			// FIXME: Leave linear filtering on by default?
			world->getTextureManager()->setTextureFilter(textureNum, false);
		}
		
	} else {
		textureNum = -1;
	}
	
	spriteFrame = getBoundingBoxFromXml(xml, "spriteFrame");
	spriteOffset = getVectorFromXml(xml, "spriteOffset");
	
	quadTreeNode = NULL;
	quadTreeListPosition = 0;
	
	world->newEntities.push_back(this);
	dead = false;
	
	if(xml->Attribute("labelFont")) {
		
		string labelFontName = xml->Attribute("labelFont");
		if(labelFontName.size()) {
			string fixedName = world->getRealFile(string("fonts/") + fixDeprecatedPath(labelFontName));
            labelFont = world->getFontManager()->getFont(fixedName);
		}
		
		if(xml->Attribute("labelText")) {
			labelText = xml->Attribute("labelText");
		}
	}
	
	editorVisibleOnly = getBoolAttributeFromXml(xml, "editorVisibleOnly");
	
	setForceUpdate(getBoolAttributeFromXml(xml, "forceUpdate"));
}

TiXmlElement *Entity::toXml(void) const {
	TiXmlElement *e = new TiXmlElement("Entity");
	
	setVectorInXml(e, "pos", getPos());
	setVectorInXml(e, "size", getSize());
	if(!vel.isZero()) setVectorInXml(e, "vel", getVel());
	
	e->SetAttribute("name", name.c_str());
    if(linkTarget.size()) e->SetAttribute("linkTarget", linkTarget.c_str());
	if(gravityInfluence != 0) e->SetDoubleAttribute("gravityInfluence", gravityInfluence);
	if(blockable) setBoolAttributeInXml(e, "blockable", blockable);
	if(blocker) setBoolAttributeInXml(e, "blocker", blocker);
	if(onGround) setBoolAttributeInXml(e, "onGround", onGround);
	if(ignoreBlockDown) setBoolAttributeInXml(e, "ignoreBlockDown", ignoreBlockDown);
	if(forceUpdate) setBoolAttributeInXml(e, "forceUpdate", getForceUpdate());
	if(flashing) setBoolAttributeInXml(e, "flashing", flashing);
	if(age != 0) e->SetAttribute("age", age);
	if(bounciness != 0.5) e->SetDoubleAttribute("bounciness", bounciness);
	if(alpha != 1) e->SetDoubleAttribute("alpha", alpha);
	if(spriteScale != 1) e->SetDoubleAttribute("spriteScale", spriteScale);
	if(spriteAngle != 0) e->SetDoubleAttribute("spriteAngle", spriteAngle);
	
	if(isLight)    setBoolAttributeInXml(e, "isLight",    isLight);
	if(fullbright) setBoolAttributeInXml(e, "fullbright", fullbright);

	if(red   != 1) e->SetDoubleAttribute("red", red);
	if(green != 1) e->SetDoubleAttribute("green", green);
	if(blue  != 1) e->SetDoubleAttribute("blue", blue);
	
	if(depth != 0) e->SetDoubleAttribute("depth", depth);
	
	if(layer != 3) e->SetAttribute("layer", layer);
	
	if(textureNum > -1) {
		e->SetAttribute("texture", getTexture().c_str());
	} 

	setBoundingBoxInXml(e, "spriteFrame", spriteFrame);
	if(!spriteOffset.isZero()) setVectorInXml(e, "spriteOffset", spriteOffset);
	
	if(labelText.size()) e->SetAttribute("labelText", labelText.c_str());
	if(labelFont != NULL) e->SetAttribute("labelFont", getLabelFont().c_str());

	if(editorVisibleOnly) setBoolAttributeInXml(e, "editorVisibleOnly", editorVisibleOnly);

	if(hFlip) setBoolAttributeInXml(e, "hFlip", hFlip);
	if(vFlip) setBoolAttributeInXml(e, "vFlip", vFlip);
	
	return e;
}

Entity::~Entity(void) {
	if(world) {
		world->entitiesByName.erase(name);
		
		// Note: We don't care WHICH layer QuadTree it's in, because
		// we can just remove it from the node.
		if(quadTreeNode) {
			quadTreeNode->remove(this);
		}
		
		if(getForceUpdate()) {
			// Remove from force update list.
			world->setForceUpdate(this, false);
		}
	}
}

void Entity::update(void) {
	
	age++;
	
	onGround = false;
	bool calledWorldCollide = false;

	// FIXME: We can probably determine the radius in a much simpler
	//   way by taking the shortest of the width/height and dividing by
	//   two. Do this if the current approach starts screwing up.

	// Here, we figure out the number of checks we'll need to move the
	// entity so we don't miss anything between where it is and where
	// it's going.
	//Vector2D maxIncrement = size;
	float minSide = size.x < size.y ? size.x : size.y;
	float xIncrements = vel.x / minSide;
	float yIncrements = vel.y / minSide;
	int numIncrements = (int((fabs(xIncrements) > fabs(yIncrements) ? fabs(xIncrements) : fabs(yIncrements)) + 0.5) + 1) * 2;
	
	// The N game moved the ninja in increments of the ninja's radius.
	// I'm having issues with things going through blocks when I do
	// otherwise, so I'll do that too...
	numIncrements *= 2;

	// Save the old location.
	Vector2D oldLoc = loc;

	// Save the velocity before we make any changes to it so we can
	// bounce properly later.
	Vector2D velPreAdjust = vel;

	// We add up all the adjustments to the velocity and position in
	// bounceTotal so that we know which way to bounce at the end, if
	// that's required.
	Vector2D bounceTotal(0, 0);

	if(blockable) {

		// Get all the entities that we might run into on the way.	
		vector<Entity *> touchingEnts;
		BoundingBox expandedBox = getBoundingBox().expand(vel);
		world->getQuadTree(layer)->getEntsTouchingBox(expandedBox, touchingEnts);

		// Get all the tiles that we might be touching, too.
		vector<Entity *> touchingTiles;
		world->getTileEntities(expandedBox, getLayer(), touchingTiles);

		touchingEnts.insert(touchingEnts.end(), touchingTiles.begin(), touchingTiles.end());
		
		bool noMoreIncrements = false;
		
		// Iterate through the increments.
		for(int j = 0; j < numIncrements && !noMoreIncrements; j++) {
			
			// Recalculate increments for adjusted velocity.
			/*
			xIncrements = vel.x / maxIncrement.x;
			yIncrements = vel.y / maxIncrement.y;
			numIncrements = int((fabs(xIncrements) > fabs(yIncrements) ? fabs(xIncrements) : fabs(yIncrements)) + 0.5) + 1;
			if(numIncrements <= j) break;
			*/
			
			loc += vel / float(numIncrements);
			
			// Go through all the touching entities and act accordingly.
			for(unsigned int i = 0; i < touchingEnts.size(); i++) {

				// We'll use this for blocker collisions.
				Polygon myCollisionArea = getCollisionArea();
		
				Entity *ent = touchingEnts[i];
				if(ent->getLayer() != getLayer()) continue; // Don't collide with objects on other layers.
				if(ent == this) continue; // Don't collide with self.

				// If this is a movement blocker, we need to act very specifically.
				if(ent->blocker) {
					bool doCollision = true;
					
					if(doCollision) {
						Vector2D shortestOverlap1;
						Vector2D shortestOverlap2;
						Polygon otherCollisionArea = ent->getCollisionArea();
						Vector2D shortestOverlap;
						TerrainBlock *tb = dynamic_cast<TerrainBlock*>(ent);
						
						bool collisionHappened = false;
						if(tb) {
							collisionHappened = 
								separatingAxisTest(myCollisionArea, otherCollisionArea, &shortestOverlap1) &&
								separatingAxisTest(otherCollisionArea, myCollisionArea, &shortestOverlap2);
							shortestOverlap = shortestOverlap1.magnitude() < shortestOverlap2.magnitude() ? (shortestOverlap1 * -1) : shortestOverlap2;
						} else {
							collisionHappened = getBoundingBox().overlap(ent->getBoundingBox(), &shortestOverlap);
							shortestOverlap.x = loc.x > ent->loc.x ? fabs(shortestOverlap.x) * -1 : fabs(shortestOverlap.x);
							shortestOverlap.y = loc.y > ent->loc.y ? fabs(shortestOverlap.y) * -1 : fabs(shortestOverlap.y);
						}
						
						if(collisionHappened) {

							bool skipAdjustment = false;
							if(tb) {
								
								if(!calledWorldCollide) {
									calledWorldCollide = true;
									collidedWithWorld();
								}
								
								if(tb->blocksDownOnly) {
									if(vel.y < 0 && !ignoreBlockDown) {
										// If a mere act of moving this upwards a bit will
										// make it no longer colliding, then do that.
										Polygon movedCollisionArea = myCollisionArea;
										movedCollisionArea.offset.y -= vel.y;
										
										if(!(separatingAxisTest(movedCollisionArea, otherCollisionArea, NULL) &&
											separatingAxisTest(otherCollisionArea, movedCollisionArea, NULL))) {
												
											loc.y += shortestOverlap.magnitude();
											vel.y = 0;
											bounceTotal -= shortestOverlap;
											
											// We're almost certainly on the ground after this happens.
											onGround = true;
										}
									
									}
									skipAdjustment = true;
								}
								
							}

							if(!skipAdjustment) {
								if(dynamic_cast<TileEntity*>(ent)) {
									
									if(!calledWorldCollide) {
										calledWorldCollide = true;
										collidedWithWorld();
									}
									
									// If this is a tile, we're going to test each axis individually.
                                    loc.y -= shortestOverlap.y;
                                    vel.y -= shortestOverlap.y;
                                    bounceTotal.y -= shortestOverlap.y;

									Vector2D newOverlap;
									if(getBoundingBox().overlap(ent->getBoundingBox(), &newOverlap)) {

                                        if(fabs(shortestOverlap.x) > 0.9) {
                                            loc.x -= shortestOverlap.x * 1.02;
                                            vel.x -= shortestOverlap.x * 1.02;
                                        }
                                        bounceTotal.x -= shortestOverlap.x;

									} else {

										// Compare the total change in
										// velocity (bounceTotal) to
										// the pre-adjustment velocity
										// to see if we've hit the
										// ground.
                                        if(gravityInfluence) {
                                            if(-bounceTotal.y * 2 / gravityInfluence < velPreAdjust.y && vel.y <= 0) {
                                                onGround = true;
                                            }
                                        }
									}
								} else {
									// Otherwise, just move it out.
									loc -= shortestOverlap;
									vel -= shortestOverlap;
									bounceTotal -= shortestOverlap;
								}
								//noMoreIncrements = true;
							}
							
							// Detect if we're standing on the ground or not
							// (so we know if the player can jump, etc).
							// TODO: Get the gravity vector from the world
							//   and do a dot product with that to get the
							//   groundiness.
							if(!dynamic_cast<TileEntity*>(ent)) {
								if(!shortestOverlap.isZero()) {
									float groundiness = shortestOverlap.normalize().y * -1;
									if(groundiness > 0.75) { // TODO: Make this value dynamic.
										onGround = true;
									}
								}
							}
							
							if(!world->collisionsThisFrame[getName()][ent->getName()]) {
								collided(ent);
								ent->collided(this);
								world->collisionsThisFrame[getName()][ent->getName()] = true;
								world->collisionsThisFrame[ent->getName()][getName()] = true;
							}
						}
					}
				}
					
				// Whatever it is, we hit it.
				// TODO: Call entity collision functions for each.
				//   Collision function should have access to all the info needed to... whatever.
				
				if(!ent->blocker) {
					if(!world->collisionsThisFrame[getName()][ent->getName()]) {
						collided(ent);
						ent->collided(this);
						world->collisionsThisFrame[getName()][ent->getName()] = true;
						world->collisionsThisFrame[ent->getName()][getName()] = true;
					}
				}
			}
		}
		
		// Clean up the tiles.
		for(unsigned int i = 0; i < touchingTiles.size(); i++) {
			delete touchingTiles[i];
		}
		
	} else {
		loc += vel;
	}	
	
	// TODO: Represent this as a vector?
	vel.y -= world->gravity * gravityInfluence;

	if(!bounceTotal.isZero()) {
		// This is a messy little chunk of code to get the proper bounce
		// angle. It projects the velocity onto the axis perpendicular
		// to the direction we've been shifted back due to collisions.
		Vector2D perpBounceAxis = getPerpendicularAxis(bounceTotal.normalize());
		Vector2D pointOnBounceAxis = perpBounceAxis * projectPointOnAxis(velPreAdjust, perpBounceAxis);
		// Then get the vector from the velocity vector's tip to the
		// projected point.
		Vector2D offsetToHalfPoint = velPreAdjust - pointOnBounceAxis;
		// Now subtract that twice from the velocity vector to get the
		// vector that we'll be moving in after an ideal bounce.
		Vector2D bounceVec = velPreAdjust - offsetToHalfPoint * 2;

		// Alpha-blend the normal resulting velocity (stopping) with the
		// bounce vector using the bounciness as the alpha value.
		// Or just the bounceVec scaled by bounciness if it's over 1
		// (ignoring normal velocity and gaining energy with each bounce
		// like flubber).
		if(bounciness > 1) {
			vel = bounceVec * bounciness;
		} else {
			vel = vel * (1.0 - bounciness) + bounceVec * bounciness;
		}
	}
	
	// Update our position in the quadtree.
	if(!(loc == oldLoc)) {
		world->getQuadTree(layer)->reinsert(this);
	}
	
	//assert(!isnan(vel.magnitude()));
}

void Entity::postUpdate(void) {
}

void Entity::draw(void) const {
	Renderer *renderer = world->getRenderer();
	
	// Set the color.
	renderer->setColor(red, green, blue, alpha);
	
	// Check to see if our layer is visible...
	EditorUI *editorUI = world->getEditor();
	if(editorUI) {
		
		if(!editorUI->junkInterface.isLayerVisible(layer)) {
			// Our layer isn't visible.
			return;
		}
		
		if(editorUI->getVisible() || editorUI->overrideEditorOnlyVisibility) {

			if(editorUI->junkInterface.getTintInactiveLayers() &&
               editorUI->junkInterface.getActiveLayer() != getLayer() &&
               !getFullbright()) {

				// We're on a different layer. Color it green.
				world->getRenderer()->setColor(0.0, green * 0.5, 0.0, alpha);
			}

		} else {
			

			if(editorVisibleOnly) return;
		}
	}
	
	// TODO: EDITORUI Make this conditional on being outside of the editor.
	if(editorVisibleOnly) {
		
		if(editorUI) {
		
			if(!editorUI->getVisible() && !editorUI->overrideEditorOnlyVisibility) {
				
				// Editor is hidden, and NOT overriding visibility.
				return;
			}
			
		} else {
			
			// No editor at all.
			return;
		}
	}
	
	// Bind a texture or disable textures.
	Texture *tex = world->getTextureManager()->getTextureByIndex(textureNum);
	
	if(tex) {
		tex->bindTexture();
	} else {
		renderer->unbindTexture();
	}
	
	// Figure out the UV coordinates and screen coordinates to draw.
	float spriteWidth = spriteFrame.lowerRight.x - spriteFrame.upperLeft.x;
	float spriteHeight = spriteFrame.upperLeft.y - spriteFrame.lowerRight.y;

	float leftu = 0;
	float rightu = 0;
	float topv = 0;
	float bottomv = 0;
	
	if(tex) {

        int origWidth = tex->getOrigWidth();
        int origHeight = tex->getOrigHeight();

#ifdef CAMERA_OFFSET_WHOLE_PIXELS
#define FUDGEAMOUNT 0
#else
#define FUDGEAMOUNT 0.001
#endif

        leftu   = spriteFrame.upperLeft.x  / float(origWidth)  + FUDGEAMOUNT;
        rightu  = spriteFrame.lowerRight.x / float(origWidth)  - FUDGEAMOUNT;

        bottomv = spriteFrame.upperLeft.y  / float(origHeight) - FUDGEAMOUNT;
        topv    = spriteFrame.lowerRight.y / float(origHeight) + FUDGEAMOUNT;
    
#define SWAPF(a, b) { float tmp = a; a = b; b = tmp; }

        if(hFlip) {
            SWAPF(leftu, rightu);
        }
        
        if(vFlip) {
            SWAPF(topv, bottomv);
        }
        
	}
	
	// Set the blend mode to add if this Entity is flashing.
	// This seems to be a popular visual feedback for a weapon hit.
	if(flashing) {
		renderer->setBlendMode(RENDERER_BLEND_ADD);
	}
	
	renderer->pushMatrix();

#ifdef CAMERA_OFFSET_WHOLE_PIXELS	
	renderer->translate(int(loc.x), int(loc.y), 0);
#else
	renderer->translate(loc.x, loc.y, 0);
#endif

	renderer->scale(spriteScale, spriteScale, 1);
	renderer->rotateZ(spriteAngle * (3.14159 / 180.0));
	
	Vector2D realOffset = spriteOffset;

	if(hFlip) {
		realOffset.x *= -1;
	}
	if(vFlip) {
		realOffset.y *= -1;
	}

#ifdef CAMERA_OFFSET_WHOLE_PIXELS
    realOffset.x = int(realOffset.x);
    realOffset.y = int(realOffset.y);
#endif

	Vertex3D points[] = {
		{spriteWidth / -2.0 - realOffset.x, spriteHeight /  2.0 - realOffset.y, 0, 1, 1, 1, 1, leftu,  topv},
		{spriteWidth /  2.0 - realOffset.x, spriteHeight /  2.0 - realOffset.y, 0, 1, 1, 1, 1, rightu, topv},
		{spriteWidth /  2.0 - realOffset.x, spriteHeight / -2.0 - realOffset.y, 0, 1, 1, 1, 1, rightu, bottomv},
		{spriteWidth / -2.0 - realOffset.x, spriteHeight / -2.0 - realOffset.y, 0, 1, 1, 1, 1, leftu,  bottomv}
	};
	
	renderer->drawQuads(points, 1);
		
	renderer->popMatrix();
	
	if(flashing) {
		renderer->setBlendMode(RENDERER_BLEND_MODULATE);
	}
	
	// Set color back - for both text and whatever else doesn't set the
	// color first.
	renderer->setColor(1, 1, 1, 1);
	
	// Do the label, too.
	if(labelFont) {
        glPushMatrix();
        glScalef(1, -1, 1);
        labelFont->drawText(loc.x - size.x / 2.0, -1 * (loc.y + size.y / 2.0) - labelFont->getOrigHeight(), labelText.c_str());
        glPopMatrix();
	}
}

BoundingBox Entity::getBoundingBox(void) const {
	return BoundingBox(
		loc.x - size.x / 2.0, loc.y + size.y / 2.0,
		loc.x + size.x / 2.0, loc.y - size.y / 2.0);
}

Polygon Entity::getCollisionArea(void) const {
	return getBoundingBox().getPoly();
}

std::string Entity::getName(void) const {
	return name;
}

void Entity::setName(const std::string &name) {
	assert(name.size());
	if(this->name.size()) {
		world->entitiesByName.erase(this->name);
	}
	
	this->name = name;
	
	// Is there something with this name already?
	if(world->entitiesByName[name]) {
		// Yes, but if it's Unnamed, it doesn't really matter.
		if(name.substr(0, 8) != string("Unnamed_")) {
			// We only need to worry about things that actually have
			// a name, and not an auto-assigned one. We should note to
			// designers that anything needing to be referred to by name
			// should damn well have something other than "Unnamed_".
			out("error") << "There's already something with the name \"" << name << "\". Can't set name." << endl;
		}
		this->name = world->getUnusedName();
	}
	
	world->entitiesByName[this->name] = this;
}

Vector2D Entity::getPos(void) const {
	return loc;
}

void Entity::setPos(const Vector2D &pos) {
	this->loc = pos;
	if(world) {
		world->getQuadTree(layer)->reinsert(this);
	}
}


Vector2D Entity::getSize(void) const {
	return size;
}

void Entity::setSize(const Vector2D &size) {
	this->size = size;
	if(world) {
		world->getQuadTree(layer)->reinsert(this);
	}
}

Vector2D Entity::getVel(void) const {
	return vel;
}

void Entity::setVel(const Vector2D &vel) {
	this->vel = vel;
}


void Entity::setSpriteFrame(const BoundingBox &box) {
	this->spriteFrame = box;
}

BoundingBox Entity::getSpriteFrame(void) const {
	return spriteFrame;
}

void Entity::collided(Entity *otherEnt) {
}

void Entity::collidedWithWorld(void) {
}

void Entity::updateEditor(EditorUI *ui) {

	ui->junkInterface.clearAttributes();
	
	ui->junkInterface.addAttribute("Entity attributes", INSPECTOR_DIVIDER);
	
	// Name
	ui->junkInterface.addAttribute("name", INSPECTOR_STRING);
	ui->junkInterface.setStringAttribute("name", name);
	
    // Link
    ui->junkInterface.addAttribute("linkTarget", INSPECTOR_STRING);
    ui->junkInterface.setStringAttribute("linkTarget", linkTarget);

	// Label
	ui->junkInterface.addAttribute("labelText", INSPECTOR_STRING);
	ui->junkInterface.setStringAttribute("labelText", labelText);
	ui->junkInterface.addAttribute("labelFont", INSPECTOR_STRING);
	//ui->junkInterface.setStringAttribute("labelFont", world->getFontManager()->getNameForFont(labelFont));
	ui->junkInterface.setStringAttribute("labelFont", getLabelFont());
	
	// Location
	ui->junkInterface.addAttribute("x", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("y", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("x", loc.x);
	ui->junkInterface.setDoubleAttribute("y", loc.y);
	
	// Velocity
	ui->junkInterface.addAttribute("dx", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("dy", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("dx", vel.x);
	ui->junkInterface.setDoubleAttribute("dy", vel.y);
	
	// Size
	ui->junkInterface.addAttribute("width", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("height", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("width", size.x);
	ui->junkInterface.setDoubleAttribute("height", size.y);
	
	// Physics properties
	ui->junkInterface.addAttribute("blocker", INSPECTOR_BOOL);
	ui->junkInterface.addAttribute("blockable", INSPECTOR_BOOL);
	ui->junkInterface.addAttribute("gravityInfluence", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("bounciness", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("ignoreBlockDown", INSPECTOR_BOOL);
	
	ui->junkInterface.setBoolAttribute("blocker", blocker);
	ui->junkInterface.setBoolAttribute("blockable", blockable);
	ui->junkInterface.setDoubleAttribute("gravityInfluence", gravityInfluence);
	ui->junkInterface.setDoubleAttribute("bounciness", bounciness);
	ui->junkInterface.setBoolAttribute("ignoreBlockDown", ignoreBlockDown);
	
	ui->junkInterface.addAttribute("forceUpdate", INSPECTOR_BOOL);
	ui->junkInterface.setBoolAttribute("forceUpdate", getForceUpdate());
	
	// Graphics properties
	ui->junkInterface.addAttribute("texture", INSPECTOR_STRING);
	ui->junkInterface.setStringAttribute("texture", getTexture());
	ui->junkInterface.addAttribute("alpha", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("alpha", alpha);
	ui->junkInterface.addAttribute("layer", INSPECTOR_INT);
	ui->junkInterface.setIntAttribute("layer", layer);
	ui->junkInterface.addAttribute("depth", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("depth", depth);
	ui->junkInterface.addAttribute("flashing", INSPECTOR_BOOL);
	ui->junkInterface.setBoolAttribute("flashing", flashing);
	ui->junkInterface.addAttribute("spriteX1", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("spriteY1", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("spriteX2", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("spriteY2", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("spriteX1", spriteFrame.upperLeft.x);
	ui->junkInterface.setDoubleAttribute("spriteY1", spriteFrame.upperLeft.y);
	ui->junkInterface.setDoubleAttribute("spriteX2", spriteFrame.lowerRight.x);
	ui->junkInterface.setDoubleAttribute("spriteY2", spriteFrame.lowerRight.y);
	ui->junkInterface.addAttribute("spriteScale", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("spriteScale", spriteScale);
	ui->junkInterface.addAttribute("spriteAngle", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("spriteAngle", spriteAngle);
	
	ui->junkInterface.addAttribute("spriteOffsetX", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("spriteOffsetY", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("spriteOffsetX", spriteOffset.x);
	ui->junkInterface.setDoubleAttribute("spriteOffsetY", spriteOffset.y);
	
	ui->junkInterface.addAttribute("red", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("green", INSPECTOR_DOUBLE);
	ui->junkInterface.addAttribute("blue", INSPECTOR_DOUBLE);
	ui->junkInterface.setDoubleAttribute("red", red);
	ui->junkInterface.setDoubleAttribute("green", green);
	ui->junkInterface.setDoubleAttribute("blue", blue);
	
	ui->junkInterface.addAttribute("editorVisibleOnly", INSPECTOR_BOOL);
	ui->junkInterface.setBoolAttribute("editorVisibleOnly", editorVisibleOnly);

	ui->junkInterface.addAttribute("hFlip", INSPECTOR_BOOL);
	ui->junkInterface.setBoolAttribute("hFlip", hFlip);

	ui->junkInterface.addAttribute("vFlip", INSPECTOR_BOOL);
	ui->junkInterface.setBoolAttribute("vFlip", vFlip);

	ui->junkInterface.addAttribute("isLight", INSPECTOR_BOOL);
	ui->junkInterface.setBoolAttribute("isLight", isLight);

	ui->junkInterface.addAttribute("fullbright", INSPECTOR_BOOL);
	ui->junkInterface.setBoolAttribute("fullbright", fullbright);

}

void Entity::updateFromEditor(EditorUI *ui) {

	// Name
	string newName = ui->junkInterface.getStringAttribute("name");
	if(newName != name) {
		setName(newName);
		ui->selectObject(name);
	}
   
    // Target
    setLinkTarget(ui->junkInterface.getStringAttribute("linkTarget"));

	// Location
	setPos(Vector2D(ui->junkInterface.getDoubleAttribute("x"), ui->junkInterface.getDoubleAttribute("y")));
	
	// Velocity
	vel = Vector2D(ui->junkInterface.getDoubleAttribute("dx"), ui->junkInterface.getDoubleAttribute("dy"));
	
	// Size
	setSize(Vector2D(ui->junkInterface.getDoubleAttribute("width"), ui->junkInterface.getDoubleAttribute("height")));

	// Physics properties
	blocker = ui->junkInterface.getBoolAttribute("blocker");
	blockable = ui->junkInterface.getBoolAttribute("blockable");
	gravityInfluence = ui->junkInterface.getDoubleAttribute("gravityInfluence");
	bounciness = ui->junkInterface.getDoubleAttribute("bounciness");
	ignoreBlockDown = ui->junkInterface.getBoolAttribute("ignoreBlockDown");
	setForceUpdate(ui->junkInterface.getBoolAttribute("forceUpdate"));
	
	// Label
	labelText = ui->junkInterface.getStringAttribute("labelText");
    string fontName = ui->junkInterface.getStringAttribute("labelFont");
    if(fontName.size()) {
        labelFont = world->getFontManager()->getFont(getWorld()->getRealFile(string("fonts/") + fixDeprecatedPath(fontName)));
    } else {
        labelFont = NULL;
    }
	
	// Set texture
	setTexture(ui->junkInterface.getStringAttribute("texture"));
	
	// Sprite frame 
	setSpriteFrame(BoundingBox(
		Vector2D(ui->junkInterface.getDoubleAttribute("spriteX1"), ui->junkInterface.getDoubleAttribute("spriteY1")),
		Vector2D(ui->junkInterface.getDoubleAttribute("spriteX2"), ui->junkInterface.getDoubleAttribute("spriteY2"))));
	
	setSpriteScale(ui->junkInterface.getDoubleAttribute("spriteScale"));
	
	// Sprite offset
	setSpriteOffset(Vector2D(ui->junkInterface.getDoubleAttribute("spriteOffsetX"), ui->junkInterface.getDoubleAttribute("spriteOffsetY")));
	
	// Sprite Angle
	setSpriteAngle(ui->junkInterface.getDoubleAttribute("spriteAngle"));
	
	// Alpha
	setAlpha(ui->junkInterface.getDoubleAttribute("alpha"));
	
	// Flashing
	flashing = ui->junkInterface.getBoolAttribute("flashing");
	
	// Layer
	setLayer(ui->junkInterface.getIntAttribute("layer"));
	
	// Depth
	setDepth(ui->junkInterface.getDoubleAttribute("depth"));
	
	// Editor visibility.
	editorVisibleOnly = ui->junkInterface.getBoolAttribute("editorVisibleOnly");
	
	// Color
	setColor(
		ui->junkInterface.getDoubleAttribute("red"),
		ui->junkInterface.getDoubleAttribute("green"),
		ui->junkInterface.getDoubleAttribute("blue"));

	hFlip = ui->junkInterface.getBoolAttribute("hFlip");
	vFlip = ui->junkInterface.getBoolAttribute("vFlip");

    setFullbright(ui->junkInterface.getBoolAttribute("fullbright"));
    setIsLight(ui->junkInterface.getBoolAttribute("isLight"));
}
	
World *Entity::getWorld(void) const {
	return world;
}

void Entity::setTexture(const std::string &fileName) {
	
	if(fileName.size()) {
		
		string fixedName = world->getRealFile(string("textures/") + fixDeprecatedPath(fileName));

		if(fixedName == "") {
			out("error") << "\"" << fileName <<  "\" does not exist." << endl;
			textureNum = -1;
			return;
		}
		
		textureNum = world->getTextureManager()->getTextureIndexByName(fixedName);
		
		// TODO: Leave as linear by default?
		if(textureNum >= 0) {
			world->getTextureManager()->setTextureFilter(textureNum, false);
		}
	
	} else {
	
		textureNum = -1;
		
	}
}

std::string Entity::getTexture(void) const {
	
	string name = world->getTextureManager()->getTextureNameByIndex(textureNum);
	
	// TODO: Fix handling of directory hierarchies.
	
	//if(strStartsWith(name
	//return name;
	
	return FileSystem::getBaseName(name);
}

float Entity::getAlpha(void) const {
	return alpha;
}

void Entity::setAlpha(float alpha) {
	this->alpha = alpha;
}

int Entity::getLayer(void) const {
	return layer;
}

void Entity::setLayer(int layer, bool keepPositionForCamera) {

    if(world) {

        if(layer >= 0 && layer < world->getNumLayers()) {
		
            if(keepPositionForCamera) {

                // Keep the position on the screen when switching layers.
                float newScale = world->getCamScaleForLayer(layer);
                float oldScale = world->getCamScaleForLayer(getLayer());
		
                Vector2D oldCamOffset = getPos() - (world->cameraLoc * oldScale);
                Vector2D newPos = (world->cameraLoc * newScale) + oldCamOffset;
		
                setPos(newPos);
            }

            this->layer = layer;
		
            world->getQuadTree(layer)->reinsert(this);
        }

    } else {

        this->layer = layer;

    }
}

bool Entity::getIgnoreBlockDown(void) const {
	return ignoreBlockDown;
}

void Entity::setIgnoreBlockDown(bool ignoreBlockDown) {
	this->ignoreBlockDown = ignoreBlockDown;
}

float Entity::getSpriteScale(void) const {
	return spriteScale;
}

void Entity::setSpriteScale(float spriteScale) {
	this->spriteScale = spriteScale;
}

Vector2D Entity::getSpriteOffset(void) const {
	return spriteOffset;
}

void Entity::setSpriteOffset(const Vector2D &spriteOffset) {
	this->spriteOffset = spriteOffset;
}

int Entity::getAge(void) const {
	return age;
}

void Entity::setSpriteBottomCenter(const Vector2D &bottomCenter) {
	
	Vector2D originalCenter =
		(spriteFrame.upperLeft + spriteFrame.lowerRight) / 2.0;
	
	float halfHeight =
		//(spriteFrame.upperLeft.y - spriteFrame.lowerRight.y) / 2.0;
		(size.y / spriteScale) / 2.0;

	Vector2D newCenter =
		//Vector2D(bottomCenter.x, tex->getOrigHeight() - bottomCenter.y) +
		//Vector2D(0, halfHeight);
		Vector2D(bottomCenter.x, bottomCenter.y - halfHeight);

	Vector2D finalOffset = newCenter - originalCenter;

	// Why do I have to do this again?
	finalOffset.y *= -1;

	setSpriteOffset(finalOffset);

}

void Entity::setDepth(float depth) {
	if(depth < -1) depth = -1;
	if(depth > 1) depth = 1;
	this->depth = depth;
}

float Entity::getDepth(void) const {
	return depth;
}


void Entity::setLabelFont(const std::string &fontName) {
	string fixedName = world->getRealFile(string("fonts/") + fixDeprecatedPath(fontName));
	labelFont = world->getFontManager()->getFont(fixedName);
}

std::string Entity::getLabelFont(void) const {
	
	string name = world->getFontManager()->getNameForFont(labelFont);
	
	// TODO: There should be a more consistent way to handle this that
	//   makes directory hierarchies in the textures dir work.
	
	/*if(strStartsWith(string("fonts/"), name)) {
		name = name.c_str() + 6;
	}*/
	
	//return name;

	return FileSystem::getBaseName(name);	
}

void Entity::setLabel(const std::string &labelText) {
	this->labelText = labelText;
}

std::string Entity::getLabel(void) const {
	return labelText;
}

void Entity::setForceUpdate(bool forceUpdate) {
	this->forceUpdate = forceUpdate;
	
	// Add to the world force update list.
	world->setForceUpdate(this, forceUpdate);
}

bool Entity::getForceUpdate(void) const {
	return forceUpdate;
}

void Entity::setSpriteAngle(float angle) {
	spriteAngle = angle;
}

float Entity::getSpriteAngle(void) {
	return spriteAngle;
}

void Entity::setColor(float red, float green, float blue) {
	this->red = red;
	this->green = green;
	this->blue = blue;
}

void Entity::getColor(float *red, float *green, float *blue) {
	*red = this->red;
	*green = this->green;
	*blue = this->blue;
}


bool compareDepth(const Entity *ent1, const Entity *ent2) {
	return ent1->getDepth() < ent2->getDepth();
}

void Entity::die(void) {
	dead = true;
}


bool Entity::getIsPermanent(void) {
	return false;
}

bool Entity::getDead() {
	return dead;
}

bool Entity::getFlipHorizontal(void) {
	return hFlip;
}

void Entity::setFlipHorizontal(bool flip) {
	hFlip = flip;
}

bool Entity::getFlipVertical(void) {
	return vFlip;
}

void Entity::setFlipVertical(bool flip) {
	vFlip = flip;
}

void Entity::setLinkTarget(const std::string &target) {
    linkTarget = target;
}

void Entity::addLinkTarget(const std::string &target) {
    
    std::map<std::string, bool> mapTargets;
    std::vector<std::string> vecTargets;

    stringTokenize(linkTarget, ", ", vecTargets);

    mapTargets[target] = true;
    for(unsigned int i = 0; i < vecTargets.size(); i++) {
        mapTargets[vecTargets[i]] = true;
    }

    ostringstream outTarget;
    bool first = true;
    for(std::map<std::string, bool>::iterator i = mapTargets.begin(); i != mapTargets.end(); i++) {
        if(!first) {
            outTarget << ", ";
        } else {
            first = false;
        }
        outTarget << (*i).first;
    }

    linkTarget = outTarget.str();
}

std::string Entity::getLinkTarget(void) {

    std::vector<std::string> vecTargets;

    stringTokenize(linkTarget, ", ", vecTargets);

    return vecTargets.size() ? vecTargets[0] : "";
}

void Entity::getAllLinkTargets(std::vector<std::string> &vecTargets) {

    stringTokenize(linkTarget, ", ", vecTargets);
}

bool Entity::getIsLight(void) {
    return isLight;
}

void Entity::setIsLight(bool isLight) {
    this->isLight = isLight;
}

void Entity::setFullbright(bool fullbright) {
    this->fullbright = fullbright;
}

bool Entity::getFullbright(void) const {
    return fullbright;
}
