#include "Viewer.h"
#include "Environment.h"
#include "InteractorPlugin.h"
#include "RendererPlugin.h"
#include "Exception.h"

Viewer::Viewer() : m_uWidth(32), m_uHeight(32)
{
}

Viewer::~Viewer()
{
	std::list<Plugin*> lisPlugins = GetPlugins();

	for (std::list<Plugin*>::iterator i = lisPlugins.begin(); i != lisPlugins.end(); i++)
		(*i)->GetEnvironment().remove(this);
}

void Viewer::initialize()
{
	bindContext();

	GLenum error = glewInit();

	if (error != GLEW_OK)
	{
		std::cerr << "Error initializing GLEW (" << glewGetErrorString(error) << ")." << std::endl << std::endl;
		throw Exception("Error initializing GLEW (%s).",glewGetErrorString(error));
	}

	m_pToolkit = new ufo::UXToolkit();
	m_pDisplay = new ufo::UXDisplay("dummygl");
	m_pContext = new ufo::UXContext(ufo::URectangle (0, 0, GetWidth(), GetHeight()),ufo::URectangle (0, 0, GetWidth(), GetHeight()));
	
	m_pContext->getRootPane()->getContentPane()->setLayout(new ufo::UBorderLayout());
	m_pContext->getRootPane()->setOpaque(false);

	m_pRendererPluginWidget = new ufo::UWidget();
	m_pRendererPluginWidget->setLayout(new ufo::UFlowLayout(4,4,ufo::AlignCenter,ufo::AlignTop));
	m_pRendererPluginWidget->setOpaque(false);

	m_pContext->getRootPane()->getContentPane()->add(m_pRendererPluginWidget,ufo::UBorderLayout::North);
/*
	ufo::UInternalFrame *pFrame = new ufo::UInternalFrame("Testme");
	pFrame->setSize(256,256);
	m_pContext->getRootPane()->addFrame(pFrame);
	pFrame->setVisible(true);
*/

}

void Viewer::uninitialize()
{
}

void Viewer::idle()
{
}

