#pragma once

#include "volumeshop.h"

#include <QMainWindow>
#include <QStackedWidget>
#include <QGridLayout>
#include <QFrame>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMenu>
#include <QSplitter>
#include <QFileDialog>
#include <QProgressDialog>
#include <QAction>
#include <QSlider>
#include <QCheckBox>
#include <QGroupBox>
#include <QComboBox>
#include <QThread>
#include <QMenuBar>
#include <QFileInfo>

#include <iostream>
#include <fstream>
#include <string>

#include "QSidebar.h"
#include "QViewer.h"
#include "QGradientWidget.h"

#include "ImporterPlugin.h"
#include "ExporterPlugin.h"

#include "logo.xpm"
#include "open.xpm"
#include "save.xpm"
#include "ghost.xpm"
#include "add.xpm"
#include "remove.xpm"

class QMainframe : public QMainWindow
{
	Q_OBJECT

	class QImportThread : public QThread
	{
	public:
		QImportThread(ImporterPlugin &impPlugin, const std::string &strFilename) : m_impPlugin(impPlugin), m_strFilename(strFilename)
		{
		};

		virtual ~QImportThread()
		{
		};

	protected:

		virtual void run()
		{
			m_impPlugin.load(m_strFilename);
		};

	private:
		ImporterPlugin &m_impPlugin;
		const std::string &m_strFilename;
	};

	class QExportThread : public QThread
	{
	public:
		QExportThread(ExporterPlugin &impPlugin, const std::string &strFilename) : m_impPlugin(impPlugin), m_strFilename(strFilename)
		{
		};

		virtual ~QExportThread()
		{
		};

	protected:

		virtual void run()
		{
			m_impPlugin.save(m_strFilename);
		};

