#include <QDoubleSpinBox>
#include <QFileDialog>
#include <QTreeView>

#include "xmlModel.h"


QString modeToString(SWITCH);

/*
	Delegate implementations
	========================
*/
CustomXmlDelegate::CustomXmlDelegate(int column, QObject *parent) : QItemDelegate(parent), parentObj(parent)
{
	valueColumn = column;
}

QWidget *CustomXmlDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
	if (index.column() == valueColumn) {
		if (index.model()->data(index, Qt::StatusTipRole).toString() == "path") {
			QComboBox *edit = new QComboBox(parent);
			edit->installEventFilter(const_cast<CustomXmlDelegate*>(this));
			edit->setEditable(true);
// 			connect(edit, SIGNAL(activated(int)), this, SLOT(commitAndCloseComboEditor(int)));
// 			connect(edit, SIGNAL(activated(int)), parentObj, SLOT(updateDirPath()));
			return edit;
		} else {
			QDoubleSpinBox *edit = new QDoubleSpinBox(parent);
			edit->installEventFilter(const_cast<CustomXmlDelegate*>(this));
			
			if (index.model()->data(index, Qt::StatusTipRole).toString() == "alt")
				edit->setRange(0,800);
			else if (index.model()->data(index, Qt::StatusTipRole).toString() == "deg")
				edit->setRange(0,90);
			edit->setDecimals(2);
			connect(edit, SIGNAL(editingFinished()), this, SLOT(commitAndCloseSpinEditor()));
			
			return edit;
		} // if
	} else {
		return QItemDelegate::createEditor(parent, option, index);
	} // if
}


void CustomXmlDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
	if (index.column() == valueColumn) {
		if (index.model()->data(index, Qt::StatusTipRole).toString() == "path") {
			QString value = index.model()->data(index, Qt::DisplayRole).toString();
			QComboBox *comboBox = qobject_cast<QComboBox*>(editor);
			comboBox->insertItem(0,value);
			comboBox->insertItem(1,"browse");
		} else {
			double value = index.model()->data(index, Qt::DisplayRole).toDouble();
			QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
			spinBox->setValue(value);
		} // if
	} else {
		QItemDelegate::setEditorData(editor, index);
	} // if
}


void CustomXmlDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
	if (index.column() == valueColumn) {
		if (index.model()->data(index, Qt::StatusTipRole).toString() == "path") {
			QComboBox *comboBox = qobject_cast<QComboBox*>(editor);
			QString value = comboBox->currentText();
			if (value == "browse") {
				QFileDialog *fileDialog = new QFileDialog();
				fileDialog->setModal(true);
				fileDialog->setReadOnly(true);
				fileDialog->setFileMode(QFileDialog::DirectoryOnly);
				fileDialog->setDirectory(QDir::currentPath());
				fileDialog->setFilter( "Xml (*.xml)" );
				value = fileDialog->getExistingDirectory();
			}
// 			QMessageBox::information(0,"",QString("%1") .arg(value));
			model->setData(index, value);
		} else {
			QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
			spinBox->interpretText();
			double value = spinBox->value();
			model->setData(index, value);
		}
	} else {
		QItemDelegate::setModelData(editor, model, index);
	} // if
}

/*
void CustomXmlDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const
{
	editor->setGeometry(option.rect);
}
*/

void CustomXmlDelegate::commitAndCloseComboEditor(int nr)
{
	QComboBox *editor = qobject_cast<QComboBox *>((sender()));
	emit commitData(editor);
	emit closeEditor(editor);

}

void CustomXmlDelegate::commitAndCloseSpinEditor()
{
	QDoubleSpinBox *editor = qobject_cast<QDoubleSpinBox *>((sender()));
	emit commitData(editor);
	emit closeEditor(editor);
}

