/*
    Copyright (C) 2004  Erik Ogenvik

    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef MOTIONMANAGER_H
#define MOTIONMANAGER_H

#include "EmberOgrePrerequisites.h"
#include "framework/Singleton.h"

#include <OgreFrameListener.h>

namespace Ember {
class EmberEntity;
namespace OgreView {

struct IAnimated;
struct IMovable;

/**
 * @brief Responsible for making sure that movement and animation within the graphical system is managed and synchronized.
 *
 * The main task of the manager is to keep track of all movables and animatables, i.e. implementations of IMovable
 * and IAnimated, and make sure that these are asked to update their movement or animation when needed (usually each frame).
 */
class MotionManager : public Ogre::FrameListener, public Singleton<MotionManager> {
public:

	/**
	 * @brief Struct containing information about the MotionManager.
	 */
	struct MotionManagerInfo {
		size_t AnimatedEntities;
		size_t MovingEntities;
	};

	/**
	 * @brief Ctor
	 */
	MotionManager();

	/**
	 * @brief Dtor
	 */
	~MotionManager() override;

	/**
	 * @brief Adds a movable to the movement list.
	 * @param movable The movable instance to add to the movable list.
	 * That means that until removeMovable is called for the specific movable it will receive calls to updateMotion each frame.
	 * It's safe to add the same movable multiple times.
	 */
	void addMovable(IMovable* movable);

	/**
	 * @brief Removes a movable from the movement list.
	 * @param movable The movable instance to add to the movable list.
	 * It's safe to remove the same movable multiple times.
	 */
	void removeMovable(IMovable* movable);

	/**
	 * @brief Adds an animatable to the animation list.
	 * That means that until removeAnimated is called for the specific animatable it will receive calls to updateAnimation each frame.
	 * @param id The id of the animatable.
	 * @param animatable The animatable instance.
	 */
	void addAnimated(const std::string& id, IAnimated* animatable);

	/**
	 * @brief Removes an animatable from the animation list.
	 * @param The id of the animatable to remove.
	 * It's safe to remove an animateble which is not in the current list.
	 */
	void removeAnimated(const std::string& id);

	/**
	 * @see Ogre::FrameListener::frameStarted
	 */
	bool frameStarted(const Ogre::FrameEvent& event) override;

	/**
	 * @see Ogre::FrameListener::frameEnded
	 */
	bool frameEnded(const Ogre::FrameEvent& event) override;

	/**
	 * @brief Gets info about the MotionManager.
	 * @return Information about the motion manager
	 */
	const MotionManagerInfo& getInfo() const;

private:

	/**
	 * @brief A store of animatables, identified by a string.
	 */
	typedef std::map<std::string, IAnimated*> AnimatedStore;

	/**
	 * @brief A store of movables.
	 */
	typedef std::set<IMovable*> MovableStore;


	/**
	 * @brief Information about this manager.
	 */
	MotionManagerInfo mInfo;

	/**
	 * @brief Contains all of the entities that will be moved each frame.
	 */
	MovableStore mMotionSet;

	/**
	 * @brief Contains all of the entities that will be animated each frame.
	 */
	AnimatedStore mAnimatedEntities;


	/**
	 * @brief Will iterate over all registered movables and ask them to update their positions.
	 */
	void doMotionUpdate(Ogre::Real timeSlice);

	/**
	 * @brief Will iterate over all registered animatables and update those that are enabled.
	 */
	void doAnimationUpdate(Ogre::Real timeSlice);
};

inline const MotionManager::MotionManagerInfo& MotionManager::getInfo() const {
	return mInfo;
}


}
}


#endif // MOTIONMANAGER_H