	private:
		ExporterPlugin &m_impPlugin;
		const std::string &m_strFilename;
	};

public:
	QMainframe(QWidget *pParent = 0) : QMainWindow(pParent), m_pEnvironment(NULL), m_pSidebar(NULL), m_pViewer(NULL)
	{
		m_pUpdateTimer = new QTimer(this);
		connect(m_pUpdateTimer,SIGNAL(timeout()),SLOT(OnUpdateTimer()));

		QAction *pLoadAction = new QAction("Open",this);
		pLoadAction->setShortcut(QKeySequence("Ctrl+O"));
		pLoadAction->setShortcutContext(Qt::ApplicationShortcut);
		connect(pLoadAction, SIGNAL(activated()), this, SLOT( OnLoad()));

		QAction *pSaveAction = new QAction("Save",this);
		pSaveAction->setShortcut(QKeySequence("Ctrl+S"));
		pSaveAction->setShortcutContext(Qt::ApplicationShortcut);
		connect(pSaveAction, SIGNAL(activated()), this, SLOT( OnSave()));

		QMenu *pFileMenu = menuBar()->addMenu("&File");
		pFileMenu->addAction(pLoadAction);
		pFileMenu->addAction(pSaveAction);		
		
		QSplitter *pSplitter = new QSplitter(this);
		m_pSidebar = new QSidebar(pSplitter);

		QLabel *pLabel = new QLabel(m_pSidebar);
		pLabel->setPixmap(QPixmap(logo_xpm));
		m_pSidebar->layout()->addWidget(pLabel);

		m_pViewer = new QViewer(pSplitter);

		pSplitter->addWidget(m_pSidebar);
		pSplitter->setStretchFactor(0,0);

		pSplitter->addWidget(m_pViewer);
		pSplitter->setStretchFactor(1,1);

		{
			QWidget *pDataSet = new QWidget(this);
			QVBoxLayout *pDataSetLayout = new QVBoxLayout(pDataSet);

			QFrame *pFileButtons = new QFrame(pDataSet);
			QHBoxLayout *pFileButtonsLayout = new QHBoxLayout(pFileButtons);

			QToolButton *pImportButton = new QToolButton(pFileButtons);
			pImportButton->setIcon(QPixmap(open_xpm));
			pImportButton->setAutoRaise(true);
			pFileButtonsLayout->addWidget(pImportButton);

			QToolButton *pExportButton = new QToolButton (pFileButtons);
			pExportButton->setIcon(QPixmap(save_xpm));
			pExportButton->setAutoRaise(true);
			pFileButtonsLayout->addWidget(pExportButton);

			pFileButtonsLayout->addStretch(1);

			pDataSetLayout->addWidget(pFileButtons);
//			pDataSetLayout->addStretch(1);

//			pDataSet->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum);

			connect(pImportButton,SIGNAL(clicked()),this,SLOT(OnImport()));
			connect(pExportButton,SIGNAL(clicked()),this,SLOT(OnExport()));

			m_pSidebar->addItem(pDataSet,"Data set");
		}


		{
			QWidget *pColorTransferFunction= new QWidget(this);
			QGridLayout *pColorTransferFunctionLayout = new QGridLayout(pColorTransferFunction);

			m_pOperatorComboBoxWidgetStack = new QStackedWidget(pColorTransferFunction);
			pColorTransferFunctionLayout->addWidget(m_pOperatorComboBoxWidgetStack,0,1);

			m_pBackgroundSelectionOperatorComboBox = new QComboBox(m_pOperatorComboBoxWidgetStack);
			m_pBackgroundSelectionOperatorComboBox->addItem("over",Compositor::OPERATOR_OVER);
			m_pBackgroundSelectionOperatorComboBox->addItem("rover",Compositor::OPERATOR_ROVER);
			m_pBackgroundSelectionOperatorComboBox->addItem("in",Compositor::OPERATOR_IN);
			m_pBackgroundSelectionOperatorComboBox->addItem("rin",Compositor::OPERATOR_RIN);
			m_pBackgroundSelectionOperatorComboBox->addItem("out",Compositor::OPERATOR_OUT);
			m_pBackgroundSelectionOperatorComboBox->addItem("rout",Compositor::OPERATOR_ROUT);
			m_pBackgroundSelectionOperatorComboBox->addItem("atop",Compositor::OPERATOR_ATOP);
			m_pBackgroundSelectionOperatorComboBox->addItem("ratop",Compositor::OPERATOR_RATOP);
			m_pBackgroundSelectionOperatorComboBox->addItem("xor",Compositor::OPERATOR_XOR);
			m_pBackgroundSelectionOperatorComboBox->addItem("plus",Compositor::OPERATOR_PLUS);
			m_pBackgroundSelectionOperatorComboBox->addItem("custom",Compositor::OPERATOR_CUSTOM);
			m_pBackgroundSelectionOperatorComboBox->addItem("rcustom",Compositor::OPERATOR_RCUSTOM);
			m_pOperatorComboBoxWidgetStack->addWidget(m_pBackgroundSelectionOperatorComboBox);

			m_pGhostSelectionOperatorComboBox = new QComboBox(m_pOperatorComboBoxWidgetStack);
			m_pGhostSelectionOperatorComboBox->addItem("over",Compositor::OPERATOR_OVER);
			m_pGhostSelectionOperatorComboBox->addItem("rover",Compositor::OPERATOR_ROVER);
			m_pGhostSelectionOperatorComboBox->addItem("in",Compositor::OPERATOR_IN);
			m_pGhostSelectionOperatorComboBox->addItem("rin",Compositor::OPERATOR_RIN);
			m_pGhostSelectionOperatorComboBox->addItem("out",Compositor::OPERATOR_OUT);
			m_pGhostSelectionOperatorComboBox->addItem("rout",Compositor::OPERATOR_ROUT);
			m_pGhostSelectionOperatorComboBox->addItem("atop",Compositor::OPERATOR_ATOP);
			m_pGhostSelectionOperatorComboBox->addItem("ratop",Compositor::OPERATOR_RATOP);
			m_pGhostSelectionOperatorComboBox->addItem("xor",Compositor::OPERATOR_XOR);
			m_pGhostSelectionOperatorComboBox->addItem("plus",Compositor::OPERATOR_PLUS);
			m_pGhostSelectionOperatorComboBox->addItem("custom",Compositor::OPERATOR_CUSTOM);
			m_pGhostSelectionOperatorComboBox->addItem("rcustom",Compositor::OPERATOR_RCUSTOM);
			m_pOperatorComboBoxWidgetStack->addWidget(m_pGhostSelectionOperatorComboBox);			

			m_pSelectionGradientWidget = new QGradientWidget(QGradientWidget::ORIENTATION_VERTICAL,pColorTransferFunction);
			pColorTransferFunctionLayout->addWidget(m_pSelectionGradientWidget,1,0);

			m_pGradientWidgetStack = new QStackedWidget(pColorTransferFunction);
			pColorTransferFunctionLayout->addWidget(m_pGradientWidgetStack,2,1);
			m_pBackgroundGradientWidget = new QGradientWidget(QGradientWidget::ORIENTATION_HORIZONTAL,m_pGradientWidgetStack);
			m_pGhostGradientWidget = new QGradientWidget(QGradientWidget::ORIENTATION_HORIZONTAL,m_pGradientWidgetStack);
			m_pGradientWidgetStack->addWidget(m_pBackgroundGradientWidget);
			m_pGradientWidgetStack->addWidget(m_pGhostGradientWidget);

			m_pGradientAreaWidgetStack = new QStackedWidget(pColorTransferFunction);
			pColorTransferFunctionLayout->addWidget(m_pGradientAreaWidgetStack,1,1);
			m_pBackgroundSelectionGradientAreaWidget = new QGradientArea(m_pBackgroundGradientWidget->GetGradient(),m_pSelectionGradientWidget->GetGradient(),m_pGradientAreaWidgetStack);
			m_pGhostSelectionGradientAreaWidget = new QGradientArea(m_pGhostGradientWidget->GetGradient(),m_pSelectionGradientWidget->GetGradient(),m_pGradientAreaWidgetStack);
			m_pGradientAreaWidgetStack->addWidget(m_pBackgroundSelectionGradientAreaWidget);
			m_pGradientAreaWidgetStack->addWidget(m_pGhostSelectionGradientAreaWidget);

			QWidget *pBackgroundGhostButtonWidget = new QWidget(pColorTransferFunction);
			pColorTransferFunctionLayout->addWidget(pBackgroundGhostButtonWidget,2,0);
			QGridLayout *pBackgroundGhostButtonLayout = new QGridLayout(pBackgroundGhostButtonWidget);
			m_pBackgroundGhostButton = new QToolButton(pBackgroundGhostButtonWidget);
			m_pBackgroundGhostButton->setIcon(QPixmap(ghost_xpm));
			m_pBackgroundGhostButton->setAutoRaise(true);
			m_pBackgroundGhostButton->setCheckable(true);
			pBackgroundGhostButtonLayout->addWidget(m_pBackgroundGhostButton,1,1);

			connect(m_pSelectionGradientWidget,SIGNAL(valueChanged()),m_pBackgroundSelectionGradientAreaWidget,SLOT(update()));
			connect(m_pSelectionGradientWidget,SIGNAL(valueChanged()),m_pGhostSelectionGradientAreaWidget,SLOT(update()));

			connect(m_pBackgroundGradientWidget,SIGNAL(valueChanged()),m_pBackgroundSelectionGradientAreaWidget,SLOT(update()));
			connect(m_pGhostGradientWidget,SIGNAL(valueChanged()),m_pGhostSelectionGradientAreaWidget,SLOT(update()));

			connect(m_pSelectionGradientWidget,SIGNAL(valueChanged()),SLOT(OnSelectionGradientWidgetChanged()));
			connect(m_pBackgroundGradientWidget,SIGNAL(valueChanged()),SLOT(OnBackgroundGradientWidgetChanged()));
			connect(m_pGhostGradientWidget,SIGNAL(valueChanged()),SLOT(OnGhostGradientWidgetChanged()));

			connect(m_pBackgroundSelectionOperatorComboBox,SIGNAL(activated(int)),SLOT(OnBackgroundSelectionOperatorComboBoxActivated(int)));
			connect(m_pGhostSelectionOperatorComboBox,SIGNAL(activated(int)),SLOT(OnGhostSelectionOperatorComboBoxActivated(int)));
			connect(m_pBackgroundGhostButton,SIGNAL(toggled(bool)),SLOT(OnBackgroundGhostButtonToggled(bool)));

			m_pOperatorComboBoxWidgetStack->setCurrentWidget(m_pBackgroundSelectionOperatorComboBox);
			m_pGradientWidgetStack->setCurrentWidget(m_pBackgroundGradientWidget);
			m_pGradientAreaWidgetStack->setCurrentWidget(m_pBackgroundSelectionGradientAreaWidget);

			m_pSidebar->addItem(pColorTransferFunction,"Color transfer function");
		}

		{
			QWidget *pLightingTransferFunction= new QWidget(this);
			QGridLayout *pLightingTransferFunctionLayout = new QGridLayout(pLightingTransferFunction);
			pLightingTransferFunctionLayout->setSpacing(10);
			pLightingTransferFunctionLayout->setColumnStretch(1,1);

			m_pBackgroundLightingComboBox = new QComboBox(pLightingTransferFunction);
			QLabel *pBackgroundLightingLabel = new QLabel("Background",pLightingTransferFunction);
			pBackgroundLightingLabel->setBuddy(m_pBackgroundLightingComboBox);
			m_pBackgroundLightingComboBox->addItem("Default");
			m_pBackgroundLightingComboBox->addItem("Silhouette");
			m_pBackgroundLightingComboBox->addItem("Cartoon");
			m_pBackgroundLightingComboBox->addItem("Metal");
			pLightingTransferFunctionLayout->addWidget(pBackgroundLightingLabel,0,0);
			pLightingTransferFunctionLayout->addWidget(m_pBackgroundLightingComboBox,0,1);
			connect(m_pBackgroundLightingComboBox,SIGNAL(activated(int)),SLOT(OnBackgroundLightingComboBoxActivated(int)));

			m_pGhostLightingComboBox = new QComboBox(pLightingTransferFunction);
			QLabel *pGhostLightingLabel = new QLabel("Ghost",pLightingTransferFunction);
			pGhostLightingLabel->setBuddy(m_pGhostLightingComboBox);
			m_pGhostLightingComboBox->addItem("Default");
			m_pGhostLightingComboBox->addItem("Silhouette");
			m_pGhostLightingComboBox->addItem("Cartoon");
			m_pGhostLightingComboBox->addItem("Metal");
			pLightingTransferFunctionLayout->addWidget(pGhostLightingLabel,1,0);
			pLightingTransferFunctionLayout->addWidget(m_pGhostLightingComboBox,1,1);
			connect(m_pGhostLightingComboBox,SIGNAL(activated(int)),SLOT(OnGhostLightingComboBoxActivated(int)));

			m_pSelectionLightingComboBox = new QComboBox(pLightingTransferFunction);
			QLabel *pSelectionLightingLabel = new QLabel("Selection",pLightingTransferFunction);
			pSelectionLightingLabel->setBuddy(m_pSelectionLightingComboBox);
			m_pSelectionLightingComboBox->addItem("Default");
			m_pSelectionLightingComboBox->addItem("Silhouette");
			m_pSelectionLightingComboBox->addItem("Cartoon");
			m_pSelectionLightingComboBox->addItem("Metal");
			pLightingTransferFunctionLayout->addWidget(pSelectionLightingLabel,2,0);
			pLightingTransferFunctionLayout->addWidget(m_pSelectionLightingComboBox,2,1);
			connect(m_pSelectionLightingComboBox,SIGNAL(activated(int)),SLOT(OnSelectionLightingComboBoxActivated(int)));

			m_pSidebar->addItem(pLightingTransferFunction,"Lighting transfer function");
		}

		{
			QWidget *pCutaway= new QWidget(this);
			QGridLayout *pCutawayLayout = new QGridLayout(pCutaway);
			pCutawayLayout->setSpacing(10);

			m_pGhostCutawayCheckBox = new QCheckBox(pCutaway);
			m_pGhostCutawaySlider = new QSlider(Qt::Horizontal,pCutaway);
			QLabel *pGhostCutawayLabel = new QLabel("Ghost",pCutaway);
			pGhostCutawayLabel->setBuddy(m_pGhostCutawayCheckBox);
			pCutawayLayout->addWidget(pGhostCutawayLabel,0,0);
			pCutawayLayout->addWidget(m_pGhostCutawayCheckBox,0,1);
			pCutawayLayout->addWidget(m_pGhostCutawaySlider,0,2);
			connect(m_pGhostCutawayCheckBox,SIGNAL(toggled(bool)),SLOT(OnGhostCutawayCheckBoxToggled(bool)));
			connect(m_pGhostCutawaySlider,SIGNAL(valueChanged(int)),SLOT(OnGhostCutawaySliderValueChanged(int)));

			m_pSelectionCutawayCheckBox = new QCheckBox(pCutaway);
			m_pSelectionCutawaySlider = new QSlider(Qt::Horizontal,pCutaway);
			QLabel *pSelectionCutawayLabel = new QLabel("Selection",pCutaway);
			pSelectionCutawayLabel->setBuddy(m_pSelectionCutawayCheckBox);
			pCutawayLayout->addWidget(pSelectionCutawayLabel,1,0);
			pCutawayLayout->addWidget(m_pSelectionCutawayCheckBox,1,1);
			pCutawayLayout->addWidget(m_pSelectionCutawaySlider,1,2);
			connect(m_pSelectionCutawayCheckBox,SIGNAL(toggled(bool)),SLOT(OnSelectionCutawayCheckBoxToggled(bool)));
			connect(m_pSelectionCutawaySlider,SIGNAL(valueChanged(int)),SLOT(OnSelectionCutawaySliderValueChanged(int)));

			m_pSidebar->addItem(pCutaway,"Cut-away views");
		}

		{
			QWidget *pViewManagement= new QWidget(this);
			QGridLayout *pViewManagementLayout = new QGridLayout(pViewManagement);
			pViewManagementLayout->setSpacing(10);
			pViewManagementLayout->setColumnStretch(0,1);

			m_pLabelComboBox = new QComboBox(pViewManagement);
			m_pLabelComboBox->setFocusPolicy(Qt::StrongFocus);
			m_pLabelComboBox->setEditable(true);
			m_pLabelComboBox->setAutoCompletion(true);
			m_pLabelComboBox->setDuplicatesEnabled(true);
			m_pLabelComboBox->setInsertPolicy(QComboBox::NoInsert);
			connect(m_pLabelComboBox,SIGNAL(activated(int)),SLOT(OnLabelComboBoxActivated(int)));
			connect(m_pLabelComboBox,SIGNAL(textChanged(const QString &)),SLOT(OnLabelComboBoxTextChanged(const QString &)));

			//QLabel *pLabelLabel = new QLabel(m_pLabelComboBox,"Label",pViewManagement);
			//pViewManagementLayout->addWidget(pLabelLabel,0,0);
			pViewManagementLayout->addWidget(m_pLabelComboBox,0,0);

			QToolButton *pLabelAddButton = new QToolButton(pViewManagement);
			pLabelAddButton->setIcon(QPixmap(add_xpm));
			pLabelAddButton->setAutoRaise(true);
			pViewManagementLayout->addWidget(pLabelAddButton,0,1);
			connect(pLabelAddButton,SIGNAL(clicked()),SLOT(OnLabelAddButtonClicked()));

			QToolButton *pLabelRemoveButton = new QToolButton(pViewManagement);
			pLabelRemoveButton->setIcon(QPixmap(remove_xpm));
			pLabelRemoveButton->setAutoRaise(true);
			pViewManagementLayout->addWidget(pLabelRemoveButton,0,2);
			connect(pLabelRemoveButton,SIGNAL(clicked()),SLOT(OnLabelRemoveButtonClicked()));

			QWidget *pAnnotations = new QWidget(pViewManagement);
			QGridLayout *pAnnotationsLayout = new QGridLayout(pAnnotations);
			pAnnotationsLayout->setColumnStretch(1,1);
			pAnnotationsLayout->setSpacing(10);

			m_pPinCheckBox = new QCheckBox(pAnnotations);
			QLabel *pPinLabel = new QLabel("Pin selection",pAnnotations);
			pPinLabel->setBuddy(m_pPinCheckBox);
			pAnnotationsLayout->addWidget(pPinLabel,1,0);
			pAnnotationsLayout->addWidget(m_pPinCheckBox,1,1);

			pViewManagementLayout->addWidget(pAnnotations,1,0,1,2);

			connect(m_pPinCheckBox,SIGNAL(toggled(bool)),SLOT(OnPinCheckBoxToggled(bool)));

			m_pSidebar->addItem(pViewManagement,"View management");
		}

		setCentralWidget(pSplitter);
	};

