#include "blockeditor.h"

#include "blockview.h"
#include "drawtools.h"
#include "ui_blockeditor.h"

#include <QComboBox>
#include <QFile>
#include <QFileDialog>
#include <QTimer>
#include <QToolBar>


BlockEditor::BlockEditor(QWidget * parent): QWidget(parent), ui(new Ui::BlockEditor) {
	init           = false;
	m_editorMode   = false;
	m_pinsEditable = true;
	ui->setupUi(this);
	src_title = windowTitle();
	connect(ui->blockView->scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
	block.setFlags(QGraphicsItem::GraphicsItemFlags());
	block.is_blockeditor = true;
	ui->blockView->addItem(&block);
	ui->blockView->viewport()->installEventFilter(this);
	DrawTools * drawtools = new DrawTools(ui->blockView);
	connect(drawtools, SIGNAL(itemCreated(QGraphicsItem *)), this, SLOT(addItem(QGraphicsItem *)));
	drawtools->textEditCombo()->addItems(QStringList() << "%name"
	                                                   << "%value"
	                                                   << "%id");
	ui->layoutProperties->addWidget(drawtools->propertyWidget());
	ui->actionRemove_items->setEnabled(false);
	ui->button_color->setColor(Qt::lightGray);
	ui->treePins->setItemDelegateForColumn(1, new PinBusDelegate());
	connect(ui->treePins, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(updateBlock()));
	ui->treePins->viewport()->installEventFilter(this);

	QToolBar * bar;
	bar = new QToolBar(ui->widgetBar);
	bar->setOrientation(Qt::Vertical);
	bar->addActions(drawtools->actionsForAdd());
	ui->widgetBar->setMinimumSize(bar->sizeHint());

	bar = new QToolBar(ui->widgetBarZ);
	bar->setOrientation(Qt::Vertical);
	bar->addActions(drawtools->actionsForZ());
	bar->addSeparator();
	bar->addActions(QList<QAction *>() << ui->actionRemove_items);
	ui->widgetBarZ->setMinimumSize(bar->sizeHint());
	init = true;
	on_buttonClear_clicked();
}


BlockEditor::~BlockEditor() {
	init = false;
	delete ui;
}


void BlockEditor::loadFile(QString path) {
	if (path.isEmpty()) return;
	QFile f(path);
	if (f.open(QIODevice::ReadOnly)) {
		cur_file = path;
		loadModel(f.readAll());
		QDir::setCurrent(QFileInfo(path).dir().path());
	}
	setWindowTitle(src_title + QString(" - %1").arg(QFileInfo(path).baseName()));
}


void BlockEditor::loadModel(const QByteArray & model) {
	BlockItem b;
	b.loadModel(model);
	ui->spin_w->setValue(b.width());
	ui->spin_h->setValue(b.height());
	ui->spin_margin->setValue(b.pinsMargin());
	ui->button_color->setColor(b.color());
	block.loadModel(model);
	treePinsClear();
	ui->treePins->blockSignals(true);
	QVector<BlockItemPin *> pins = block.pins();
	foreach(BlockItemPin * p, pins) {
		QTreeWidgetItem * ti = new QTreeWidgetItem(QStringList() << p->text() << QString::number(p->busType()));
		ti->setData(0, Qt::UserRole, qulonglong(p));
		ti->setData(0, Qt::UserRole + 1, (int)p->alignment());
		ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled);
		pin_tli[p->alignment()]->addChild(ti);
	}
	foreach(QGraphicsItem * i, block.decors()) {
		i->setData(bvidMoveParent, false);
		i->setData(bvidCorrectMove, true);
	}
	ui->treePins->blockSignals(false);
}


QByteArray BlockEditor::saveModel() {
	return block.saveModel();
}


void BlockEditor::setEditorMode(bool editorMode) {
	m_editorMode = editorMode;
	ui->buttonClear->setVisible(!editorMode);
	ui->buttonSave->setVisible(!editorMode);
	ui->buttonSaveAs->setVisible(!editorMode);
	ui->buttonLoad->setVisible(!editorMode);
}


void BlockEditor::setPinsEditable(bool pinsEditable) {
	m_pinsEditable = pinsEditable;
	ui->treePins->setVisible(pinsEditable);
	ui->buttonPinAdd->setVisible(pinsEditable);
	ui->buttonPinClear->setVisible(pinsEditable);
	ui->buttonPinDelete->setVisible(pinsEditable);
	ui->buttonPinDup->setVisible(pinsEditable);
	ui->groupPins->setVisible(pinsEditable);
}


