#include "MainWindow.hpp"
#include "ui_mainwindow.h"

#include <iostream>

#include <QCloseEvent>
#include <QTimer>

#include <QInputDialog>
#include <QMessageBox>

#include "Editor.hpp"
#include "ColoredTabWidget.hpp"
#include "AddPropertyDialog.hpp"

sh::MainWindow::MainWindow(QWidget *parent)
	: QMainWindow(parent)
	, ui(new Ui::MainWindow)
	, mRequestShowWindow(false)
	, mRequestExit(false)
	, mIgnoreGlobalSettingChange(false)
	, mIgnoreConfigurationChange(false)
	, mIgnoreMaterialChange(false)
	, mIgnoreMaterialPropertyChange(false)
{
	ui->setupUi(this);

	QTimer *timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), this, SLOT(onIdle()));
	timer->start(50);

	QList<int> sizes;
	sizes << 250;
	sizes << 550;
	ui->splitter->setSizes(sizes);

	mMaterialModel = new QStringListModel(this);

	mMaterialProxyModel = new QSortFilterProxyModel(this);
	mMaterialProxyModel->setSourceModel(mMaterialModel);
	mMaterialProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
	mMaterialProxyModel->setDynamicSortFilter(true);
	mMaterialProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);

	ui->materialList->setModel(mMaterialProxyModel);
	ui->materialList->setSelectionMode(QAbstractItemView::SingleSelection);
	ui->materialList->setEditTriggers(QAbstractItemView::NoEditTriggers);
	ui->materialList->setAlternatingRowColors(true);

	connect(ui->materialList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
			this,								SLOT(onMaterialSelectionChanged(QModelIndex,QModelIndex)));

	mMaterialPropertyModel = new QStandardItemModel(0, 2, this);
	mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
	mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));
	connect(mMaterialPropertyModel,	SIGNAL(itemChanged(QStandardItem*)),
			this,					SLOT(onMaterialPropertyChanged(QStandardItem*)));

	mMaterialSortModel = new PropertySortModel(this);
	mMaterialSortModel->setSourceModel(mMaterialPropertyModel);
	mMaterialSortModel->setDynamicSortFilter(true);
	mMaterialSortModel->setSortCaseSensitivity(Qt::CaseInsensitive);

	ui->materialView->setModel(mMaterialSortModel);
	ui->materialView->setContextMenuPolicy(Qt::CustomContextMenu);
	ui->materialView->setAlternatingRowColors(true);
	ui->materialView->setSortingEnabled(true);
	connect(ui->materialView,	SIGNAL(customContextMenuRequested(QPoint)),
				this,			SLOT(onContextMenuRequested(QPoint)));

	mGlobalSettingsModel = new QStandardItemModel(0, 2, this);
	mGlobalSettingsModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
	mGlobalSettingsModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));
	connect(mGlobalSettingsModel,	SIGNAL(itemChanged(QStandardItem*)),
			this,					SLOT(onGlobalSettingChanged(QStandardItem*)));

	ui->globalSettingsView->setModel(mGlobalSettingsModel);
	ui->globalSettingsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
	ui->globalSettingsView->verticalHeader()->hide();
	ui->globalSettingsView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
	ui->globalSettingsView->setSelectionMode(QAbstractItemView::SingleSelection);

	ui->configurationList->setSelectionMode(QAbstractItemView::SingleSelection);
	ui->configurationList->setEditTriggers(QAbstractItemView::NoEditTriggers);
	connect(ui->configurationList,	SIGNAL(currentTextChanged(QString)),
							this,	SLOT(onConfigurationSelectionChanged(QString)));

	mConfigurationModel = new QStandardItemModel(0, 2, this);
	mConfigurationModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
	mConfigurationModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));
	connect(mConfigurationModel,	SIGNAL(itemChanged(QStandardItem*)),
			this,					SLOT(onConfigurationChanged(QStandardItem*)));

	ui->configurationView->setModel(mConfigurationModel);
	ui->configurationView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
	ui->configurationView->verticalHeader()->hide();
	ui->configurationView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
	ui->configurationView->setSelectionMode(QAbstractItemView::SingleSelection);
}