	virtual ~QMainframe()
	{
	};

	void initialize(Environment *pEnvironment)
	{
		m_pEnvironment = pEnvironment;

		m_pLabelComboBox->blockSignals(true);
		m_pLabelComboBox->clear();

		for (unsigned int i=0;i<GetEnvironment().GetSelectionCount();i++)
			m_pLabelComboBox->addItem(GetEnvironment().GetSelectionLabel(i).c_str(),int(i));

		m_pLabelComboBox->blockSignals(false);
		m_pLabelComboBox->setCurrentIndex(GetEnvironment().GetActiveSelection());

		initialize(m_pBackgroundGradientWidget,GetEnvironment().GetBackgroundSelectionColorTransferFunction().Get(0));
		initialize(m_pGhostGradientWidget,GetEnvironment().GetGhostSelectionColorTransferFunction().Get(0));
		initialize(m_pSelectionGradientWidget,GetEnvironment().GetBackgroundSelectionColorTransferFunction().Get(1));

		m_pGhostCutawayCheckBox->setChecked(GetEnvironment().IsGhostCutoutEnabled());
		m_pGhostCutawaySlider->setEnabled(GetEnvironment().IsGhostCutoutEnabled());
		m_pGhostCutawaySlider->setValue(int(GetEnvironment().GetGhostGhosting()*float(m_pGhostCutawaySlider->maximum()-m_pGhostCutawaySlider->minimum()) + float(m_pGhostCutawaySlider->minimum())));

		m_pSelectionCutawayCheckBox->setChecked(GetEnvironment().IsSelectionCutoutEnabled());
		m_pSelectionCutawaySlider->setEnabled(GetEnvironment().IsGhostCutoutEnabled());
		m_pSelectionCutawaySlider->setValue(int(GetEnvironment().GetSelectionGhosting()*float(m_pSelectionCutawaySlider->maximum()-m_pSelectionCutawaySlider->minimum()) + float(m_pSelectionCutawaySlider->minimum())));

		OnBackgroundSelectionOperatorComboBoxActivated((int)GetEnvironment().GetBackgroundSelectionColorTransferFunction().GetOperator());
		OnGhostSelectionOperatorComboBoxActivated((int)GetEnvironment().GetGhostSelectionColorTransferFunction().GetOperator());

		OnBackgroundLightingComboBoxActivated(0);
		OnGhostLightingComboBoxActivated(0);
		OnSelectionLightingComboBoxActivated(0);
	};

