/*
    QAD - Qt ADvanced

    Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef BLOCKBUSITEM_H
#define BLOCKBUSITEM_H

#include "blockitem.h"
#include "qad_blockview_export.h"


class QAD_BLOCKVIEW_EXPORT BlockBusItem
	: public QGraphicsObject
	, public PropertyStorage {
	Q_OBJECT
	Q_INTERFACES(QGraphicsItem)
	Q_PROPERTY(double pointSize READ pointSize WRITE setPointSize DESIGNABLE false SCRIPTABLE false)
	friend class BlockView;

public:
	BlockBusItem(bool temp = false);
	BlockBusItem(const BlockBusItem & other);
	~BlockBusItem() { ; }

	struct QAD_BLOCKVIEW_EXPORT PointInfo {
		enum class Type {
			None,
			Node, // point > 2 segments
			Endpoint,
			Segment
		};
		Type type          = Type::None;
		BlockItemPin * pin = nullptr; // if tEndpoint and pin connected
	};

	void setGridStep(double gs) { grid_step = gs; }
	void setEndpointsNumber(int num) { max_ep = num; }
	void setImages(const QImage & bus, const QImage & end = QImage()) {
		im_bus = bus;
		im_end = end;
		update();
	}
	void setBusImageScale(double s) {
		im_bus_scale = s;
		update();
	}
	void setEndpointImageScale(double s) {
		im_end_scale = s;
		update();
	}
	void setBusType(int type_) { bus_type = type_; }
	void setBusName(const QString & name) { bus_name = name; }
	int busType() const { return bus_type; }
	QString busName() const { return bus_name; }
	double busImageScale() const { return im_bus_scale; }
	double endpointImageScale() const { return im_end_scale; }
	void appendPoint(const QPointF & p);
	void appendPoint(qreal x, qreal y);
	void movePolyline(const QPointF & dp);
	void clear();
	void setPen(const QPen & p) {
		p_ = p;
		update();
	}
	QPen pen() const { return p_; }
	void setBrush(const QBrush & b) {
		b_ = b;
		update();
	}
	QBrush brush() const { return b_; }
	double width() const { return pen_width; }
	void setWidth(const double & w);
	void setColor(const QColor & c);
	void setSquareNodes(bool yes);
	void markAsInput();
	void markAsOutput();
	void unmark();
	void simplify(bool full = true);
	bool hasNullSegment() const;
	void adjustLine();
	int endpointCount() const;
	bool isBusSelected() const { return selSegment >= 0 || selPoint >= 0; }
	QList<BlockItem *> connectedBlocks() const;
	QList<BlockItemPin *> connectedPins() const;
	PointInfo pointInfo(QPointF pos) const;

	void setBusState(bool state);
	bool busState() const { return state_ > 0; }
	void clearBusState();

	QByteArray save() const;
	void load(const QByteArray & data);
	BlockBusItem * copy() const;

	void saveState();
	void restoreState();

	PIValueTree & values() { return value_tree; }

	enum {
		Type = UserType + 2
	};

protected:
	void _init();
	void reconnect();
	void updateGeometry();
	bool checkDelete();
	void emitAction(BlockItemBase::Action a);
	void testPoint(QPointF pos, int * sel_point, int * sel_segment, bool for_trace = false) const;
	int addPoint(const QPointF & point, bool update = true);
	int segmentPointPair(int point, int * seg = 0) const;
	int pointSegmentsCount(int point, QList<int> * segs = 0) const;
	void removePoint(int index);
	void removeSegment(int index);
	void movePoint(int index, const QPointF & dp);
	QVector<int> endpoints() const;
	QVector<int> endpointLine(int ep, double angle) const;
	int neighborSegmentPoint(int point, int * seg) const;
	int type() const { return Type; }
	QRectF boundingRect() const;
	bool sceneEvent(QEvent * e);
	void hoverEnterEvent(QGraphicsSceneHoverEvent * e);
	void hoverMoveEvent(QGraphicsSceneHoverEvent * e);
	void hoverLeaveEvent(QGraphicsSceneHoverEvent * e);
	void mousePressEvent(QGraphicsSceneMouseEvent * e);
	void mouseMoveEvent(QGraphicsSceneMouseEvent * e);
	void mouseReleaseEvent(QGraphicsSceneMouseEvent * e);
	void paint(QPainter * p, const QStyleOptionGraphicsItem * o, QWidget * w = 0);

	QPointF lp, new_start, new_end, press_pos, qp;
	QPen p_, ph, pu, pa, pr, pn;
	QBrush b_, bh, bu, ba, br;
	QString tt, bus_name;
	QList<QPair<int, int>> segments, ends_ind, segments_s, ends_ind_s;
	QMap<int, BlockItemPin *> connections_;
	QVector<int> ends, ends_s;
	QImage im_bus, im_end;
	QPolygonF pol, bpol, pol_s;
	Qt::KeyboardModifiers mm_mods;
	PIValueTree value_tree;
	bool temp_, square_node;
	double pen_width, grid_step, im_bus_scale, im_end_scale;
	int selPoint, selSegment, max_ep, bus_type, state_;
	bool moved, deleted, mark_in, mark_out, new_segment, mm_cancel, lm_point;

private:
	double pointSize() const { return point_size; }
	void setPointSize(double s);

	double point_size;
	QPropertyAnimation anim_point_size;
};


inline QDataStream & operator<<(QDataStream & s, const BlockBusItem * b) {
	s << b->save();
	return s;
}
inline QDataStream & operator>>(QDataStream & s, BlockBusItem *& b) {
	QByteArray ba;
	s >> ba;
	b = new BlockBusItem();
	b->load(ba);
	return s;
}


#endif // BLOCKBUSITEM_H