sh::MainWindow::~MainWindow()
{
	delete ui;
}

void sh::MainWindow::closeEvent(QCloseEvent *event)
{
	this->hide();
	event->ignore();
}

void sh::MainWindow::onIdle()
{
	if (mRequestShowWindow)
	{
		mRequestShowWindow = false;
		show();
	}

	if (mRequestExit)
	{
		QApplication::exit();
		return;
	}

	boost::mutex::scoped_lock lock(mSync->mUpdateMutex);


	mIgnoreMaterialChange = true;
	QString selected;

	QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
	if (selectedIndex.isValid())
		selected = mMaterialModel->data(selectedIndex, Qt::DisplayRole).toString();

	QStringList list;

	for (std::vector<std::string>::const_iterator it = mState.mMaterialList.begin(); it != mState.mMaterialList.end(); ++it)
	{
		list.push_back(QString::fromStdString(*it));
	}

	if (mMaterialModel->stringList() != list)
	{
		mMaterialModel->setStringList(list);

		// quick hack to keep our selection when the model has changed
		if (!selected.isEmpty())
			for (int i=0; i<mMaterialModel->rowCount(); ++i)
			{
				const QModelIndex& index = mMaterialModel->index(i,0);
				if (mMaterialModel->data(index, Qt::DisplayRole).toString() == selected)
				{
					ui->materialList->setCurrentIndex(index);
					break;
				}
			}
	}
	mIgnoreMaterialChange = false;

	mIgnoreGlobalSettingChange = true;
	for (std::map<std::string, std::string>::const_iterator it = mState.mGlobalSettingsMap.begin();
		 it != mState.mGlobalSettingsMap.end(); ++it)
	{
		QList<QStandardItem *> list = mGlobalSettingsModel->findItems(QString::fromStdString(it->first));
		if (!list.empty()) // item was already there
		{
			// if it changed, set the value column
			if (mGlobalSettingsModel->data(mGlobalSettingsModel->index(list.front()->row(), 1)).toString()
					!= QString::fromStdString(it->second))
			{
				mGlobalSettingsModel->setItem(list.front()->row(), 1, new QStandardItem(QString::fromStdString(it->second)));
			}
		}
		else // item wasn't there; insert new row
		{
			QList<QStandardItem*> toAdd;
			QStandardItem* name = new QStandardItem(QString::fromStdString(it->first));
			name->setFlags(name->flags() &= ~Qt::ItemIsEditable);
			QStandardItem* value = new QStandardItem(QString::fromStdString(it->second));
			toAdd.push_back(name);
			toAdd.push_back(value);
			mGlobalSettingsModel->appendRow(toAdd);
		}
	}
	mIgnoreGlobalSettingChange = false;


	mIgnoreConfigurationChange = true;
	QList<QListWidgetItem*> selected_ = ui->configurationList->selectedItems();
	QString selectedStr;
	if (selected_.size())
		selectedStr = selected_.front()->text();

	ui->configurationList->clear();

	for (std::vector<std::string>::const_iterator it = mState.mConfigurationList.begin(); it != mState.mConfigurationList.end(); ++it)
		ui->configurationList->addItem(QString::fromStdString(*it));

	if (!selectedStr.isEmpty())
		for (int i=0; i<ui->configurationList->count(); ++i)
		{
			if (ui->configurationList->item(i)->text() == selectedStr)
			{
				ui->configurationList->setCurrentItem(ui->configurationList->item(i), QItemSelectionModel::ClearAndSelect);
			}
		}

	mIgnoreConfigurationChange = false;

	if (!mState.mErrors.empty())
	{
		ui->errorLog->append(QString::fromStdString(mState.mErrors));
		mState.mErrors = "";
		QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::Link);
		ui->tabWidget->tabBar()->setTabTextColor(3, color);
	}


	// process query results
	boost::mutex::scoped_lock lock2(mSync->mQueryMutex);
	for (std::vector<Query*>::iterator it = mQueries.begin(); it != mQueries.end();)
	{
		if ((*it)->mDone)
		{
			if (typeid(**it) == typeid(ConfigurationQuery))
				buildConfigurationModel(static_cast<ConfigurationQuery*>(*it));
			else if (typeid(**it) == typeid(MaterialQuery))
				buildMaterialModel(static_cast<MaterialQuery*>(*it));
			else if (typeid(**it) == typeid(MaterialPropertyQuery))
			{
				MaterialPropertyQuery* q = static_cast<MaterialPropertyQuery*>(*it);
				mIgnoreMaterialPropertyChange = true;
				if (getSelectedMaterial().toStdString() == q->mName)
				{
					for (int i=0; i<mMaterialPropertyModel->rowCount(); ++i)
					{
						if (mMaterialPropertyModel->item(i,0)->text() == QString::fromStdString(q->mPropertyName))
						{
							mMaterialPropertyModel->item(i,1)->setText(QString::fromStdString(q->mValue));
							if (mMaterialPropertyModel->item(i,1)->isCheckable())
								mMaterialPropertyModel->item(i,1)->setCheckState ((q->mValue == "true")
										? Qt::Checked : Qt::Unchecked);
						}
					}
				}
				mIgnoreMaterialPropertyChange = false;
			}

			delete *it;
			it = mQueries.erase(it);
		}
		else
			++it;
	}
}