	void initialize(QGradientWidget *pGradientWidget, const std::map<float,Color> & mapValues)
	{
		std::map<float,QColor> mapColor;
		std::map<float,float> mapAlpha;

		for (std::map<float,Color>::const_iterator j = mapValues.begin(); j != mapValues.end(); j++)
		{
			std::map<float,Color>::const_iterator i = j;
			--i;

			std::map<float,Color>::const_iterator k = j;
			++k;

			const bool bFirst = (j == mapValues.begin());
			const bool bLast =  (k == mapValues.end());

			if (bFirst && !bLast)
			{
				if (!( qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue()) == qRgb(k->second.GetRed(),k->second.GetGreen(),k->second.GetBlue())))
					mapColor[j->first] = qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue());

				if (!(j->second.GetAlpha() == k->second.GetAlpha()))
					mapAlpha[j->first] = j->second.GetNormalizedAlpha();
			}
			else if (!bFirst && bLast)
			{
				if (!(qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue()) == qRgb(i->second.GetRed(),i->second.GetGreen(),i->second.GetBlue())))
					mapColor[i->first] = qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue());

				if (!(j->second.GetAlpha() == i->second.GetAlpha()))
					mapAlpha[j->first] = j->second.GetNormalizedAlpha();
			}
			else if (bFirst && bLast)
			{
				mapColor[j->first] = qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue());
				mapAlpha[j->first] = j->second.GetNormalizedAlpha();
			}
			else
			{
				const float fD = (j->first - i->first) / (k->first - i->first);
				const float fRed = k->second.GetNormalizedRed() * fD + i->second.GetNormalizedRed() * (1.0f - fD);
				const float fGreen = k->second.GetNormalizedGreen() * fD + i->second.GetNormalizedGreen() * (1.0f - fD);
				const float fBlue = k->second.GetNormalizedBlue() * fD + i->second.GetNormalizedBlue() * (1.0f - fD);
				const float fAlpha = k->second.GetNormalizedAlpha() * fD + i->second.GetNormalizedAlpha() * (1.0f - fD);

				const Color colColorMiddle(fRed,fGreen,fBlue,fAlpha);

				if (!(qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue()) == qRgb(i->second.GetRed(),i->second.GetGreen(),i->second.GetBlue()) &&	qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue()) == qRgb(k->second.GetRed(),k->second.GetGreen(),k->second.GetBlue())))
					if (qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue()) != qRgb(colColorMiddle.GetRed(),colColorMiddle.GetGreen(),colColorMiddle.GetBlue()))
						mapColor[j->first] = qRgb(j->second.GetRed(),j->second.GetGreen(),j->second.GetBlue());

				if (!(j->second.GetAlpha() == i->second.GetAlpha() && j->second.GetAlpha() == k->second.GetAlpha()))
					if (j->second.GetAlpha() != colColorMiddle.GetAlpha())
						mapAlpha[j->first] = j->second.GetNormalizedAlpha();
			}
		}

		if (mapAlpha.size() == 0)
			mapAlpha[0.5f] = mapValues.begin()->second.GetNormalizedAlpha();

		if (mapColor.size() == 0)
			mapColor[0.5f] = qRgb(mapValues.begin()->second.GetRed(),mapValues.begin()->second.GetGreen(),mapValues.begin()->second.GetBlue());

		pGradientWidget->blockSignals(true);
		pGradientWidget->removeAllColorButtons();
		pGradientWidget->removeAllAlphaButtons();
		pGradientWidget->blockSignals(false);

		for (std::map<float,QColor>::const_iterator i=mapColor.begin(); i != mapColor.end(); i++)
			pGradientWidget->addColorButton(i->first,i->second);

		for (std::map<float,float>::const_iterator i=mapAlpha.begin(); i != mapAlpha.end(); i++)
			pGradientWidget->addAlphaButton(i->first,i->second);

	};

	Environment & GetEnvironment()
	{
		return *m_pEnvironment;
	}
	
	QSidebar * GetSidebar()
	{
		return m_pSidebar;
	};

	QViewer * GetViewer()
	{
		return m_pViewer;
	};

