//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
//**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************//
#pragma once

#include "BsCorePrerequisites.h"
#include "BsEventQuery.h"
#include "BsModule.h"

namespace BansheeEngine
{
	/** @addtogroup RenderAPI-Internal
	 *  @{
	 */

	/**
	 * Handles creation and destruction of GPU queries.
	 * 			
	 * @note	Core thread only.
	 */
	class BS_CORE_EXPORT QueryManager : public Module<QueryManager>
	{
	public:
		QueryManager();
		~QueryManager();

		/** Creates a new event query that allows you to get notified when GPU starts executing the query. */
		virtual SPtr<EventQuery> createEventQuery() const = 0;

		/**
		 * Creates a new timer query that allows you to get notified of how much time has passed between query start and end.
		 */
		virtual SPtr<TimerQuery> createTimerQuery() const = 0;

		/**
		 * Creates a new occlusion query that allows you to know how many fragments were rendered between query start and 
		 * end.
		 *
		 * @param[in] binary	If query is binary it will not give you an exact count of fragments rendered, but will 
		 *						instead just return 0 (no fragments were rendered) or 1 (one or more fragments were 
		 *						rendered). Binary queries can return sooner as they potentially do not need to wait until 
		 *						all of the geometry is rendered.
		 */
		virtual SPtr<OcclusionQuery> createOcclusionQuery(bool binary) const = 0;

		/** Triggers completed queries. Should be called every frame. */
		void _update();

	protected:
		friend class EventQuery;
		friend class TimerQuery;
		friend class OcclusionQuery;

		/**
		 * Deletes an Event query. Always use this method and don't delete them manually. Actual deletion will be delayed 
		 * until next update.
		 */
		static void deleteEventQuery(EventQuery* query);

		/**
		 * Deletes a Timer query. Always use this method and don't delete them manually. Actual deletion will be delayed 
		 * until next update.
		 */
		static void deleteTimerQuery(TimerQuery* query);

		/**
		 * Deletes an Occlusion query. Always use this method and don't delete them manually. Actual deletion will be 
		 * delayed until next update.
		 */
		static void deleteOcclusionQuery(OcclusionQuery* query);

		/** Deletes any queued queries. */
		void processDeletedQueue();

	protected:
		mutable Vector<EventQuery*> mEventQueries;
		mutable Vector<TimerQuery*> mTimerQueries;
		mutable Vector<OcclusionQuery*> mOcclusionQueries;

		mutable Vector<EventQuery*> mDeletedEventQueries;
		mutable Vector<TimerQuery*> mDeletedTimerQueries;
		mutable Vector<OcclusionQuery*> mDeletedOcclusionQueries;
	};

	/** @} */
}