/*
	Model implementations
	=====================
*/
QDomNode xmlFindTag(QString needle, QDomNode hayStack) 
{// parse dom-tree (hayStack) for needle !!no errorcode 
	QDomNode dummy;
	while (!hayStack.isNull() && hayStack.nodeName() != needle)
	{
		if (hayStack.hasChildNodes()) {
			dummy = xmlFindTag(needle, hayStack.firstChild());
			if (dummy.nodeName() == needle) return dummy; // if
		} // if
		hayStack = hayStack.nextSibling();
	} // while
	
	return hayStack;
}
	
XmlModel::XmlModel(QDomDocument document, SWITCH refmode, QObject *parentObj) 
			: QAbstractItemModel(parentObj), domDocument(document), mode(refmode), parentObj(parentObj)
{
	rootItem = new DomItem(domDocument, 0);
	
	QString refTag = modeToString(refmode);
	QDomNode veryRoot = rootItem->node();
	
	veryRoot = xmlFindTag(QString("satpred"), veryRoot);
 	if (veryRoot.nodeName() == "satpred") {
		veryRoot = xmlFindTag(refTag, veryRoot);
		if (veryRoot.nodeName() == refTag) {
			delete rootItem;
			rootItem = new DomItem(veryRoot,0);
		} else {
			QMessageBox::warning(0,"xmlModel.cpp", 
				tr("Kann gewuenschten Tag - %1 - nicht finden")
				.arg(refTag));
		}// if
	} // if
}

int XmlModel::columnCount(const QModelIndex &/*parent*/) const
{
	return 3; // 3 columns -> 0: name 1: value 2: comment
}

bool XmlModel::setData(const QModelIndex & index, const QVariant & value, int role)
{	
	if (index.isValid() && role == Qt::EditRole && index.column() == 1) { // only value column is editable
	
		DomItem *item = static_cast<DomItem*>(index.internalPointer()); // get pointer		
		QDomElement valueTag = domDocument.createElement("value"); // create new dom element
		valueTag.setAttribute("type", index.model()->data(index, Qt::StatusTipRole).toString()); // !set type #> segmentation fault
		
		QDomText text = domDocument.createTextNode(value.toString()); // set entered value
		
		item->node().appendChild(valueTag); // ->
		valueTag.appendChild(text);		// set node.valueTag.text()
		
		item->node().replaceChild(valueTag,item->node().firstChild()); // kick old node and insert new
		emit dataChanged (index, index); // tell view class that sth changed	
		return true;
	} // if
	return false;
}

QVariant XmlModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	if (role == Qt::TextAlignmentRole && index.column() == 1)
		return int(Qt::AlignCenter | Qt::AlignVCenter);	// center value column
	
	if (role == Qt::DisplayRole || role == Qt::StatusTipRole) {	
	
		DomItem *item = static_cast<DomItem*>(index.internalPointer());
	
		QDomNode node = item->node(); // get node
	
		QDomNode child = node.firstChild(); // first child to evaluate
		QStringList attributes;
		QDomNamedNodeMap attributeMap = node.attributes(); // get attributes
		
		switch (index.column()) {
		case 0:
			return attributeMap.namedItem("name").nodeValue(); // first column is name = attribute of this node
		case 1:
			while (!child.isNull() && child.nodeName() != "value") // check childs for value node -> 2nd col
			{
				child = child.nextSibling();
			}
			
			if (role == Qt::StatusTipRole) {
			// the statustiprole is used to determine, which type
			// of data this field holds. thus we may have differing ranges or delegates
				QDomNamedNodeMap valueAttributeMap = child.attributes(); // get attributes
				if (!valueAttributeMap.namedItem("type").isNull())
					return valueAttributeMap.namedItem("type").nodeValue();
			} // if
			return child.toElement().text(); // return value of value node
		case 2:
			while (!child.isNull() && child.nodeName() != "comment") // chech child for comment node
			{
				child = child.nextSibling();
			}
			return child.toElement().text(); // return value of comment node
		default:
			return QVariant();
		} // switch
	} // if
	return QVariant();
} // member