protected slots:

	void OnBackgroundGhostButtonToggled(bool bOn)
	{
		if (bOn)
		{
			m_pOperatorComboBoxWidgetStack->setCurrentWidget(m_pGhostSelectionOperatorComboBox);
			m_pGradientWidgetStack->setCurrentWidget(m_pGhostGradientWidget);
			m_pGradientAreaWidgetStack->setCurrentWidget(m_pGhostSelectionGradientAreaWidget);
		}
		else
		{
			m_pOperatorComboBoxWidgetStack->setCurrentWidget(m_pBackgroundSelectionOperatorComboBox);
			m_pGradientWidgetStack->setCurrentWidget(m_pBackgroundGradientWidget);
			m_pGradientAreaWidgetStack->setCurrentWidget(m_pBackgroundSelectionGradientAreaWidget);
		}
	};

	void OnSelectionGradientWidgetChanged()
	{
		GetEnvironment().GetBackgroundSelectionColorTransferFunction().clear(1);
		GetEnvironment().GetGhostSelectionColorTransferFunction().clear(1);

		QGradientWidget *pGradientWidget = (QGradientWidget*) sender();
		const QWidgetGradient & graGradient  = pGradientWidget->GetGradient();
		const std::map<float,QRgb> & mapValues = graGradient.Get();

		for (std::map<float,QRgb>::const_iterator i=mapValues.begin(); i != mapValues.end(); i++)
		{
			GetEnvironment().GetBackgroundSelectionColorTransferFunction().add(1,i->first,Color(qRed(i->second),qGreen(i->second),qBlue(i->second),qAlpha(i->second)));
			GetEnvironment().GetGhostSelectionColorTransferFunction().add(1,i->first,Color(qRed(i->second),qGreen(i->second),qBlue(i->second),qAlpha(i->second)));
		}

		updateEnvironment();
	};

	void OnBackgroundGradientWidgetChanged()
	{
		GetEnvironment().GetBackgroundSelectionColorTransferFunction().clear(0);

		QGradientWidget *pGradientWidget = (QGradientWidget*) sender();
		const QWidgetGradient & graGradient  = pGradientWidget->GetGradient();
		const std::map<float,QRgb> & mapValues = graGradient.Get();

		for (std::map<float,QRgb>::const_iterator i=mapValues.begin(); i != mapValues.end(); i++)
			GetEnvironment().GetBackgroundSelectionColorTransferFunction().add(0,i->first,Color(qRed(i->second),qGreen(i->second),qBlue(i->second),qAlpha(i->second)));

		updateEnvironment();
	};

	void OnGhostGradientWidgetChanged()
	{
		GetEnvironment().GetGhostSelectionColorTransferFunction().clear(0);

		QGradientWidget *pGradientWidget = (QGradientWidget*) sender();
		const QWidgetGradient & graGradient  = pGradientWidget->GetGradient();
		const std::map<float,QRgb> & mapValues = graGradient.Get();

		for (std::map<float,QRgb>::const_iterator i=mapValues.begin(); i != mapValues.end(); i++)
			GetEnvironment().GetGhostSelectionColorTransferFunction().add(0,i->first,Color(qRed(i->second),qGreen(i->second),qBlue(i->second),qAlpha(i->second)));

		updateEnvironment();
	};

	void OnBackgroundSelectionOperatorComboBoxActivated(int iIndex)
	{
		m_pBackgroundSelectionGradientAreaWidget->SetOperator((Compositor::Operator) iIndex);
		GetEnvironment().GetBackgroundSelectionColorTransferFunction().SetOperator((Compositor::Operator) iIndex);
		updateEnvironment();
	};

	void OnGhostSelectionOperatorComboBoxActivated(int iIndex)
	{
		m_pGhostSelectionGradientAreaWidget->SetOperator((Compositor::Operator) iIndex);
		GetEnvironment().GetGhostSelectionColorTransferFunction().SetOperator((Compositor::Operator) iIndex);
		updateEnvironment();
	};

	void OnBackgroundLightingComboBoxActivated(int iIndex)
	{
		updateLightingTransferFunction(GetEnvironment().GetBackgroundLightingTransferFunction(),unsigned int(iIndex));
		updateEnvironment();
	};

	void OnGhostLightingComboBoxActivated(int iIndex)
	{
		updateLightingTransferFunction(GetEnvironment().GetGhostLightingTransferFunction(),unsigned int(iIndex));
		updateEnvironment();
	};

	void OnSelectionLightingComboBoxActivated(int iIndex)
	{
		updateLightingTransferFunction(GetEnvironment().GetSelectionLightingTransferFunction(),unsigned int(iIndex));
		updateEnvironment();
	};

	void updateLightingTransferFunction(LightingTransferFunction &ligFunction, const unsigned int uSetting)
	{
		switch(uSetting)
		{
		case 0:
			ligFunction.clear();
			ligFunction.addAmbient(0.125f);
			ligFunction.addDiffuse(0.75f);
			ligFunction.addSpecular(0.5f,32.0f);
			break;

		case 1:
			ligFunction.clear();
			ligFunction.addAmbient(0.125f);
			ligFunction.addDiffuse(0.875f);
			ligFunction.addSpecular(0.66f,32.0f);
			ligFunction.addSilhouette();
			break;

		case 2:
			ligFunction.clear();
			ligFunction.addAmbient(0.125f);
			ligFunction.addDiffuse(0.875f);
			ligFunction.addSpecular(0.75f,64.0f);
			ligFunction.catoonize();
			ligFunction.addSilhouette();
			break;

		case 3:
			ligFunction.clear();
			ligFunction.addAmbient(0.125f);
			ligFunction.addMetal();
			ligFunction.addSpecular(1.5f,32.0f);
			ligFunction.addSilhouette();
			break;
		}

		QImage imgAmbient(512,512,QImage::Format_ARGB32);
/*
		QImage imgDiffuse(512,512,32);
		imgDiffuse.setAlphaBuffer(true);

		QImage imgSpecular(512,512,32);
		imgSpecular.setAlphaBuffer(true);

		QImage imgAlpha(512,512,32);
		imgAlpha.setAlphaBuffer(true);
*/
		for (unsigned int j=0;j<512;j++)
		{
			for (unsigned int i=0;i<512;i++)
			{
				imgAmbient.setPixel(i,511-j,qRgba(ligFunction.Get(i,j).GetRed(),ligFunction.Get(i,j).GetGreen(),ligFunction.Get(i,j).GetBlue(),ligFunction.Get(i,j).GetAlpha()));

				/*
				imgAmbient.setPixel(i,511-j,qRgba(255,0,0,ligFunction.Get(i,j).GetRed()));
				imgDiffuse.setPixel(i,511-j,qRgba(0,255,0,ligFunction.Get(i,j).GetGreen()));
				imgSpecular.setPixel(i,511-j,qRgba(0,0,255,ligFunction.Get(i,j).GetBlue()));
				imgAlpha.setPixel(i,511-j,qRgba(0,0,0,ligFunction.Get(i,j).GetAlpha()));
				*/
			}
		}

		imgAmbient.save("c:/ltf.png","PNG");
		/*
		imgDiffuse.save("c:/ltf_diffuse.png","PNG");
		imgSpecular.save("c:/ltf_specular.png","PNG");
		imgAlpha.save("c:/ltf_alpha.png","PNG");*/
	};

	void OnGhostCutawayCheckBoxToggled(bool bOn)
	{
		m_pGhostCutawaySlider->setEnabled(bOn);
		GetEnvironment().SetGhostCutoutEnabled(bOn);
		updateEnvironment();
	};

	void OnSelectionCutawayCheckBoxToggled(bool bOn)
	{
		m_pSelectionCutawaySlider->setEnabled(bOn);
		GetEnvironment().SetSelectionCutoutEnabled(bOn);
		updateEnvironment();
	};

	void OnGhostCutawaySliderValueChanged(int iValue)
	{
		GetEnvironment().SetGhostGhosting(float(iValue-m_pGhostCutawaySlider->minimum()) / float(m_pGhostCutawaySlider->maximum()-m_pGhostCutawaySlider->minimum()));
		updateEnvironment();
	};

	void OnSelectionCutawaySliderValueChanged(int iValue)
	{
		GetEnvironment().SetSelectionGhosting(float(iValue-m_pSelectionCutawaySlider->minimum()) / float(m_pSelectionCutawaySlider->maximum()-m_pSelectionCutawaySlider->minimum()));
		updateEnvironment();
	};

	void OnPinCheckBoxToggled(bool bOn)
	{
		GetEnvironment().SetSelectionPinEnabled(bOn);
		updateEnvironment();
	};

	void OnLabelComboBoxTextChanged(const QString & strString)
	{
		GetEnvironment().SetSelectionLabel(unsigned int(m_pLabelComboBox->currentIndex()),strString.toAscii().data());
		m_pLabelComboBox->setItemText(m_pLabelComboBox->currentIndex(),strString);
		updateEnvironment();
	};

	void OnLabelComboBoxActivated(int iIndex)
	{
		GetEnvironment().SetActiveSelection(unsigned int(iIndex));
		updateEnvironment();
	};

	void OnLabelAddButtonClicked()
	{	
		GetEnvironment().addSelection();
		m_pLabelComboBox->addItem(GetEnvironment().GetSelectionLabel().c_str(),int(GetEnvironment().GetActiveSelection()));	
		m_pLabelComboBox->setCurrentIndex(int(GetEnvironment().GetActiveSelection()));
		updateEnvironment();
	};

	void OnLabelRemoveButtonClicked()
	{
		GetEnvironment().SetActiveSelection(unsigned int(m_pLabelComboBox->currentIndex()));
		GetEnvironment().removeSelection();
		m_pLabelComboBox->removeItem(m_pLabelComboBox->currentIndex());
		m_pLabelComboBox->setCurrentIndex(GetEnvironment().GetActiveSelection());
		updateEnvironment();
	};

	void OnUpdateTimer()
	{
		GetEnvironment().SetQuality(Environment::QUALITY_HIGH);
		GetEnvironment().update();
	};

	void OnLoad()
	{
		const QString strFilename = QFileDialog::getOpenFileName(this,QString(),QString(),"Session files (*.xml)");

		if (strFilename != QString::null)
			load(strFilename.toAscii().data());
	};

	void OnSave()
	{
		QString strFilename = QFileDialog::getSaveFileName(this,QString(),QString(),"Session files (*.xml)");

		if (strFilename != QString::null)
			save(strFilename.toAscii().data());
	};

	void OnImport()
	{
		std::string strFilter;
		QString strSelected;
		
		for (ImporterPlugin::Iterator i("*.dll");!i.IsAtEnd();++i)
		{
			strFilter += (i.GetDescription() + ";;");
		}

		const QString strFilename = QFileDialog::getOpenFileName(this,QString(),QString(),strFilter.c_str(),&strSelected);

		if (strFilename != QString::null)
		{
			for (ImporterPlugin::Iterator i("*.dll");!i.IsAtEnd();++i)
			{
				if (i.GetDescription() == std::string(strSelected.toAscii().data()))
				{
					ImporterPlugin impPlugin(*m_pEnvironment,*i);
					import(impPlugin,strFilename.toAscii().data());
				}
			}
		}
	};

	void OnExport()
	{
		std::string strFilter;
		QString strSelected;

		for (ExporterPlugin::Iterator i("*.dll");!i.IsAtEnd();++i)
		{
			strFilter += (i.GetDescription() + ";;");
		}

		QString strFilename = QFileDialog::getSaveFileName(this,QString(),QString(),strFilter.c_str(),&strSelected);

		if (strFilename != QString::null)
		{
			for (ExporterPlugin::Iterator i("*.dll");!i.IsAtEnd();++i)
			{
				if (i.GetDescription() == std::string(strSelected.toAscii().data()))
				{
					int iIndex = strSelected.indexOf("*.",-1);

					if (iIndex != -1)
					{
						QString strExtension = strSelected.mid(iIndex+1).toLower();
						iIndex = strExtension.indexOf(")");

						if (iIndex != -1)
						{
							strExtension = strExtension.left(iIndex);

							if (strFilename.toLower().indexOf(strExtension) != strFilename.length()-strExtension.length())
							{
								if (strFilename.right(1) == ".")
									strFilename += strExtension.right(strExtension.length()-1);
								else
									strFilename += strExtension;
							}
						}
					}

					ExporterPlugin impPlugin(*m_pEnvironment,*i);			
					export(impPlugin,strFilename.toAscii().data());
				}
			}
		}
	};

