/*
 * $Id: rendermanager.cpp 4661 2017-08-23 20:40:02Z antonin.misek-skoda-auto.cz $
 *
 * Description  : One line description of file.
 * Author       : Vaclav Kyba <mail/Jabber: vaseo1@gmail.com>  <ICQ: 98576293>
 *
 * Purpose      :
 *     Long description of what the file is for.
 */


#include "kernel.h"
#include "rendermanager.h"
//#include "kernel.h"
#include "basedefs.h"
#include "draganddrop.h"

using namespace VRUT;

RenderManager::~RenderManager()
{
	///Close all GL windows
	bool cycle=true;
	while (cycle)
	{
		cycle=false;
		windowListLock.ReadLock();
		for (WindowList::iterator glwit = windowList.begin(); glwit != windowList.end(); ++glwit)
		{
			if (*glwit)
			{
				cycle=true;
				GLWindow *glwin=*glwit;
				windowListLock.ReadUnlock();
				delete glwin;
				break;
			}
		}
	}
	windowListLock.ReadUnlock();
	WriteLocker wlLock(windowListLock);
	windowList.clear();
}

void RenderManager::ReleaseManagerData()
{
	///Close all GL windows
	bool cycle=true;
	while (cycle)
	{
		cycle=false;
		windowListLock.ReadLock();
		for (auto &glw : windowList)
		{
			if (glw)
			{
				cycle=true;
				GLWindow *glwin=glw;
				windowListLock.ReadUnlock();
				delete glwin;
				break;
			}
		}
	}
	windowListLock.ReadUnlock();
	windowListLock.WriteLock();
	windowList.clear();
	windowListLock.WriteUnlock();
	ServerBase<RenderModule>::ReleaseManagerData();
}


GLWindow * RenderManager::GetGLWindow(RENDER_WINDOW_ID windowID)
{
	ReadLocker rlLock(windowListLock);
	return ( windowID >= windowList.size() ? (GLWindow *)NULL : windowList[windowID] );
}

GLWindow * RenderManager::GetGLWindow(RenderModule * module)
{
	ReadLocker rlLock(windowListLock);
	for (auto &win : windowList)
	{
		if (win &&  (win->GetRenderModule() == module))
			return win;
	}
	return NULL;
}

GLWindow * RenderManager::GetActiveGLWindow()
{
	ReadLocker rlLock(windowListLock);
	for (auto &win : windowList)
		if (win && win->HasFocus())
			return win;
	wxAuiNotebook * renderNotebook = KERNEL->GetMainWindow()->GetRenderNotebook();
	if (!renderNotebook || renderNotebook->GetPageCount()==0)
		return NULL;
	return (GLWindow *)renderNotebook->GetPage(renderNotebook->GetSelection());
}