void BlockEditor::selectionChanged() {
	if (!init) return;
	ui->actionRemove_items->setEnabled(!ui->blockView->scene()->selectedItems().isEmpty());
}


void BlockEditor::addItem(QGraphicsItem * item) {
	block.addDecor(item);
	item->setData(bvidMoveParent, false);
	item->setData(bvidCorrectMove, true);
}


void BlockEditor::updateBlock() {
	block.setSize(ui->spin_w->value(), ui->spin_h->value());
	block.setColor(ui->button_color->color());
	block.setPinsMargin(ui->spin_margin->value());
}


void BlockEditor::treePinsClear() {
	ui->treePins->blockSignals(true);
	ui->treePins->clear();
	QFont bf(font());
	bf.setBold(true);
	QList<int> al  = QList<int>() << Qt::AlignLeft << Qt::AlignRight << Qt::AlignTop << Qt::AlignBottom;
	QStringList an = QStringList() << "Left"
	                               << "Right"
	                               << "Top"
	                               << "Bottom";
	pin_tli.clear();
	for (int i = 0; i < al.size(); ++i) {
		QTreeWidgetItem * ti = new QTreeWidgetItem();
		ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDropEnabled);
		ti->setData(0, Qt::UserRole, al[i]);
		ti->setText(0, an[i]);
		ti->setFont(0, bf);
		ui->treePins->addTopLevelItem(ti);
		ti->setFirstColumnSpanned(true);
		ti->setExpanded(true);
		pin_tli[al[i]] = ti;
	}
	ui->treePins->blockSignals(false);
}


bool BlockEditor::eventFilter(QObject * o, QEvent * e) {
	if (!init) QWidget::eventFilter(o, e);
	if (o == ui->treePins->viewport()) {
		if (e->type() == QEvent::Drop) {
			QTimer::singleShot(0, this, SLOT(arrangePins()));
		}
	}
	if (o == ui->blockView->viewport()) {
		if (e->type() == QEvent::Resize) {
			ui->blockView->centerOn(&block);
		}
	}
	return QWidget::eventFilter(o, e);
}


void BlockEditor::changeEvent(QEvent * e) {
	QWidget::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange: ui->retranslateUi(this); break;
	default: break;
	}
}


void BlockEditor::on_actionRemove_items_triggered() {
	QList<QGraphicsItem *> si = ui->blockView->scene()->selectedItems();
	foreach(QGraphicsItem * i, si)
		block.removeDecor(i);
}


void BlockEditor::on_buttonSave_clicked() {
	if (cur_file.isEmpty()) {
		on_buttonSaveAs_clicked();
		return;
	}
	QFile f(cur_file);
	if (f.open(QIODevice::WriteOnly)) {
		f.write(saveModel());
	}
}


void BlockEditor::on_buttonSaveAs_clicked() {
	QString c = QFileDialog::getSaveFileName(this, "Save block model to file", cur_file, "*.blockmodel");
	if (!c.isEmpty()) {
		QFile f(c);
		if (f.open(QIODevice::WriteOnly)) {
			cur_file = c;
			f.write(saveModel());
			setWindowTitle(src_title + QString(" - %1").arg(QFileInfo(c).baseName()));
		}
	}
}


void BlockEditor::on_buttonLoad_clicked() {
	QString c = QFileDialog::getOpenFileName(this, "Save block model to file", cur_file, "*.blockmodel");
	loadFile(c);
}


void BlockEditor::on_buttonClear_clicked() {
	BlockItem b;
	ui->spin_w->setValue(b.width());
	ui->spin_h->setValue(b.height());
	ui->spin_margin->setValue(b.pinsMargin());
	ui->button_color->setColor(b.color());
	block.loadModel(QByteArray());
	treePinsClear();
}


void BlockEditor::on_buttonPinAdd_clicked() {
	ui->treePins->blockSignals(true);
	QTreeWidgetItem * ti = new QTreeWidgetItem(QStringList() << ""
	                                                         << "0");
	ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled);
	ti->setData(0, Qt::UserRole, qulonglong(block.addPin(Qt::AlignLeft, ti->text(1).toInt(), ti->text(0))));
	ti->setData(0, Qt::UserRole + 1, (int)Qt::AlignLeft);
	pin_tli[Qt::AlignLeft]->addChild(ti);
	ui->treePins->setCurrentItem(ti);
	ui->treePins->blockSignals(false);
	updateBlock();
}