private:

	virtual void updateEnvironment()
	{
		GetEnvironment().SetQuality(Environment::QUALITY_LOW);
		GetEnvironment().update();

		m_pUpdateTimer->setSingleShot(true);
		m_pUpdateTimer->start(100);
	};

	virtual const bool import(ImporterPlugin &impPlugin, const std::string &strFilename)
	{
		impPlugin.initialize();

		if (impPlugin.IsInitialized())
		{
			if (impPlugin.IsAccepted(strFilename))
			{
				QFileInfo fi(strFilename.c_str());
				const std::string strDisplayName(fi.fileName().toAscii().data());

				QProgressDialog dlg(this,Qt::SplashScreen | Qt::MSWindowsFixedSizeDialogHint);
				dlg.setModal(true);
				dlg.setLabelText((std::string("Importing ") + strDisplayName + std::string(" ...")).c_str());
				dlg.setCancelButton(NULL);
				dlg.show();

				// Give Qt the chance to update the GUI before loading starts
				qApp->processEvents();

				QImportThread thread(impPlugin,strFilename);	
				thread.start();

				while (thread.isRunning())
				{
					dlg.setValue(int(impPlugin.GetProgress()*100.0f));
					qApp->processEvents();
					//dlg.update();
					//qApp->processEvents();
				}
				
				return true;
			}
		}

		return false;
	};

	virtual const bool export(ExporterPlugin &expPlugin, const std::string &strFilename)
	{
		expPlugin.initialize();

		if (expPlugin.IsInitialized())
		{
			if (expPlugin.IsAccepted(strFilename))
			{
				QFileInfo fi(strFilename.c_str());
				const std::string strDisplayName(fi.fileName().toAscii().data());

				QProgressDialog dlg(this,Qt::SplashScreen | Qt::MSWindowsFixedSizeDialogHint);
				dlg.setModal(true);
				dlg.setLabelText((std::string("Exporting ") + strDisplayName + std::string(" ...")).c_str());
				dlg.setCancelButton(NULL);
				dlg.show();

				// Give Qt the chance to update the GUI before loading starts
				qApp->processEvents();

				QExportThread thread(expPlugin,strFilename);	
				thread.start();

				while (thread.isRunning())
				{
					dlg.setValue(int(expPlugin.GetProgress()*100.0f));
					qApp->processEvents();
					//dlg.update();
					//qApp->processEvents();
				}

				return true;
			}
		}

		return false;
	};

	void save(const std::string & strFilename)
	{
		//std::ofstream os(strFilename.c_str());
		QFileInfo fi(strFilename.c_str());

		const std::string strDirectory = std::string(fi.absolutePath().toAscii().data()) + std::string("/");
		const std::string strBaseFilename = std::string(fi.baseName().toAscii().data());
		const std::string strProject = strDirectory + strBaseFilename + ".xml";

		Element eleElement = (*m_pEnvironment);

		ExporterPlugin expDataVolume(*m_pEnvironment,"plugin_exporter_datavolumebrk.dll");
		ExporterPlugin expSelectionVolume(*m_pEnvironment,"plugin_exporter_selectionvolumebrk.dll");

		const unsigned int uActiveData = GetEnvironment().GetActiveData();

		for (Element::VolatileIterator i = eleElement[ Element("data") ]; !i.IsAtEnd(); ++i)
		{
			Element & eleData = (Element &) i;
			const unsigned int uIndex = i[ Attribute("index") ];

			const std::string strDataVolume = strBaseFilename + "." + toString(uIndex) + ".datavolume.brk";
			eleData[ Element("filename") = strDataVolume ];

			GetEnvironment().SetActiveData(uIndex);
			export(expDataVolume,strDirectory + strDataVolume);
		}

		GetEnvironment().SetActiveData(uActiveData);

		const unsigned int uActiveSelection = GetEnvironment().GetActiveSelection();

		for (Element::VolatileIterator i = eleElement[ Element("selection") ]; !i.IsAtEnd(); ++i)
		{
			Element & eleSelection = (Element &) i;
			const unsigned int uIndex = i[ Attribute("index") ];

			const std::string strSelectionVolume= strBaseFilename + "." + toString(uIndex) +".selectionvolume.brk";
			eleSelection[ Element("filename") = strSelectionVolume ];

			GetEnvironment().SetActiveSelection(uIndex);
			export(expSelectionVolume,strDirectory + strSelectionVolume);
		}

		GetEnvironment().SetActiveSelection(uActiveSelection);

		std::ofstream os(strProject.c_str());
		os << eleElement;
	};

	void load(const std::string & strFilename)
	{
		QFileInfo fi(strFilename.c_str());
		const std::string strDirectory = std::string(fi.absolutePath().toAscii().data()) + std::string("/");

		Element eleElement;
		std::ifstream is(strFilename.c_str());

		is >> eleElement;		
		(*m_pEnvironment) = eleElement;

		ImporterPlugin impDataVolume(*m_pEnvironment,"plugin_importer_datavolumebrk.dll");
		ImporterPlugin impSelectionVolume(*m_pEnvironment,"plugin_importer_selectionvolumebrk.dll");

		const unsigned int uActiveData = GetEnvironment().GetActiveData();

		for (Element::VolatileIterator i = eleElement[ Element("data") ]; !i.IsAtEnd(); ++i)
		{
			const Element & eleData = (const Element &) i;
			const unsigned int uIndex = i[ Attribute("index") ];

			const std::string strDataFilename = (const Element &) eleData[ Element("filename") ];

			GetEnvironment().SetActiveData(uIndex);
			import(impDataVolume,strDirectory + strDataFilename);
		}

		GetEnvironment().SetActiveData(uActiveData);

		const unsigned int uActiveSelection = GetEnvironment().GetActiveSelection();

		for (Element::VolatileIterator i = eleElement[ Element("selection") ]; !i.IsAtEnd(); ++i)
		{
			const Element & eleSelection = (const Element &) i;
			const unsigned int uIndex = i[ Attribute("index") ];

			const std::string strSelectionFilename = (const Element &) eleSelection[ Element("filename") ];

			GetEnvironment().SetActiveSelection(uIndex);
			import(impSelectionVolume,strDirectory + strSelectionFilename);
		}

		GetEnvironment().SetActiveData(uActiveSelection);
		initialize(m_pEnvironment);
	};

