#pragma once

#include "../Animator.h"
#include <Utils/Point2D.h>
#include "../Assets/Object.h"
#include "Map.h"
#include "OwnerType.h"
#include <Utils/SortedCollection.h>

namespace mechanics
{
	class Object {
	public:
		Object(Animator &given_animator, OwnerType::Type owner) : animator(given_animator), ownerType(owner) {}

		virtual void removeFromMap()
		{
			wrapper->Remove();
			wrapper = NULL;
		}

		virtual void locateTo(const gge::utils::Point &given_location)
		{
			location = given_location;
			wrapper = &Map::current().getObjects().Add(this, this->location.y);
		}

		virtual void setLocation(const gge::utils::Point &given_location)
		{
			location = given_location;
			wrapper->Reorder(given_location.y);
		}

		gge::utils::Point currentLocation() const 
		{
			return location;
		}

		OwnerType::Type getOwnerType() const
		{
			return ownerType;
		}

		void setNonPassable(const assets::Object &provider)
		{
			for(auto it = provider.nonPassablePoints.begin(); it != provider.nonPassablePoints.end(); ++it)
			{
				if(gge::utils::Bounds(0,0, Map::current().getPointInfoMap().GetWidth(),Map::current().getPointInfoMap().GetHeight()).isInside(*it+location))
					Map::current().getPointInfoMap()(*it + location).walkable = false;
			}

			for(int y=0;y<provider.size.Height;y++) {
				for(int x=0;x<provider.size.Width;x++) {
					if(gge::utils::Bounds(0,0, Map::current().getPointInfoMap().GetWidth(),Map::current().getPointInfoMap().GetHeight()).isInside(gge::utils::Point(x,y) + location))
						Map::current().getPointInfoMap()(gge::utils::Point(x,y) + location).object=this;
				}
			}
		}

		void setPassable(const assets::Object &provider)
		{
			for(auto it = provider.nonPassablePoints.begin(); it != provider.nonPassablePoints.end(); ++it)
			{
				if(gge::utils::Bounds(0,0, Map::current().getPointInfoMap().GetWidth(),Map::current().getPointInfoMap().GetHeight()).isInside(*it+location))
					Map::current().getPointInfoMap()(*it + location).walkable = true;
			}

			for(int y=0;y<provider.size.Height;y++) {
				for(int x=0;x<provider.size.Width;x++) {
					if(gge::utils::Bounds(0,0, Map::current().getPointInfoMap().GetWidth(),Map::current().getPointInfoMap().GetHeight()).isInside(gge::utils::Point(x,y) + location))
						Map::current().getPointInfoMap()(gge::utils::Point(x,y) + location).object=NULL;
				}
			}
		}

		virtual void setAnimation(Direction direction, std::string animationID, bool isLooping = true, float speed = 1) {
			animator.setAnimation(direction, animationID, isLooping, speed);
		}

		virtual void setDirection(Direction direction, std::string animationID) {
			animator.setDirection(direction, animationID);
		}

		Animator &animator;

	protected:
		gge::utils::SortedCollection<Object>::Wrapper *wrapper;
		gge::utils::Point location;
		OwnerType::Type ownerType;
	};

	class Obstacle: public Object
	{
	public:
		Obstacle(const assets::Obstacle &given_provider, Animator &given_animator):
			mProvider(given_provider),
				Object(given_animator, OwnerType::neutral)
		{
			given_animator.setAnimation(North, "Stand");
		}

		virtual void locateTo(const gge::utils::Point &given_location)
		{
			Object::locateTo(given_location);
			Object::setNonPassable(mProvider);
		}

		const assets::Obstacle & provider() const { 
			return mProvider;
		}

		virtual void setLocation(const gge::utils::Point &given_location) {
			setPassable(mProvider);
			Object::setLocation(given_location);
			setNonPassable(mProvider);
		}

		virtual void removeFromMap() {
			Object::removeFromMap();
			setPassable(mProvider);
		}

	private:
		const assets::Obstacle &mProvider;
	};
}