#include "scene.h"

using namespace CBK;
using namespace kLibary;
using namespace std;
using namespace stdext;

namespace cbk
{
	Scene::Scene(void)
	{
		_width = 640;
		_height = 480;
		_hinstance = 0;
		_device = 0;
	}

	Scene::~Scene(void)
	{
		{
			list<Shape*>::iterator x;
			list<Shape*>::iterator end;

			end = _objects.end();
			for (x = _objects.begin(); x != end; x++)
			{
				delete (*x);
				*x = 0;
			}
		}

		{
			hash_map<string, Light*>::iterator x;
			hash_map<string, Light*>::iterator end;

			end = _lights.end();
			for (x = _lights.begin(); x != end; x++)
			{
				delete x->second;
				x->second = 0;
			}
		}

		{
			hash_map<string, Camera*>::iterator x;
			hash_map<string, Camera*>::iterator end;

			end = _cameras.end();
			for (x = _cameras.begin(); x != end; x++)
			{
				delete x->second;
				x->second = 0;
			}
		}
	}

	Shape* Scene::addObject(const string& objectName, Shape* newObject)
	{
		_objects.push_front(newObject);
		pair<string, list<Shape*>::iterator> myPair(objectName, _objects.begin());
		_objectsNamed.insert(myPair);

		return newObject;
	}

	Camera* Scene::addCamera(const string& cameraName, Camera* newCamera)
	{
		pair<string, Camera*> myPair(cameraName, newCamera);
		_cameras.insert(myPair);
		return newCamera;
	}

	Light* Scene::addLight(const string& lightName, Light* newLight)
	{
		pair<string, Light*> myPair(lightName, newLight);
		_lights.insert(myPair);
		return newLight;
	}

	Shape* Scene::getObject(const string& objectName)
	{
		stdext::hash_map<std::string, list<Shape*>::iterator>::iterator found = _objectsNamed.find(objectName);
		if (found != _objectsNamed.end())
			return *(found->second);

		return 0;
	}

	Camera* Scene::getCamera(const string& cameraName)
	{
		stdext::hash_map<std::string, CBK::Camera*>::iterator found = _cameras.find(cameraName);
		if (found != _cameras.end())
			return found->second;

		return 0;
	}

	Light* Scene::getLight(const string& lightName)
	{
		stdext::hash_map<std::string, CBK::Light*>::iterator found = _lights.find(lightName);
		if (found != _lights.end())
			return found->second;

		return 0;
	}

	Scene& Scene::sortObjects(void)
	{
		_objects.sort();
		return *this;
	}

	Scene& Scene::applyLight(const std::string& lightName, const DWORD& index)
	{
		stdext::hash_map<std::string, CBK::Light*>::iterator found = _lights.find(lightName);
		if (found != _lights.end())
			(found->second)->apply(index);

		return *this;
	}

	Scene& Scene::applyCamera(const std::string& cameraName)
	{
		stdext::hash_map<std::string, CBK::Camera*>::iterator found = _cameras.find(cameraName);
		if (found != _cameras.end())
			(found->second)->apply();

		return *this;
	}

	Scene& Scene::draw(void)
	{
		list<Shape*>::iterator x;
		list<Shape*>::iterator end;

		_device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
		_device->BeginScene();

		end = _objects.end();
		for (x = _objects.begin(); x != end; x++)
			(*x)->apply();

		_device->EndScene();
		_device->Present(0, 0, 0, 0);

		return *this;
	}

	Scene& Scene::setup(const HINSTANCE& instance, const int& width, const int& height)
	{
		setWidth(width);
		setHeight(height);
		setInstance(instance);
		createDevice();

		return *this;
	}

	Scene& Scene::setWidth(const int& width)
	{
		if (_device == 0)
			_width = width;
		return *this;
	}

	Scene& Scene::setHeight(const int& height)
	{
		if (_device == 0)
			_height = height;
		return *this;
	}

	Scene& Scene::setInstance(const HINSTANCE& instance)
	{
		if (_device == 0)
			_hinstance = instance;
		return *this;
	}

	Scene& Scene::createDevice(void)
	{
		if ((_hinstance == 0) || (_width == 0) || (_height == 0))
			return *this;

		d3d::InitD3D(_hinstance, _width, _height, true, D3DDEVTYPE_HAL, &_device);
		return *this;
	}

	const int& Scene::getWidth(void) const
	{
		return _width;
	}

	const int& Scene::getHeight(void) const
	{
		return _height;
	}

	Scene::operator IDirect3DDevice9*(void) const
	{
		return _device;
	}

	IDirect3DDevice9* Scene::getDevice(void) const
	{
		return _device;
	}

	const HINSTANCE& Scene::getInstance(void) const
	{
		return _hinstance;
	}

	Factory<Material, IDirect3DDevice9*>* Scene::getMaterialFactory(void)
	{
		return &_materialFactory;
	}

	Factory<Texture, IDirect3DDevice9*>* Scene::getTextureFactory(void)
	{
		return &_textureFactory;
	}
}