void sh::MainWindow::onMaterialSelectionChanged (const QModelIndex & current, const QModelIndex & previous)
{
	if (mIgnoreMaterialChange)
		return;

	QString name = getSelectedMaterial();
	if (!name.isEmpty())
		requestQuery(new sh::MaterialQuery(name.toStdString()));
}

QString sh::MainWindow::getSelectedMaterial()
{
	QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
	if (!selectedIndex.isValid())
		return QString("");

	return mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString();
}

void sh::MainWindow::onConfigurationSelectionChanged (const QString& current)
{
	if (mIgnoreConfigurationChange)
		return;
	requestQuery(new sh::ConfigurationQuery(current.toStdString()));
}

void sh::MainWindow::onGlobalSettingChanged(QStandardItem *item)
{
	if (mIgnoreGlobalSettingChange)
		return; // we are only interested in changes by the user, not by the backend.

	std::string name = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 0)).toString().toStdString();
	std::string value = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 1)).toString().toStdString();

	queueAction(new sh::ActionChangeGlobalSetting(name, value));
}

void sh::MainWindow::onConfigurationChanged (QStandardItem* item)
{
	QList<QListWidgetItem*> items = ui->configurationList->selectedItems();
	if (items.size())
	{
		std::string name = items.front()->text().toStdString();
		std::string key = mConfigurationModel->data(mConfigurationModel->index(item->row(), 0)).toString().toStdString();
		std::string value = mConfigurationModel->data(mConfigurationModel->index(item->row(), 1)).toString().toStdString();

		queueAction(new sh::ActionChangeConfiguration(name, key, value));

		requestQuery(new sh::ConfigurationQuery(name));
	}
}

void sh::MainWindow::on_lineEdit_textEdited(const QString &arg1)
{
	mMaterialProxyModel->setFilterFixedString(arg1);
}

void sh::MainWindow::on_actionSave_triggered()
{
	queueAction (new sh::ActionSaveAll());
}

void sh::MainWindow::on_actionNewMaterial_triggered()
{

}

void sh::MainWindow::on_actionDeleteMaterial_triggered()
{
	QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
	QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString();

	queueAction (new sh::ActionDeleteMaterial(name.toStdString()));
}

void sh::MainWindow::queueAction(Action* action)
{
	boost::mutex::scoped_lock lock(mSync->mActionMutex);
	mActionQueue.push(action);
}

void sh::MainWindow::requestQuery(Query *query)
{
	boost::mutex::scoped_lock lock(mSync->mActionMutex);
	mQueries.push_back(query);
}

void sh::MainWindow::on_actionQuit_triggered()
{
	hide();
}

void sh::MainWindow::on_actionNewConfiguration_triggered()
{
	QInputDialog dialog(this);

	QString text = QInputDialog::getText(this, tr("New Configuration"),
											  tr("Configuration name:"));

	if (!text.isEmpty())
	{
		queueAction(new ActionCreateConfiguration(text.toStdString()));
	}
}