RENDER_WINDOW_ID RenderManager::RenderScene(smart_pointer_v2<Scene> &scene, NODE_ID cameraID, RENDER_WINDOW_ID windowID, bool activeStereo)
{
#ifndef VRUT_HPC
	if (!scene)
	{
		LOGERROR(SERVER_CLASS_NAMES[serverID]+wxT("Cannot render NULL scene"));
		return RENDER_WINDOW_ID_NONE;
	}

	MODULE_ID moduleID = getOrCreateModuleInstance();
	RenderModule * module = getModule(moduleID);
	if (module && module->IsOfType(MODULE_TYPE_RENDER))
	{
		windowListLock.WriteLock();
		if (windowList.size()>windowID)
		{
			if (windowList[windowID])
			{
				GLWindow *glwin=windowList[windowID];
				windowListLock.WriteUnlock();
				delete glwin;
			}
			else
				windowListLock.WriteUnlock();
		}
		else
		{
			if (windowID==RENDER_WINDOW_ID_NONE)
				windowID=(RENDER_WINDOW_ID)windowList.size();
			windowList.insert(windowList.end(), windowID-windowList.size()+1, NULL);
			windowListLock.WriteUnlock();
		}
		wxString title = wxString::Format(wxT("%lu ("), (unsigned long)windowID)+scene->GetName()+wxT(")");
		MainWindow * mainWindow = KERNEL->GetMainWindow();
		wxAuiNotebook * renderNotebook = mainWindow->GetRenderNotebook();
		
		
		int attribList[12];
		attribList[0]=WX_GL_STENCIL_SIZE; //enable stencil buffer for rendering of mirrors
		attribList[1]=8;
		attribList[2]=WX_GL_RGBA;
		attribList[3]=WX_GL_DOUBLEBUFFER;
		attribList[10]=0;
		attribList[11]=0;

#if wxCHECK_VERSION(2, 9, 1)
		attribList[4]=WX_GL_MIN_RED;
		attribList[5]=10; //In case we are connected via Display Port to a 10 bit per component capable device
		attribList[6]=WX_GL_MIN_GREEN;
		attribList[7]=10;
		attribList[8]=WX_GL_MIN_BLUE;
		attribList[9]=10;
		
		//check if active stereo is enabled
		if(activeStereo)  
		{
			//check support for active stereo
			int attribListStereo[2] = {WX_GL_STEREO, 0};
			if (wxGLCanvas::IsDisplaySupported(attribListStereo)) //only in wxWidgets 2.9.x
			{
				attribList[10]=WX_GL_STEREO;

				//set active stereo to renderModule
				module->setActiveStereo(true);
			}
			else
				LOGERROR(wxT("<RenderManager>Active stereo is not supported!"));			
		}		

		if (!wxGLCanvas::IsDisplaySupported(attribList)) //only in wxWidgets 2.9.x
		{
			attribList[5]=8;
			attribList[7]=8;
			attribList[9]=8;
		}
#else
		attribList[4]=WX_GL_MIN_RED;
		attribList[5]=8;
		attribList[6]=WX_GL_MIN_GREEN;
		attribList[7]=8;
		attribList[8]=WX_GL_MIN_BLUE;
		attribList[9]=8;

		if(activeStereo)  
		{
			attribList[10]=WX_GL_STEREO;

			//set active stereo indicator to renderModule
			module->setActiveStereo(true);			
		}	
#endif

		GLWindow * glw = new GLWindow(renderNotebook, module, windowID, wxDefaultPosition, wxDefaultSize, attribList, title);
		//GLWindow * glw = new GLWindow(renderNotebook, module, windowID, wxDefaultPosition, wxDefaultSize, (int *)NULL, title);
		glw->SetRenderManager(this);
		wxGLContext *sharedContext(0);
		{
			/*//AM: commented out because shared context cause problems with wxWidgets 3.1.0
			ReadLocker rlLock(windowListLock);
			for (WindowList::iterator i = windowList.begin(); i != windowList.end(); ++i)
			{
				if (*i && *i != glw && (*i)->GetGLContext())
				{
					sharedContext = (*i)->GetGLContext();
					break;
				}
			}*/
		}
		//each render module must create context itself (because of Vulkan), for example by using:
		//if (!glw->CreateGLContext(sharedContext))
		//	LOGERROR(wxT("RenderGl: Failed to create GL context"));

		glw->SetFocus();
		renderNotebook->AddPage(glw, title, true);

#if wxCHECK_VERSION(2, 9, 4)
		renderNotebook->SetPageToolTip(renderNotebook->GetPageCount()-1, wxT("Right click here to Fit"));
#endif
		glw->SetDropTarget(new DropTarget(glw, DropTarget::MATERIAL | DropTarget::FILE, true, true));

		KERNEL->GetMessageSink()->PostEvent(GET_EVT_GUI_PANE_SHOW(ID_MainWin_View_RenderNotebook, true));

		{
			WriteLocker wlLock(windowListLock);
			windowList[windowID] = glw;
		}
		commonSetup(module, glw, scene, cameraID);

		///Inform about new window
		KERNEL->GetMessageSink()->PostEvent(GET_EVT_RENDER_WIN_CREATED(windowID));

		return windowID;
	}
	else
	{
		releaseModule(moduleID);
		LOGERROR(SERVER_CLASS_NAMES[serverID] + wxT("No suitable render module found"));
		return RENDER_WINDOW_ID_NONE;
	}
#else
	return RENDER_WINDOW_ID_NONE;
#endif
}

