#ifndef SCENE_H
#define SCENE_H

#include <hash_map>
#include <list>
#include <string>

#include <kLibrary\factory\factory.h>

#include "d3dUtility.h"

#include "singleton.h"

#include "texture.h"
#include "material.h"
#include "mesh.h"
#include "xLoader.h"

#include "shape.h"
#include "light.h"
#include "camera.h"

#include "terrain.h"

#include "text.h"

#include "sphere.h"

#include <Octree.h>
#include <BSPTree.h>

namespace cbk
{
	class Scene : public Singleton<Scene>
	{
	public:
		struct mousePos
		{
			long x, y;
			mousePos(const long x = 0, const long y = 0)
			{
				this->x = x;
				this->y = y;
			}
		};

		typedef kLibrary::Factory<CBK::Material, IDirect3DDevice9*> MaterialFactory;
		typedef kLibrary::Factory<CBK::Texture, IDirect3DDevice9*> TextureFactory;
		typedef kLibrary::Factory<CBK::Shapes::xLoader, const CBK::MeshParam&> MeshFactory;
		typedef kLibrary::Factory<CBK::Terrain, const CBK::TerrainParam> TerrainFactory;

		inline const int& getWidth(void) const;
		inline const int& getHeight(void) const;

		inline const HINSTANCE& getInstance(void) const;

		inline operator IDirect3DDevice9*(void) const;
		inline IDirect3DDevice9* getDevice(void) const;

		inline Scene& setup(const HINSTANCE& instance, const int& width = 0, const int& height = 0);

		inline Scene& setWidth(const int& width);
		inline Scene& setHeight(const int& height);
		inline Scene& setInstance(const HINSTANCE& instance);
		inline Scene& createDevice(void);

		inline MaterialFactory* getMaterialFactory(void);
		inline TextureFactory* getTextureFactory(void);
		inline MeshFactory* getMeshFactory(void);

		inline Scene& update(const float& dt);
		inline Scene& draw(void);

		inline CBK::Shape* getObject(const std::string& name);
		inline Scene& addObject(const std::string& name, CBK::Shape* object);
		inline Scene& removeObject(const std::string& name);
		inline Scene& sortObjects(void);

		inline CBK::Camera* getCamera(const std::string& name) const;
		inline Scene& addCamera(const std::string& name, CBK::Camera* object);
		inline Scene& removeCamera(const std::string& name);
		inline Scene& applyCamera(const std::string& name);

		inline CBK::Light* getLight(const std::string& name) const;
		inline Scene& addLight(const std::string& name, CBK::Light* object);
		inline Scene& removeLight(const std::string& name);
		inline Scene& applyLight(const std::string& name, const int index = 0);

		inline CBK::Shape* addTerrian(const std::string& file, const std::string& name, const int numVertsPerRow, const int numVertsPerCol, const int cellSpacing, const float heightScale);
		inline CBK::Shape* getTerrian(const std::string& name);
		inline Scene& removeTerrian(const std::string& name);
		inline Scene& applyTerrain(const std::string& name);

		Scene& clearSelection(void);

		Scene& mouseLClick(const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);
		Scene& mouseRClick(const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);
		Scene& mouseMClick(const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);

		Scene& mouseLUp(const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);
		Scene& mouseRUp(const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);
		Scene& mouseMUp(const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);

		Scene& mouseScroll(const float& distance, const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);

		Scene& mouseMove(const float& x, const float& y, const bool shift, const bool ctrl, const bool lButton, const bool rButton, const bool mButton);
		CBK::Shape* mouseCoords(D3DXVECTOR3* nearPoint, D3DXVECTOR3* farPoint, const long& x, const long& y, const bool doTrace = false);
		CBK::Shape* trace(D3DXVECTOR3* hitLocation, const D3DXVECTOR3& start, const D3DXVECTOR3& end, const bool collideObjects = true) const;

		Scene& keyDown(const long& keyCode);
		Scene& keyUp(const long& keyCode);

		Scene& setMouseScrollArea(const float& width, const float& height);

		Scene& setCameraScrollSpeed(const float& speed);
		const float& getCameraScrollSpeed(void) const;

		Scene& setCameraZoomSpeed(const float& speed);
		const float& getCameraZoomSpeed(void) const;

		inline ~Scene(void);
	protected:
		inline Scene(void);

	private:
		CBK::Text* fps[2];
		CBK::Text* obj[2];
		CBK::Text* sel[2];

		int _width, _height;
		HINSTANCE _hinstance;
		HWND _hwnd;

		D3DVIEWPORT9 _viewport;
		D3DXMATRIX _matProjection;

		MaterialFactory _materialLoader;
		TextureFactory _textureLoader;
		MeshFactory _meshLoader;
		TerrainFactory _terrainLoader;

		CBK::Terrain* _curTerrainMesh;
		CBK::Shape* _curTerrainTransform;
		stdext::hash_map<std::string, CBK::Shape*> _terrain;

		std::list<CBK::Shape*> _objects;
		std::list<CBK::Shape*> _selection;
		stdext::hash_map<std::string, std::list<CBK::Shape*>::iterator> _objectsNamed;

		float _cameraScrollSpeed, _cameraZoomSpeed;
		D3DXVECTOR2 _vectCameraScroll;
		CBK::Camera* _curCamera;
		stdext::hash_map<std::string, CBK::Camera*> _cameras;

		stdext::hash_map<std::string, CBK::Light*> _lights;

		mousePos _mousePosition, _mousePositionDown;
		D3DXVECTOR3 _mousePositionNear, _mousePositionFar;
		D3DXVECTOR2 _mouseScrollArea;

		IDirect3DDevice9* _device;
		friend class Singleton<Scene>;

		static bool _objSortFunc(const CBK::Shape* first, const CBK::Shape* second);

		Octree<CBK::Shape*> octree;
		BSPTree<CBK::Shape*> bsptree;

		enum _MouseButtons
		{
			MOUSE_Left = 0,
			MOUSE_Right,
			MOUSE_Middle,
		};

		static const long _maxKey = 255;
		bool _keyState[_maxKey + 1];
		bool _mouseState[3];
		DWORD _clickTime;

		bool _deviceLost;
		bool _recoverDevice(void);
		D3DPRESENT_PARAMETERS _deviceParams;

		int _FPSFrames;
		float _FPSTime;
		float _lastFPS;

		DWORD _terrainRenderMode;
		bool _renderBoundingBox;
		bool _renderBSPPlanes;
		bool _renderOctreeNodes;
	};
}

#include "scene.inl"

#endif //SCENE_H