Qt::ItemFlags XmlModel::flags(const QModelIndex &index) const
{
	if (!index.isValid())
		return Qt::ItemIsEnabled;

	if(index.column() == 1) // only value col editable
		return Qt::ItemIsEnabled | Qt::ItemIsSelectable  | Qt::ItemIsEditable;
	
	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

QVariant XmlModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
		switch (section) {
			case 0:
			return tr("Name");
			case 1:
			return tr("Wert");
			case 2:
			return tr("Kommentar");
			default:
			return QVariant();
		}//switch
	}//if

	return QVariant();
}

QModelIndex XmlModel::index(int row, int column, const QModelIndex &parent) const
{
	DomItem *parentItem;

	if (!parent.isValid())
		parentItem = rootItem;
	else
		parentItem = static_cast<DomItem*>(parent.internalPointer());

	DomItem *childItem = parentItem->child(row);
	if (childItem && childItem->node().nodeName() == "entry")
		return createIndex(row, column, childItem); 
	else
		return QModelIndex();
}

QModelIndex XmlModel::parent(const QModelIndex &child) const
{
	if (!child.isValid())
		return QModelIndex();

	DomItem *childItem = static_cast<DomItem*>(child.internalPointer());
	DomItem *parentItem = childItem->parent();

	if (!parentItem || parentItem == rootItem)
		return QModelIndex();

	return createIndex(parentItem->row(), 0, parentItem);
}

int XmlModel::rowCount(const QModelIndex &parent) const
{
	DomItem *parentItem;

	if (!parent.isValid())
		parentItem = rootItem;
	else
		parentItem = static_cast<DomItem*>(parent.internalPointer());

	int count = parentItem->node().childNodes().count();
	QDomNode child = parentItem->node().firstChild();
	while (!child.isNull())
	{
		if (child.nodeName() == "value" || child.nodeName() == "comment")
			count--;
		child = child.nextSibling();
	}//while
	return count;
}


bool XmlModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{// 
	if (action == Qt::IgnoreAction) return true; // if
	
	if (!data->hasFormat("application/x-qabstractitemmodeldatalist")) return false; // if

	if (column > 0) return false; // if
	
	int beginRow;
	
	if (row != -1) 
		beginRow = row;
	else if (parent.isValid())
		beginRow = parent.row();
	else
		beginRow = rowCount(QModelIndex());
	// if
	
	QByteArray encodedData = data->data("application/x-qabstractitemmodeldatalist");
	QDataStream stream (&encodedData, QIODevice::ReadOnly);
	QStringList newItems;
	int rows = 0;
	
	while (!stream.atEnd()) {
		QString text;
		stream >> text;
		newItems << text;
		++rows;
	} // while
	
	newPath = newItems[0] + "/" + newItems[1]; // [0] is relative path (to working dir) [1] is filename
	connect(this, SIGNAL(newXmlPath(QString)), parentObj, SLOT(updateTreeView(QString)));
	emit newXmlPath(newPath);
	return true;	
}


XmlModel::~XmlModel()
{
	delete rootItem;
}

/* 
	DomItem implementations 
	=======================
*/

DomItem::DomItem(QDomNode &node, int row, DomItem *parent)
{
	domNode = node;
	// Record the item's location within its parent.
	rowNumber = row;
	parentItem = parent;
}

DomItem::~DomItem()
{
	QHash<int,DomItem*>::iterator it;
	for (it = childItems.begin(); it != childItems.end(); ++it)
		delete it.value();
}

QDomNode DomItem::node() const
{
	return domNode;
}

DomItem *DomItem::parent()
{
	return parentItem;
}

DomItem *DomItem::child(int i)
{
	if (childItems.contains(i))
		return childItems[i];

	if (i >= 0 && i < domNode.childNodes().count()) {
		QDomNode childNode = domNode.childNodes().item(i);
		DomItem *childItem = new DomItem(childNode, i, this);
		childItems[i] = childItem;
		return childItem;
	}
	return 0;
}

int DomItem::row()
{
	return rowNumber;
}