/*
void RenderManager::commonPredefSetup(const wxString &moduleName, SCENE_ID sceneID, NODE_ID cameraID)
{
	Parameter::ParameterIdentifier camPI(moduleName, wxT("cameraID"), wxT("predef"));
	///Register here so value will not be overwritten when module registers its default
	KERNEL->environment.SetParam(camPI, wxString::Format(wxT("%i"), cameraID));

	Parameter::ParameterIdentifier scenePI(moduleName, wxT("sceneID"), wxT("predef"));
	///Register here so value will not be overwritten when module registers its default
	KERNEL->environment.SetParam(scenePI, wxString::Format(wxT("%i"), sceneID));
}*/

void RenderManager::commonSetup(RenderModule *module, GLWindow *glw, smart_pointer_v2<Scene> &scene, NODE_ID cameraID) {
	Parameter::ParameterIdentifier camPI(module->GetName(), wxT("cameraID"), module->GetID());
	///Register here so value will not be overwritten when module registers its default
	KERNEL->environment.SetParam(camPI, wxString::Format(wxT("%lu"), (unsigned long)cameraID));

	Parameter::ParameterIdentifier scenePI(module->GetName(), wxT("sceneID"), module->GetID());
	///Register here so value will not be overwritten when module registers its default
	KERNEL->environment.SetParam(scenePI, wxString::Format(wxT("%lu"), (unsigned long)scene->GetID()));
	ModuleHandle<RenderModule>::StartModuleThread(module);

	///Update projection after frame is shown
	//workaround added for cluster (because events in EventHandler are sometimes faster then in main thread...)
	//LOG(wxString::Format(wxT("RenderManager::commonSetup before SetProjectionParams camID=%lu, w=%i, h=%i"), (unsigned long)cameraID, glw->GetClientSize().x, glw->GetClientSize().y));
	smart_pointer<Camera> cam;
	scene->GetNode(cam, cameraID);
	if (cam && cam->IsOfType(SceneNode::CAMERA) && (cam->GetLastWidth()==640) && (cam->GetLastHeight()==480))
		scene->SetProjectionParams(cameraID, glw->GetClientSize().x, glw->GetClientSize().y);
}


void RenderManager::DestroyGLWindow(GLWindow * glWin)
{
	windowListLock.WriteLock();
	if (glWin && (windowList[glWin->GetID()]))
	{
		windowList[glWin->GetID()] = (GLWindow *)NULL;
		windowListLock.WriteUnlock();
		MODULE_ID mid = glWin->GetRenderModule()->GetID();
		glWin->Deinitialize(protectedCameraID);
		releaseModule(mid);
		return;
	}
	windowListLock.WriteUnlock();
}

void RenderManager::DestroyGLWindow(RENDER_WINDOW_ID windowID)
{
	windowListLock.WriteLock();
	if ((windowList.size()>windowID) && (windowList.at(windowID)))
	{
		GLWindow *glwin=windowList[windowID];
		windowListLock.WriteUnlock();
		delete glwin;
	}
	else
		windowListLock.WriteUnlock();
}

void RenderManager::DestroyGLWindow(RenderModule * module)
{
	windowListLock.WriteLock();
	for (auto &win : windowList)
	{
		if (win && (win->GetRenderModule() == module))
		{
			GLWindow *glwin = win;
			win = NULL;
			windowListLock.WriteUnlock();
			delete glwin;
			return;
		}
	}
	windowListLock.WriteUnlock();
}

void RenderManager::SetProtectedCameraID(NODE_ID _protectedCameraID)
{
	protectedCameraID = _protectedCameraID;
}