void sh::MainWindow::on_actionDeleteConfiguration_triggered()
{
	QList<QListWidgetItem*> items = ui->configurationList->selectedItems();
	if (items.size())
		queueAction(new ActionDeleteConfiguration(items.front()->text().toStdString()));
}

void sh::MainWindow::on_actionDeleteConfigurationProperty_triggered()
{
	QList<QListWidgetItem*> items = ui->configurationList->selectedItems();
	if (items.empty())
		return;
	std::string configurationName = items.front()->text().toStdString();

	QModelIndex current = ui->configurationView->currentIndex();
	if (!current.isValid())
		return;

	std::string propertyName = mConfigurationModel->data(mConfigurationModel->index(current.row(), 0)).toString().toStdString();

	queueAction(new sh::ActionDeleteConfigurationProperty(configurationName, propertyName));
	requestQuery(new sh::ConfigurationQuery(configurationName));
}

void sh::MainWindow::on_actionCloneMaterial_triggered()
{
	QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
	QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString();
	if (name.isEmpty())
		return;

	QInputDialog dialog(this);

	QString text = QInputDialog::getText(this, tr("Clone material"),
											  tr("Name:"));

	if (!text.isEmpty())
	{
		queueAction(new ActionCloneMaterial(name.toStdString(), text.toStdString()));
	}
}

void sh::MainWindow::onContextMenuRequested(const QPoint &point)
{
	QPoint globalPos = ui->materialView->viewport()->mapToGlobal(point);

	QMenu menu;

	QList <QAction*> actions;
	actions.push_back(ui->actionNewProperty);
	actions.push_back(ui->actionDeleteProperty);
	actions.push_back(ui->actionCreatePass);
	actions.push_back(ui->actionCreateTextureUnit);
	menu.addActions(actions);

	menu.exec(globalPos);
}

void sh::MainWindow::getContext(QModelIndex index, int* passIndex, int* textureIndex, bool* isInPass, bool* isInTextureUnit)
{
	if (passIndex)
	{
		*passIndex = 0;
		if (isInPass)
			*isInPass = false;
		QModelIndex passModelIndex = index;
		// go up until we find the pass item.
		while (getPropertyKey(passModelIndex) != "pass" && passModelIndex.isValid())
			passModelIndex = passModelIndex.parent();

		if (passModelIndex.isValid())
		{
			if (passModelIndex.column() != 0)
				passModelIndex = passModelIndex.parent().child(passModelIndex.row(), 0);
			for (int i=0; i<mMaterialPropertyModel->rowCount(); ++i)
			{
				if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass"))
				{
					if (mMaterialPropertyModel->index(i, 0) == passModelIndex)
					{
						if (isInPass)
							*isInPass = true;
						break;
					}
					++(*passIndex);
				}
			}
		}
	}
	if (textureIndex)
	{
		*textureIndex = 0;
		if (isInTextureUnit)
			*isInTextureUnit = false;
		QModelIndex texModelIndex = index;
		// go up until we find the texture_unit item.
		while (getPropertyKey(texModelIndex) != "texture_unit" && texModelIndex.isValid())
			texModelIndex = texModelIndex.parent();
		if (texModelIndex.isValid())
		{
			if (texModelIndex.column() != 0)
				texModelIndex = texModelIndex.parent().child(texModelIndex.row(), 0);
			for (int i=0; i<mMaterialPropertyModel->rowCount(texModelIndex.parent()); ++i)
			{
				if (texModelIndex.parent().child(i, 0).data().toString() == QString("texture_unit"))
				{
					if (texModelIndex.parent().child(i, 0) == texModelIndex)
					{
						if (isInTextureUnit)
							*isInTextureUnit = true;
						break;
					}
					++(*textureIndex);
				}
			}
		}
	}
}

std::string sh::MainWindow::getPropertyKey(QModelIndex index)
{
	if (!index.parent().isValid())
		return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 0)).toString().toStdString();
	else
		return index.parent().child(index.row(), 0).data().toString().toStdString();
}

