#include "blockview.h"

#include <QApplication>


BlockItemPin::BlockItemPin(Qt::Alignment a, int bus_type_, const QString & text_, QGraphicsObject * _parent)
	: QGraphicsObject(_parent)
	, ell_item(this)
	, text_item(this) {
	parent_ = 0;
	setData(bvidType, bvitPin);
	setAcceptHoverEvents(true);
	text_item.setData(bvidMoveParent, true);
	ell_item.setData(bvidVisualizeSelection, true);
	br[Disconnected] = QBrush(Qt::lightGray);
	br[Connected]    = QBrush(Qt::darkGreen);
	br[Hover]        = QBrush(Qt::blue);
	br[Drop]         = QBrush(Qt::green);
	br[Accept]       = QBrush(Qt::green);
	br[Reject]       = QBrush(Qt::red);
	anim_pin_size.setTargetObject(this);
	anim_pin_size.setPropertyName("pinSize");
	anim_pin_size.setEasingCurve(QEasingCurve::OutElastic);
	anim_pin_size.setDuration(700);
	anim_accept.setTargetObject(this);
	anim_accept.setPropertyName("pinSize");
	anim_accept.setEasingCurve(QEasingCurve::InOutQuad);
	anim_accept.setDuration(150);
	connect(&anim_accept, SIGNAL(finished()), this, SLOT(animationAccept()));
	setState(Disconnected);
	setAlignment(a);
	setBusType(bus_type_);
	setText(text_);
	setZValue(2.);
	_reparent();
}


void BlockItemPin::setPen(const QPen & p) {
	ell_item.setPen(p);
}


QPen BlockItemPin::pen() const {
	return ell_item.pen();
}


void BlockItemPin::setBrush(const QBrush & b) {
	ell_item.setBrush(b);
}


QBrush BlockItemPin::brush() const {
	return ell_item.brush();
}


QString BlockItemPin::text() const {
	return text_item.text();
}


void BlockItemPin::setBusType(int type_) {
	bus_type = type_;
}


void BlockItemPin::setAlignment(Qt::Alignment a) {
	align = a;
	_init(true);
}


void BlockItemPin::setText(const QString & t) {
	text_item.setText(t);
	_init(true);
}


void BlockItemPin::animAccept() {
	if (!((BlockView *)scene()->views().back())->isBlockAnimationEnabled() && anim_accept.state() != QAbstractAnimation::Running) return;
	anim_accept.setStartValue(pinSize());
	anim_accept.setEndValue(10.);
	anim_accept.start();
}


QRectF BlockItemPin::boundingRect() const {
	return ell_item.boundingRect().translated(ell_item.pos()) | text_item.boundingRect().translated(text_item.pos());
}


void BlockItemPin::setState(State s) {
	State os = state_;
	state_   = s;
	setBrush(br[int(state_)]);
	if (s == Accept && os != Accept) {
		animAccept();
	}
	update();
}


void BlockItemPin::saveState() {
	sstate_.push(state_);
}


bool BlockItemPin::restoreState() {
	if (sstate_.isEmpty()) return false;
	setState(sstate_.pop());
	return true;
}


void BlockItemPin::clearStateStack() {
	sstate_.clear();
}


void BlockItemPin::enlargePin(bool enlarge) {
	double sz = enlarge ? 12. : 7;
	if (anim_accept.state() == QAbstractAnimation::Running && enlarge) {
		anim_accept.stop();
		resizePin(sz);
	}
	if (!((BlockView *)scene()->views().back())->isBlockAnimationEnabled()) {
		resizePin(sz);
		return;
	}
	if (sz == anim_pin_size.endValue()) return;
	anim_pin_size.stop();
	anim_pin_size.setStartValue(pinSize());
	anim_pin_size.setEndValue(sz);
	anim_pin_size.start();
}


void BlockItemPin::resizePin(double r) {
	ell_item.setRect(-r, -r, r + r, r + r);
}


double BlockItemPin::pinSize() const {
	return ell_item.rect().width() / 2.;
}


void BlockItemPin::animationAccept() {
	if (anim_accept.endValue().toDouble() == 7.) return;
	anim_accept.setStartValue(pinSize());
	anim_accept.setEndValue(7.);
	anim_accept.start();
}


void BlockItemPin::_init(bool affect_parent) {
	text_item.setFont(AlignedTextItem::sceneFont(QApplication::font()));
	QRectF tbr     = text_item.boundingRect();
	const double r = 7.;
	ell_item.setRect(-r, -r, r + r, r + r);
	ell_item.setSpanAngle(16 * 180);
	text_item.resetTransform();
	text_item.setPos(0, -tbr.height() / 2.);
	text_item.setTransformOriginPoint(0, tbr.height() / 2.);
	switch (align) {
	case Qt::AlignBottom:
		ell_item.setStartAngle(16 * 0);
		text_item.setRotation(-90.);
		text_item.moveBy(0, -r * 1.5);
		break;
	case Qt::AlignRight:
		ell_item.setStartAngle(16 * 90);
		text_item.setRotation(0.);
		text_item.moveBy(-tbr.width() - r * 1.5, 0);
		break;
	case Qt::AlignTop:
		ell_item.setStartAngle(16 * 180);
		text_item.setRotation(-90.);
		text_item.moveBy(0, tbr.width() + r * 1.5);
		break;
	case Qt::AlignLeft:
		ell_item.setStartAngle(16 * 270);
		text_item.setRotation(0.);
		text_item.moveBy(r * 1.5, 0);
		break;
	default: break;
	}
	if (affect_parent && parent_) parent_->arrangePins();
}


void BlockItemPin::_reparent() {
	if (parentItem() == 0) return;
	if (qgraphicsitem_cast<BlockItem *>(parentItem()) == 0) return;
	QPen p = qgraphicsitem_cast<BlockItem *>(parentItem())->g_main.pen();
	ell_item.setPen(p);
	if (scene()) {
		text_item.setFont(AlignedTextItem::sceneFont(scene()->font()));
		QRectF tbr = text_item.boundingRect();
		text_item.resetTransform();
		text_item.setPos(0, -tbr.height() / 2.);
		text_item.setTransformOriginPoint(0, tbr.height() / 2.);
	}
}


QGraphicsView * BlockItemPin::_view() const {
	if (!scene()) return 0;
	if (scene()->views().isEmpty()) return 0;
	return scene()->views().back();
}


QVariant BlockItemPin::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value) {
	if (change == QGraphicsItem::ItemParentChange) _reparent();
	return QGraphicsItem::itemChange(change, value);
}


void BlockItemPin::hoverEnterEvent(QGraphicsSceneHoverEvent * e) {
	QGraphicsView * v = _view();
	bool m_pin_mc     = false;
	if (v) {
		qobject_cast<BlockView *>(v)->getPinMC(&m_pin_mc);
		QMetaObject::invokeMethod(
			v,
			[this, v]() { qobject_cast<BlockView *>(v)->pinHoverInOut(this); },
			Qt::QueuedConnection);
	}
	if ((state() != Disconnected) && !m_pin_mc) return;
	saveState();
	setState(BlockItemPin::Hover);
	enlargePin(true);
	update();
}


void BlockItemPin::hoverLeaveEvent(QGraphicsSceneHoverEvent * e) {
	QGraphicsView * v = _view();
	restoreState();
	enlargePin(false);
	update();
	if (v) {
		qobject_cast<BlockView *>(v)->pinHoverInOut(nullptr);
	}
}
