// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


//
// Includes
//

#include <nel/misc/path.h>
#include <nel/misc/file.h>
#include <nel/misc/common.h>
#include <nel/3d/u_scene.h>
#include <nel/3d/u_light.h>
#include <nel/3d/u_camera.h>
#include <nel/3d/u_driver.h>
#include <nel/3d/u_instance.h>

using namespace NLMISC;
using namespace NL3D;

// function to split a string into several substrings delimited by specified characters
void split(const std::string &str, std::vector<std::string> &tokens, const std::string &delimiters)
{
	// Skip delimiters at beginning.
	std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	// Find first "non-delimiter".
	std::string::size_type pos     = str.find_first_of(delimiters, lastPos);

	while (std::string::npos != pos || std::string::npos != lastPos)
	{
		// Found a token, add it to the vector.
		tokens.push_back(str.substr(lastPos, pos - lastPos));
		// Skip delimiters.  Note the "not_of"
		lastPos = str.find_first_not_of(delimiters, pos);
		// Find next "non-delimiter"
		pos = str.find_first_of(delimiters, lastPos);
	}
}

#ifdef NL_OS_WINDOWS
sint WINAPI WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR cmdline, int /* nCmdShow */)
{
	// we can specify several shapes on command line
	// so we need to process it
	std::vector<std::string> argv;

	// argv[0] is the fullpath to the executable but since it's not used, we can let it empty
	argv.push_back("");

	// split the whole cmdline into args
	split(cmdline, argv, " ");

	// args number
	int argc = (int)argv.size();

#else
sint main(int argc, char **argv)
{
#endif

	try
	{
		// create OpenGL driver
		NL3D::UDriver *Driver = UDriver::createDriver();
		if (!Driver) throw 2;

		// create a window in 800x600
		Driver->setDisplay(UDriver::CMode(800, 600, 32, true));

		// set the title
		Driver->setWindowTitle(ucstring("NeL shape viewer"));

		// can use both dds and tga textures for shapes
		CPath::remapExtension ("dds", "tga", true);

		// create the light
		ULight *Light = ULight::createLight();
		if (!Light) throw 3;

		// set mode of the light
		Light->setMode(ULight::DirectionalLight);

		// set position of the light
		Light->setPosition(CVector(-20.f, 30.f, 10.f));

		// white light
		Light->setAmbiant(CRGBA(255, 255, 255));

		// set and enable the light
		Driver->setLight(0, *Light);
		Driver->enableLight(0);

		// Create a scene
		NL3D::UScene *Scene = Driver->createScene(true);
		if (!Scene) throw 4;

		// create the camera
		UCamera Camera = Scene->getCam();
		if (Camera.empty()) throw 5;

		Camera.setTransformMode (UTransformable::DirectMatrix);
		Camera.setPerspective ((float)Pi/2.f, 1.33f, 0.1f, 1000);

		// camera will look at entities
		Camera.lookAt (CVector(-10.f, 10.f, 0.f), CVector(0.f, 0.f, 0.f));

		// create entities
		std::vector<UInstance> Entities;

		// create each entity
		for(int i = 1; i < argc; ++i)
		{
			// use the path of the shape to find its textures
			CPath::addSearchPath(CFile::getPath(argv[i]), true, false);

			// add an entity to the scene
			UInstance Entity = Scene->createInstance(argv[i]);

			// if we can't create entity, skip it
			if (Entity.empty()) continue;

			// we will rotate it later so use Euler rotation transform mode
			Entity.setTransformMode(UTransformable::RotEuler);

			// add entity to the vector
			Entities.push_back(Entity);
		}

		// initial angle
		float angle = 0.f;

		// main loop
		while (Driver->isActive())
		{
			Driver->EventServer.pump();
			
			// the background is black
			Driver->clearBuffers(CRGBA(0, 0, 0));

			// increase the angle
			angle += 0.1f;

			if (angle >= NLMISC::Pi*2) angle = 0.f;

			// rotate all entities
			for(size_t i = 0; i < Entities.size(); ++i)
			{
				Entities[i].setRotEuler(0.f, 0.f, angle);
			}

			// animate the scene
			Scene->animate(NLMISC::CTime::getLocalTime() / 1000.0);

			// render the scene
			Scene->render();

			// show the scene
			Driver->swapBuffers();

			// escape will leave the program
			if (Driver->AsyncListener.isKeyPushed(KeyESCAPE))
			{
				break;
			}
			// F3 will change the render mode
			else if (Driver->AsyncListener.isKeyPushed(KeyF3))
			{
				UDriver::TPolygonMode p = Driver->getPolygonMode();
				p = UDriver::TPolygonMode(((int)p+1)%3);
				Driver->setPolygonMode(p);
			}
			// F12 will take a screenshot
			else if (Driver->AsyncListener.isKeyPushed(KeyF12))
			{
				CBitmap btm;
				Driver->getBuffer(btm);
				COFile fs(CFile::findNewFile("screenshot.png"));
				btm.writePNG(fs,24);
			}
		}

		// we are leaving the program

		// delete all entities
		for(size_t i = 0; i < Entities.size(); ++i)
		{
			Scene->deleteInstance(Entities[i]);
		}

		// delete the scene
		Driver->deleteScene(Scene);

		// delete the light
		delete Light;

		// release all textures and others elements
		Driver->release();

		// delete the driver
		delete Driver;
	}
	catch(int a)
	{
		return a;
	}
	catch(...)
	{
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}