std::string sh::MainWindow::getPropertyValue(QModelIndex index)
{
	if (!index.parent().isValid())
		return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 1)).toString().toStdString();
	else
		return index.parent().child(index.row(), 1).data().toString().toStdString();
}

void sh::MainWindow::onMaterialPropertyChanged(QStandardItem *item)
{
	if (mIgnoreMaterialPropertyChange)
		return;

	QString material = getSelectedMaterial();
	if (material.isEmpty())
		return;

	// handle checkboxes being checked/unchecked
	std::string value = getPropertyValue(item->index());
	if (item->data(Qt::UserRole).toInt() == MaterialProperty::Boolean)
	{
		if (item->checkState() == Qt::Checked && value != "true")
			value = "true";
		else if (item->checkState() == Qt::Unchecked && value == "true")
			value = "false";
		item->setText(QString::fromStdString(value));
	}

	// handle inherited properties being changed, i.e. overridden by the current (derived) material
	if (item->data(Qt::UserRole+1).toInt() == MaterialProperty::Inherited_Unchanged)
	{
		QColor normalColor = ui->materialView->palette().color(QPalette::Normal, QPalette::WindowText);
		mIgnoreMaterialPropertyChange = true;
		mMaterialPropertyModel->item(item->index().row(), 0)
				->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1);
		mMaterialPropertyModel->item(item->index().row(), 0)
				->setData(normalColor, Qt::ForegroundRole);
		mMaterialPropertyModel->item(item->index().row(), 1)
				->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1);
		mMaterialPropertyModel->item(item->index().row(), 1)
				->setData(normalColor, Qt::ForegroundRole);
		mIgnoreMaterialPropertyChange = false;

		ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(item->index()));
	}

	if (!item->index().parent().isValid())
	{
		// top level material property
		queueAction(new ActionSetMaterialProperty(
				material.toStdString(), getPropertyKey(item->index()), value));
	}
	else if (getPropertyKey(item->index()) == "texture_unit")
	{
		// texture unit name changed
		int passIndex, textureIndex;
		getContext(item->index(), &passIndex, &textureIndex);
		std::cout << "passIndex " << passIndex << " " << textureIndex << std::endl;

		queueAction(new ActionChangeTextureUnitName(
				material.toStdString(), passIndex, textureIndex, value));

	}
	else if (item->index().parent().data().toString() == "pass")
	{
		// pass property
		int passIndex;
		getContext(item->index(), &passIndex, NULL);
		/// \todo if shaders are changed, check that the material provides all properties needed by the shader
		queueAction(new ActionSetPassProperty(
				material.toStdString(), passIndex, getPropertyKey(item->index()), value));
	}
	else if (item->index().parent().data().toString() == "shader_properties")
	{
		// shader property
		int passIndex;
		getContext(item->index(), &passIndex, NULL);
		queueAction(new ActionSetShaderProperty(
				material.toStdString(), passIndex, getPropertyKey(item->index()), value));
	}
	else if (item->index().parent().data().toString() == "texture_unit")
	{
		// texture property
		int passIndex, textureIndex;
		getContext(item->index(), &passIndex, &textureIndex);
		queueAction(new ActionSetTextureProperty(
				material.toStdString(), passIndex, textureIndex, getPropertyKey(item->index()), value));
	}
}