void Viewer::display()
{
	bindContext();

	glClearColor(0.0f,0.0f,0.0f,0.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	std::list<Plugin*> lisInteractorPlugins = GetPlugins("Interactor");
	std::list<Plugin*> lisRendererPlugins = GetPlugins("Renderer");

	for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
	{
		InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->underlay();
	}

	for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
	{
		RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->underlay();
	}

	for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
	{
		InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->display();
	}

	for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
	{
		RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->display();
	}

	for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
	{
		InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->overlay();
	}

	for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
	{
		RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->overlay();
	}

	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

	glColor4f(1.0f,1.0f,1.0f,0.0f);	
	glBegin(GL_QUADS);
	glVertex2f(-1.0f,-1.0f);
	glVertex2f(1.0f,-1.0f);
	glVertex2f(1.0f,1.0f);
	glVertex2f(-1.0f,1.0f);
	glEnd();

	glPopAttrib();

	glDisable(GL_BLEND);


	m_pDisplay->pumpEvents();
	m_pDisplay->dispatchEvents();
	m_pContext->getRootPane()->repaint();
	m_pContext->repaint();
}

void Viewer::reshape(const unsigned int uWidth, const unsigned int uHeight)
{
	bindContext();

	m_uWidth = uWidth;
	m_uHeight = uHeight;

	std::list<Plugin*> lisInteractorPlugins = GetPlugins("Interactor");
	std::list<Plugin*> lisRendererPlugins = GetPlugins("Renderer");

	for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
	{
		InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->reshape(uWidth,uHeight);
	}

	for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
	{
		RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->reshape(uWidth,uHeight);
	}

	m_pContext->setContextBounds(ufo::URectangle(0,0,(int)uWidth,(int)uHeight));
	m_pContext->setDeviceBounds(ufo::URectangle(0,0,(int)uWidth,(int)uHeight));
	m_pContext->getRootPane()->getContentPane()->setBounds(0,0,(int)uWidth,(int)uHeight);

	update();
}

void Viewer::mousePress(const MouseEvent & mouEvent)
{
	bindContext();

	const int iX = int(float(GetWidth())*0.5f*(mouEvent.GetPosition().GetX()+1.0f));
	const int iY = int(float(GetHeight())*0.5f*(1.0f-mouEvent.GetPosition().GetY()));
	const ufo::UMod_t uButton = mouEvent.GetButton() == MouseEvent::BUTTON_LEFT ? ufo::UMod::LeftButton : (mouEvent.GetButton() == MouseEvent::BUTTON_RIGHT ? ufo::UMod::RightButton : ufo::UMod::MiddleButton);

	if (!m_pDisplay->dispatchMouseButtonDown(m_pContext,iX,iY,uButton))
	{
		std::list<Plugin*> lisInteractorPlugins = GetPlugins("Interactor");
		std::list<Plugin*> lisRendererPlugins = GetPlugins("Renderer");

		for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
		{
			InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

			if (pPlugin)
				if (m_mapButtons[pPlugin]->isSelected())
					pPlugin->mousePress(mouEvent);
		}

		for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
		{
			RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

			if (pPlugin)
				if (m_mapButtons[pPlugin]->isSelected())
					pPlugin->mousePress(mouEvent);
		}
	}
}

void Viewer::mouseRelease(const MouseEvent & mouEvent)
{
	bindContext();

	const int iX = int(float(GetWidth())*0.5f*(mouEvent.GetPosition().GetX()+1.0f));
	const int iY = int(float(GetHeight())*0.5f*(1.0f-mouEvent.GetPosition().GetY()));
	const ufo::UMod_t uButton = mouEvent.GetButton() == MouseEvent::BUTTON_LEFT ? ufo::UMod::LeftButton : (mouEvent.GetButton() == MouseEvent::BUTTON_RIGHT ? ufo::UMod::RightButton : ufo::UMod::MiddleButton);

	if (!m_pDisplay->dispatchMouseButtonUp(m_pContext,iX,iY,uButton))
	{
		std::list<Plugin*> lisInteractorPlugins = GetPlugins("Interactor");
		std::list<Plugin*> lisRendererPlugins = GetPlugins("Renderer");

		for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
		{
			InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

			if (pPlugin)
				if (m_mapButtons[pPlugin]->isSelected())
					pPlugin->mouseRelease(mouEvent);
		}

		for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
		{
			RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

			if (pPlugin)
				if (m_mapButtons[pPlugin]->isSelected())
					pPlugin->mouseRelease(mouEvent);
		}
	}

	update();
}

void Viewer::mouseMove(const MouseEvent & mouEvent)
{
	bindContext();

	const int iX = int(float(GetWidth())*0.5f*(mouEvent.GetPosition().GetX()+1.0f));
	const int iY = int(float(GetHeight())*0.5f*(1.0f-mouEvent.GetPosition().GetY()));

	if (!m_pDisplay->dispatchMouseMove(m_pContext,iX,iY))
	{
		std::list<Plugin*> lisInteractorPlugins = GetPlugins("Interactor");
		std::list<Plugin*> lisRendererPlugins = GetPlugins("Renderer");

		for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
		{
			InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

			if (pPlugin)
				if (m_mapButtons[pPlugin]->isSelected())
					pPlugin->mouseMove(mouEvent);
		}

		for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
		{
			RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

			if (pPlugin)
				if (m_mapButtons[pPlugin]->isSelected())
					pPlugin->mouseMove(mouEvent);
		}
	}

	update();
}

void Viewer::keyboardPress(const KeyboardEvent & keyEvent)
{
	bindContext();

	std::list<Plugin*> lisInteractorPlugins = GetPlugins("Interactor");
	std::list<Plugin*> lisRendererPlugins = GetPlugins("Renderer");

	for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
	{
		InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->keyboardPress(keyEvent);
	}

	for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
	{
		RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->keyboardPress(keyEvent);
	}

	update();
}

void Viewer::keyboardRelease(const KeyboardEvent & keyEvent)
{
	bindContext();

	std::list<Plugin*> lisInteractorPlugins = GetPlugins("Interactor");
	std::list<Plugin*> lisRendererPlugins = GetPlugins("Renderer");

	for (std::list<Plugin*>::iterator i = lisInteractorPlugins.begin(); i != lisInteractorPlugins.end(); i++)
	{
		InteractorPlugin *pPlugin = dynamic_cast<InteractorPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->keyboardRelease(keyEvent);
	}

	for (std::list<Plugin*>::iterator i = lisRendererPlugins.begin(); i != lisRendererPlugins.end(); i++)
	{
		RendererPlugin *pPlugin = dynamic_cast<RendererPlugin*>(*i);

		if (pPlugin)
			if (m_mapButtons[pPlugin]->isSelected())
				pPlugin->keyboardRelease(keyEvent);
	}

	update();
}

void Viewer::add(Plugin *pPlugin)
{
	bindContext();
	initialize(pPlugin);

	if (pPlugin->IsInitialized())
	{
		m_mapPlugins[pPlugin->GetType()].push_back(pPlugin);
		pPlugin->GetEnvironment().add(this);

		const Image imgOnIcon(pPlugin->GetIcon());
		const Image imgOffIcon(imgOnIcon*0.5f);

		ufo::UImageIO *pOffImageIO = new ufo::UImageIO((unsigned char*)imgOffIcon.Get(),imgOffIcon.GetWidth(),imgOffIcon.GetHeight(),4);
		ufo::UImage *pOffImage = m_pContext->createImage(pOffImageIO);
		ufo::UImageIcon *pOffImageIcon = new ufo::UImageIcon(pOffImage);

		ufo::UImageIO *pOnImageIO = new ufo::UImageIO((unsigned char*)imgOnIcon.Get(),imgOnIcon.GetWidth(),imgOnIcon.GetHeight(),4);
		ufo::UImage *pOnImage = m_pContext->createImage(pOnImageIO);
		ufo::UImageIcon *pOnImageIcon = new ufo::UImageIcon(pOnImage);

		ufo::UButton *pButton = new ufo::UButton(pOffImageIcon);
		pButton->setPressedIcon(pOnImageIcon);		
		pButton->setToggable(true);
		pButton->setOpacity(0.5f);
		pButton->setName(pPlugin->GetName());
		pButton->put("plugin",new PluginObject(pPlugin));
		pButton->sigActivated().connect(ufo::slot(*this,&Viewer::OnPluginButtonPressed));

		m_pRendererPluginWidget->add(pButton);
		m_mapButtons[pPlugin] = pButton;
	}

	update();
}

void Viewer::add(const std::list<Plugin *> & lisPlugins)
{
	bindContext();

	ufo::UButtonGroup *pGroup = new ufo::UButtonGroup();

	for (std::list<Plugin*>::const_iterator i=lisPlugins.begin(); i != lisPlugins.end(); i++)
	{
		Plugin *pPlugin(*i);
		initialize(pPlugin);

		if (pPlugin->IsInitialized())
		{
			m_mapPlugins[pPlugin->GetType()].push_back(pPlugin);
			pPlugin->GetEnvironment().add(this);

			const Image imgOnIcon(pPlugin->GetIcon());
			const Image imgOffIcon(imgOnIcon*0.5f);

			ufo::UImageIO *pOffImageIO = new ufo::UImageIO((unsigned char*)imgOffIcon.Get(),imgOffIcon.GetWidth(),imgOffIcon.GetHeight(),4);
			ufo::UImage *pOffImage = m_pContext->createImage(pOffImageIO);
			ufo::UImageIcon *pOffImageIcon = new ufo::UImageIcon(pOffImage);

			ufo::UImageIO *pOnImageIO = new ufo::UImageIO((unsigned char*)imgOnIcon.Get(),imgOnIcon.GetWidth(),imgOnIcon.GetHeight(),4);
			ufo::UImage *pOnImage = m_pContext->createImage(pOnImageIO);
			ufo::UImageIcon *pOnImageIcon = new ufo::UImageIcon(pOnImage);

			ufo::UButton *pButton = new ufo::UButton(pOffImageIcon);
			pButton->setPressedIcon(pOnImageIcon);		
			pButton->setToggable(true);
			pButton->setOpacity(0.5f);
			pButton->setName(pPlugin->GetName());
			pButton->put("plugin",new PluginObject(pPlugin));
			pButton->sigActivated().connect(ufo::slot(*this,&Viewer::OnPluginButtonPressed));

			m_pRendererPluginWidget->add(pButton);
			pGroup->addButton(pButton);
			m_mapButtons[pPlugin] = pButton;
		}
	}

	update();
}

void Viewer::remove(Plugin *pPlugin)
{
	bindContext();

	m_mapPlugins[pPlugin->GetType()].remove(pPlugin);
	pPlugin->GetEnvironment().remove(this);

	std::map<Plugin *, ufo::UButton *>::iterator i = m_mapButtons.find(pPlugin);

	if (i != m_mapButtons.end())
	{
		delete i->second;
		m_mapButtons.erase(i);
	}

	update();
}

void Viewer::remove(const std::list<Plugin *> & lisPlugins)
{
	bindContext();

	for (std::list<Plugin*>::const_iterator j = lisPlugins.begin(); j != lisPlugins.end(); j++)
	{
		Plugin *pPlugin(*j);

		m_mapPlugins[pPlugin->GetType()].remove(pPlugin);
		pPlugin->GetEnvironment().remove(this);

		std::map<Plugin *, ufo::UButton *>::iterator i = m_mapButtons.find(pPlugin);

		if (i != m_mapButtons.end())
		{
			delete i->second;
			m_mapButtons.erase(i);
		}
	}

	update();
}

void Viewer::remove(Environment *pEnvironment)
{
	bindContext();

	for (std::map< std::string, std::list<Plugin*> >::iterator i = m_mapPlugins.begin(); i != m_mapPlugins.end(); i++)
	{
		for (std::list<Plugin*>::iterator j = i->second.begin(); j != i->second.end(); j++)
		{
			if (&((*j)->GetEnvironment()) == pEnvironment)
				j = i->second.erase(j);
		}
	}
}

void Viewer::SetEnabled(Plugin *pPlugin, const bool bEnabled)
{
	std::map<Plugin *, ufo::UButton *>::iterator i = m_mapButtons.find(pPlugin);

	if (i != m_mapButtons.end())
		i->second->setSelected(bEnabled);
}

const bool Viewer::IsEnabled(Plugin *pPlugin) const
{
	std::map<Plugin *, ufo::UButton *>::const_iterator i = m_mapButtons.find(pPlugin);

	if (i != m_mapButtons.end())
		return i->second->isSelected();

	return false;
}


void Viewer::initialize(Plugin *pPlugin)
{
	if (!pPlugin->IsInitialized())
		pPlugin->initialize();

	if (pPlugin->IsInitialized())
	{
		if (dynamic_cast<InteractorPlugin*>(pPlugin))
			dynamic_cast<InteractorPlugin*>(pPlugin)->reshape(GetWidth(),GetHeight());
		else if (dynamic_cast<RendererPlugin*>(pPlugin))
			dynamic_cast<RendererPlugin*>(pPlugin)->reshape(GetWidth(),GetHeight());
	}
}

void Viewer::OnPluginButtonPressed(ufo::UActionEvent * e)
{
	ufo::UButton * pButton = dynamic_cast<ufo::UButton*>(e->getSource());
	PluginObject *pPluginObject = dynamic_cast<PluginObject*>(pButton->get("plugin"));

	if (pPluginObject)
	{
		initialize(pPluginObject->GetPlugin());
		update();
	}
}