void BlockEditor::on_buttonPinDup_clicked() {
	QTreeWidgetItem * ci = ui->treePins->currentItem();
	if (ci == 0) return;
	ui->treePins->blockSignals(true);
	QTreeWidgetItem * ti = ci->clone();
	ti->setText(0, ti->text(0));
	ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled);
	Qt::Alignment al = (Qt::Alignment)ci->data(0, Qt::UserRole + 1).toInt();
	ti->setData(0, Qt::UserRole, qulonglong(block.addPin(al, ci->text(1).toInt(), ci->text(0))));
	ti->setData(0, Qt::UserRole + 1, (int)al);
	pin_tli[al]->addChild(ti);
	ui->treePins->setCurrentItem(ti);
	ui->treePins->blockSignals(false);
}


void BlockEditor::on_buttonPinDelete_clicked() {
	ui->treePins->blockSignals(true);
	QList<QTreeWidgetItem *> si = ui->treePins->selectedItems();
	foreach(QTreeWidgetItem * i, si) {
		if (!i->parent()) continue;
		block.removePin((BlockItemPin *)(i->data(0, Qt::UserRole).toLongLong()));
		delete i;
	}
	ui->treePins->blockSignals(false);
}


void BlockEditor::on_buttonPinClear_clicked() {
	treePinsClear();
	block.clearPins();
}


void BlockEditor::on_treePins_itemChanged(QTreeWidgetItem * item, int column) {
	if (!item) return;
	BlockItemPin * pin = (BlockItemPin *)item->data(0, Qt::UserRole).toULongLong();
	if (!pin) return;
	switch (column) {
	case 0:
		ui->treePins->blockSignals(true);
		item->setText(0, item->text(0));
		pin->setText(item->text(0));
		ui->treePins->blockSignals(false);
		break;
	case 1: pin->setBusType(item->text(1).toInt()); break;
	};
}


void BlockEditor::arrangePins() {
	QVector<BlockItemPin *> pins = block.pins();
	QList<QTreeWidgetItem *> tli = pin_tli.values();
	foreach(QTreeWidgetItem * ti, tli) {
		for (int i = 0; i < ti->childCount(); ++i) {
			foreach(BlockItemPin * p, pins)
				if (p == (BlockItemPin *)(ti->child(i)->data(0, Qt::UserRole).toULongLong())) {
					p->setAlignment((Qt::Alignment)ti->data(0, Qt::UserRole).toInt());
					BlockItemPin * np = block.addPin(p, false);
					ti->child(i)->setData(0, Qt::UserRole, qulonglong(np));
					ti->child(i)->setData(0, Qt::UserRole + 1, ti->data(0, Qt::UserRole).toInt());
					break;
				}
		}
	}
	block.arrangePins();
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


QWidget * PinAlignDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QComboBox * combo = new QComboBox(parent);
	int cv            = index.data().toInt();
	combo->addItem("Left", int(Qt::AlignLeft));
	if (cv == Qt::AlignLeft) combo->setCurrentIndex(0);
	combo->addItem("Right", int(Qt::AlignRight));
	if (cv == Qt::AlignRight) combo->setCurrentIndex(1);
	combo->addItem("Top", int(Qt::AlignTop));
	if (cv == Qt::AlignTop) combo->setCurrentIndex(2);
	combo->addItem("Bottom", int(Qt::AlignBottom));
	if (cv == Qt::AlignBottom) combo->setCurrentIndex(3);
	combo->setGeometry(option.rect);
	return combo;
}


QString PinAlignDelegate::displayText(const QVariant & value, const QLocale & locale) const {
	int cv = value.toInt();
	switch (cv) {
	case Qt::AlignLeft: return tr("Left"); break;
	case Qt::AlignRight: return tr("Right"); break;
	case Qt::AlignTop: return tr("Top"); break;
	case Qt::AlignBottom: return tr("Bottom"); break;
	}
	return tr("unknown");
}


void PinAlignDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	model->setData(index, ((QComboBox *)editor)->itemData(((QComboBox *)editor)->currentIndex()).toInt());
}


/// ***********************************************************
/// ***********************************************************
/// ***********************************************************
/// ***********************************************************


QWidget * PinBusDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {
	QSpinBox * spin = new QSpinBox(parent);
	spin->setMinimum(-2147483648);
	spin->setMaximum(2147483647);
	spin->setValue(index.data().toInt());
	return spin;
}


QString PinBusDelegate::displayText(const QVariant & value, const QLocale & locale) const {
	int cv = value.toInt();
	return QString::number(cv);
}


void PinBusDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
	model->setData(index, ((QSpinBox *)editor)->value());
}