void sh::MainWindow::buildMaterialModel(MaterialQuery *data)
{
	mMaterialPropertyModel->clear();

	mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
	mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));

	for (std::map<std::string, MaterialProperty>::const_iterator it = data->mProperties.begin();
		 it != data->mProperties.end(); ++it)
	{
		addProperty(mMaterialPropertyModel->invisibleRootItem(), it->first, it->second);
	}

	for (std::vector<PassInfo>::iterator it = data->mPasses.begin();
		 it != data->mPasses.end(); ++it)
	{
		QStandardItem* passItem = new QStandardItem (QString("pass"));
		passItem->setFlags(passItem->flags() &= ~Qt::ItemIsEditable);
		passItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);

		if (it->mShaderProperties.size())
		{
			QStandardItem* shaderPropertiesItem = new QStandardItem (QString("shader_properties"));
			shaderPropertiesItem->setFlags(shaderPropertiesItem->flags() &= ~Qt::ItemIsEditable);
			shaderPropertiesItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);

			for (std::map<std::string, MaterialProperty>::iterator pit = it->mShaderProperties.begin();
				 pit != it->mShaderProperties.end(); ++pit)
			{
				addProperty(shaderPropertiesItem, pit->first, pit->second);
			}
			passItem->appendRow(shaderPropertiesItem);
		}

		for (std::map<std::string, MaterialProperty>::iterator pit = it->mProperties.begin();
			 pit != it->mProperties.end(); ++pit)
		{
			addProperty(passItem, pit->first, pit->second);
		}

		for (std::vector<TextureUnitInfo>::iterator tIt = it->mTextureUnits.begin();
			 tIt != it->mTextureUnits.end(); ++tIt)
		{
			QStandardItem* unitItem = new QStandardItem (QString("texture_unit"));
			unitItem->setFlags(unitItem->flags() &= ~Qt::ItemIsEditable);
			unitItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);
			QStandardItem* nameItem = new QStandardItem (QString::fromStdString(tIt->mName));
			nameItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);

			QList<QStandardItem*> texUnit;
			texUnit << unitItem << nameItem;

			for (std::map<std::string, MaterialProperty>::iterator pit = tIt->mProperties.begin();
				 pit != tIt->mProperties.end(); ++pit)
			{
				addProperty(unitItem, pit->first, pit->second);
			}

			passItem->appendRow(texUnit);
		}

		QList<QStandardItem*> toAdd;
		toAdd << passItem;
		toAdd << new QStandardItem(QString(""));
		mMaterialPropertyModel->appendRow(toAdd);
	}

	ui->materialView->expandAll();
	ui->materialView->resizeColumnToContents(0);
	ui->materialView->resizeColumnToContents(1);
}

void sh::MainWindow::addProperty(QStandardItem *parent, const std::string &key, MaterialProperty value, bool scrollTo)
{
	QList<QStandardItem*> toAdd;
	QStandardItem* keyItem = new QStandardItem(QString::fromStdString(key));
	keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable);
	keyItem->setData(QVariant(value.mType), Qt::UserRole);
	keyItem->setData(QVariant(value.mSource), Qt::UserRole+1);
	toAdd.push_back(keyItem);

	QStandardItem* valueItem = NULL;
	if (value.mSource != MaterialProperty::None)
	{
		valueItem = new QStandardItem(QString::fromStdString(value.mValue));
		valueItem->setData(QVariant(value.mType), Qt::UserRole);
		valueItem->setData(QVariant(value.mSource), Qt::UserRole+1);
		toAdd.push_back(valueItem);
	}


	if (value.mSource == MaterialProperty::Inherited_Unchanged)
	{
		QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText);
		keyItem->setData(color, Qt::ForegroundRole);
		if (valueItem)
			valueItem->setData(color, Qt::ForegroundRole);
	}
	if (value.mType == MaterialProperty::Boolean && valueItem)
	{
		valueItem->setCheckable(true);
		valueItem->setCheckState((value.mValue == "true") ? Qt::Checked : Qt::Unchecked);
	}

	parent->appendRow(toAdd);

	if (scrollTo)
		ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(keyItem->index()));
}

void sh::MainWindow::buildConfigurationModel(ConfigurationQuery *data)
{
	while (mConfigurationModel->rowCount())
		mConfigurationModel->removeRow(0);
	for (std::map<std::string, std::string>::iterator it = data->mProperties.begin();
		 it != data->mProperties.end(); ++it)
	{
		QList<QStandardItem*> toAdd;
		QStandardItem* name = new QStandardItem(QString::fromStdString(it->first));
		name->setFlags(name->flags() &= ~Qt::ItemIsEditable);
		QStandardItem* value = new QStandardItem(QString::fromStdString(it->second));
		toAdd.push_back(name);
		toAdd.push_back(value);
		mConfigurationModel->appendRow(toAdd);
	}

	// add items that are in global settings, but not in this configuration (with a "inactive" color)
	for (std::map<std::string, std::string>::const_iterator it = mState.mGlobalSettingsMap.begin();
		 it != mState.mGlobalSettingsMap.end(); ++it)
	{
		if (data->mProperties.find(it->first) == data->mProperties.end())
		{
			QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText);
			QList<QStandardItem*> toAdd;
			QStandardItem* name = new QStandardItem(QString::fromStdString(it->first));
			name->setFlags(name->flags() &= ~Qt::ItemIsEditable);
			name->setData(color, Qt::ForegroundRole);
			QStandardItem* value = new QStandardItem(QString::fromStdString(it->second));
			value->setData(color, Qt::ForegroundRole);
			toAdd.push_back(name);
			toAdd.push_back(value);
			mConfigurationModel->appendRow(toAdd);
		}
	}
}