private:

	Environment *m_pEnvironment;
	QSidebar *m_pSidebar;
	QViewer *m_pViewer;
	QTimer *m_pUpdateTimer;

	QGradientWidget *m_pBackgroundGradientWidget;
	QGradientWidget *m_pGhostGradientWidget;
	QGradientWidget *m_pSelectionGradientWidget;
	QGradientArea *m_pBackgroundSelectionGradientAreaWidget;
	QGradientArea *m_pGhostSelectionGradientAreaWidget;

	QComboBox *m_pBackgroundSelectionOperatorComboBox;
	QComboBox *m_pGhostSelectionOperatorComboBox;

	QStackedWidget *m_pOperatorComboBoxWidgetStack;
	QStackedWidget *m_pGradientWidgetStack;
	QStackedWidget *m_pGradientAreaWidgetStack;

	QToolButton *m_pBackgroundGhostButton;

	QComboBox *m_pBackgroundLightingComboBox;
	QComboBox *m_pGhostLightingComboBox;
	QComboBox *m_pSelectionLightingComboBox;

	QCheckBox *m_pGhostCutawayCheckBox;
	QCheckBox *m_pSelectionCutawayCheckBox;

	QSlider *m_pGhostCutawaySlider;
	QSlider *m_pSelectionCutawaySlider;

	QComboBox *m_pLabelComboBox;
	QCheckBox *m_pPinCheckBox;
};