void sh::MainWindow::on_actionCreatePass_triggered()
{
	QString material = getSelectedMaterial();
	if (!material.isEmpty())
	{
		addProperty(mMaterialPropertyModel->invisibleRootItem(),
					"pass", MaterialProperty("", MaterialProperty::Object, MaterialProperty::None), true);

		queueAction (new ActionCreatePass(material.toStdString()));
	}
}

void sh::MainWindow::on_actionDeleteProperty_triggered()
{
	QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex());
	QString material = getSelectedMaterial();
	if (material.isEmpty())
		return;

	mIgnoreMaterialPropertyChange = true;

	if (getPropertyKey(selectedIndex) == "pass")
	{
		// delete whole pass
		int passIndex;
		getContext(selectedIndex, &passIndex, NULL);
		if (passIndex == 0)
		{
			QMessageBox msgBox;
			msgBox.setText("The first pass can not be deleted.");
			msgBox.exec();
		}
		else
		{
			queueAction(new ActionDeletePass(material.toStdString(), passIndex));
			mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
		}
	}
	else if (getPropertyKey(selectedIndex) == "texture_unit")
	{
		// delete whole texture unit
		int passIndex, textureIndex;
		getContext(selectedIndex, &passIndex, &textureIndex);
		queueAction(new ActionDeleteTextureUnit(material.toStdString(), passIndex, textureIndex));
		mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
	}
	else if (!selectedIndex.parent().isValid())
	{
		// top level material property
		MaterialProperty::Source source = static_cast<MaterialProperty::Source>(
					mMaterialPropertyModel->itemFromIndex(selectedIndex)->data(Qt::UserRole+1).toInt());
		if (source == MaterialProperty::Inherited_Unchanged)
		{
			QMessageBox msgBox;
			msgBox.setText("Inherited properties can not be deleted.");
			msgBox.exec();
		}
		else
		{
			queueAction(new ActionDeleteMaterialProperty(
					material.toStdString(), getPropertyKey(selectedIndex)));
			std::cout << "source is " << source << std::endl;
			if (source == MaterialProperty::Inherited_Changed)
			{
				QColor inactiveColor = ui->materialView->palette().color(QPalette::Disabled, QPalette::WindowText);
				mMaterialPropertyModel->item(selectedIndex.row(), 0)
						->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1);
				mMaterialPropertyModel->item(selectedIndex.row(), 0)
						->setData(inactiveColor, Qt::ForegroundRole);
				mMaterialPropertyModel->item(selectedIndex.row(), 1)
						->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1);
				mMaterialPropertyModel->item(selectedIndex.row(), 1)
						->setData(inactiveColor, Qt::ForegroundRole);

				// make sure to update the property's value
				requestQuery(new sh::MaterialPropertyQuery(material.toStdString(), getPropertyKey(selectedIndex)));
			}
			else
				mMaterialPropertyModel->removeRow(selectedIndex.row());
		}
	}
	else if (selectedIndex.parent().data().toString() == "pass")
	{
		// pass property
		int passIndex;
		getContext(selectedIndex, &passIndex, NULL);
		queueAction(new ActionDeletePassProperty(
				material.toStdString(), passIndex, getPropertyKey(selectedIndex)));
		mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
	}
	else if (selectedIndex.parent().data().toString() == "shader_properties")
	{
		// shader property
		int passIndex;
		getContext(selectedIndex, &passIndex, NULL);
		queueAction(new ActionDeleteShaderProperty(
				material.toStdString(), passIndex, getPropertyKey(selectedIndex)));
		mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
	}
	else if (selectedIndex.parent().data().toString() == "texture_unit")
	{
		// texture property
		int passIndex, textureIndex;
		getContext(selectedIndex, &passIndex, &textureIndex);
		queueAction(new ActionDeleteTextureProperty(
				material.toStdString(), passIndex, textureIndex, getPropertyKey(selectedIndex)));
		mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
	}
	mIgnoreMaterialPropertyChange = false;
}

void sh::MainWindow::on_actionNewProperty_triggered()
{
	QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex());
	QString material = getSelectedMaterial();
	if (material.isEmpty())
		return;

	AddPropertyDialog* dialog = new AddPropertyDialog(this);
	dialog->exec();
	QString propertyName = dialog->mName;
	QString defaultValue = "";

	/// \todo check if this property name exists already

	if (!propertyName.isEmpty())
	{
		int passIndex, textureIndex;
		bool isInPass, isInTextureUnit;
		getContext(selectedIndex, &passIndex, &textureIndex, &isInPass, &isInTextureUnit);

		QList<QStandardItem*> items;
		QStandardItem* keyItem = new QStandardItem(propertyName);
		keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable);
		items << keyItem;
		items << new QStandardItem(defaultValue);

		// figure out which item the new property should be a child of
		QModelIndex parentIndex = selectedIndex;
		if (selectedIndex.data(Qt::UserRole) != MaterialProperty::Object)
			parentIndex = selectedIndex.parent();
		QStandardItem* parentItem;
		if (!parentIndex.isValid())
			parentItem = mMaterialPropertyModel->invisibleRootItem();
		else
			parentItem = mMaterialPropertyModel->itemFromIndex(parentIndex);

		if (isInTextureUnit)
		{
			queueAction(new ActionSetTextureProperty(
							material.toStdString(), passIndex, textureIndex, propertyName.toStdString(), defaultValue.toStdString()));
		}
		else if (isInPass)
		{
			if (selectedIndex.parent().child(selectedIndex.row(),0).data().toString() == "shader_properties"
				|| selectedIndex.parent().data().toString() == "shader_properties")
			{
				queueAction(new ActionSetShaderProperty(
								material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString()));
			}
			else
			{
				queueAction(new ActionSetPassProperty(
								material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString()));
			}
		}
		else
		{
			queueAction(new ActionSetMaterialProperty(
							material.toStdString(), propertyName.toStdString(), defaultValue.toStdString()));
		}

		addProperty(parentItem, propertyName.toStdString(),
					MaterialProperty (defaultValue.toStdString(), MaterialProperty::Misc, MaterialProperty::Normal), true);

		/// \todo scroll to newly added property
	}
}

void sh::MainWindow::on_actionCreateTextureUnit_triggered()
{
	QString material = getSelectedMaterial();
	if (material.isEmpty())
		return;

	QInputDialog dialog(this);

	QString text = QInputDialog::getText(this, tr("New texture unit"),
											  tr("Texture unit name (for referencing in shaders):"));
	if (!text.isEmpty())
	{
		QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex());
		int passIndex;
		getContext(selectedIndex, &passIndex, NULL);
		queueAction(new ActionCreateTextureUnit(material.toStdString(), passIndex, text.toStdString()));

		// add to model
		int index = 0;
		for (int i=0; i<mMaterialPropertyModel->rowCount(); ++i)
		{
			if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass"))
			{
				if (index == passIndex)
				{
					addProperty(mMaterialPropertyModel->itemFromIndex(mMaterialPropertyModel->index(i, 0)),
								"texture_unit", MaterialProperty(text.toStdString(), MaterialProperty::Object), true);
					break;
				}

				++index;
			}
		}
	}
}

void sh::MainWindow::on_clearButton_clicked()
{
	ui->errorLog->clear();
}

void sh::MainWindow::on_tabWidget_currentChanged(int index)
{
	QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::WindowText);

	if (index == 3)
		ui->tabWidget->tabBar()->setTabTextColor(3, color);
}