/***************************************************************************
 *   Copyright (C) 2005 by David Saxton                                    *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#ifndef RESIZEOVERLAY_H
#define RESIZEOVERLAY_H

// This file contains class definitions for different types of resizing and rotating

//#include <canvas.h> // 2018.10.16 - not needed
#include "canvasitems.h"
#include <QPointer>
#include <QMap>
#include <QObject>
#include <QList>

class MechanicsItem;
class ResizeHandle;
class ResizeOverlay;
class QEvent;

typedef QMap< int, QPointer<ResizeHandle> > ResizeHandleMap;

/**
@author David Saxton
*/
class ResizeHandle : /* public QObject, */ public KtlQCanvasRectangle
{
	Q_OBJECT
public:
	/**
	 * Convenience enumeration for resize handle positioning. Note: this class
	 * does not make use of the values in this enumeration - it is just
	 * provided here for use by other classes.
	 */
	enum ResizeHandlePosition
	{
		rhp_none,
		rhp_topLeft,
		rhp_topMiddle,
		rhp_topRight,
		rhp_middleRight,
		rhp_bottomRight,
		rhp_bottomMiddle,
		rhp_bottomLeft,
		rhp_middleLeft,
		rhp_center,
		rhp_start,
		rhp_end
	};

	enum DrawType
	{
		// Draws a simple rectangle
		dt_point_rect,

		// Crosshair
		dt_point_crosshair,

		// Straight arrows in various directions
		dt_resize_forwardsDiagonal,
		dt_resize_backwardsDiagonal,
		dt_resize_vertical,
		dt_resize_horizontal,

		// Arrows as part of an arc
		dt_rotate_topLeft,
		dt_rotate_topRight,
		dt_rotate_bottomRight,
		dt_rotate_bottomLeft
	};

	ResizeHandle( ResizeOverlay *resizeOverlay, int id, DrawType drawType, int xsnap, int ysnap );
	~ResizeHandle() override;

	int id() const { return m_id; }

	void setDrawType( DrawType drawType );
	void moveRH( double x, double y );
	void setHover( bool hover );

	static const QPixmap& handlePixmap( DrawType drawType, bool hover );

	QPolygon areaPoints () const override;

public slots:
	void slotMoveByX( double dx ) { moveBy( dx, 0 ); }
	void slotMoveByY( double dy ) { moveBy( 0, dy ); }

signals:
	void rhMovedBy( int id, double dx, double dy );
	void rhMovedByX( double dx );
	void rhMovedByY( double dy );

protected:
	void drawShape( QPainter &p ) override;

	DrawType m_drawType;
	bool b_hover; // If true, then paint resize handle for mouse hovering over
	int m_id;
	int m_xsnap;
	int m_ysnap;
	ResizeOverlay *p_resizeOverlay;

};
typedef QList<ResizeHandle*> ResizeHandleList;

/**
@author David Saxton
*/
class ResizeOverlay : public QObject
{
	Q_OBJECT
public:
	ResizeOverlay( Item *parent );
	~ResizeOverlay() override;


	Item *parentItem() const { return p_item; }

	/**
	 * Shows / hides the resize handles. They are hidden by default.
	 */
	void showResizeHandles( bool show );
	/**
	 * Sets the visibility. Visibility is true by default.
	 */
	void setVisible( bool visible );
	/**
	 * Reinherit this function to determine whether the X coordinate of the spot
	 * that the resize handle has moved into is valid or not
	 */
	virtual bool isValidXPos( ResizeHandle *rh ) { Q_UNUSED(rh); return true; }
	/**
	 * Reinherit this function to determine whether the Y coordinate of the spot
	 * that the resize handle has moved into is valid or not
	 */
	virtual bool isValidYPos( ResizeHandle *rh ) { Q_UNUSED(rh); return true; }

public slots:
	void slotMoveAllResizeHandles( double dx, double dy );

protected slots:
	virtual void slotResizeHandleMoved( int id, double dx, double dy ) = 0;

protected:
	/**
	 * Connects up the given resize handles so that they are always kept at the
	 * same horizontal coordinate
	 */
	void syncX( ResizeHandle *rh1, ResizeHandle *rh2 );
	void syncX( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 );
	/**
	 * Connects up the given resize handles so that they are always kept at the
	 * same vertical coordinate
	 */
	void syncY( ResizeHandle *rh1, ResizeHandle *rh2 );
	void syncY( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 );
	/**
	 * Returns a pointer to the ResizeHandle with the given id, or 0 if no such
	 * handle exists
	 */
	ResizeHandle *resizeHandle( int id );
	/**
	 * Creates and attaches the resize handle with the given DrawType. If a
	 * ResizeHandle with the given id exists, will return a pointer to that
	 * instead
	 */
	ResizeHandle *createResizeHandle( int id, ResizeHandle::DrawType drawType, int xsnap = 1, int ysnap = 1 );
	/**
	 * Removes the resize handle with the given id
	 */
	void removeResizeHandle( int id );

	Item *p_item;
	ResizeHandleMap m_resizeHandleMap;
	bool b_showResizeHandles;
	bool b_visible;
};


/**
@author David Saxton
*/
class MechanicsItemOverlay : public ResizeOverlay
{
Q_OBJECT
public:
	MechanicsItemOverlay( MechanicsItem *parent );
	~MechanicsItemOverlay() override;


public slots:
	void slotUpdateResizeHandles();

protected slots:
	void slotResizeHandleMoved( int id, double dx, double dy ) override;

protected:
	ResizeHandle *m_tl;
	ResizeHandle *m_tm;
	ResizeHandle *m_tr;
	ResizeHandle *m_mr;
	ResizeHandle *m_br;
	ResizeHandle *m_bm;
	ResizeHandle *m_bl;
	ResizeHandle *m_ml;
	ResizeHandle *m_mm;
	MechanicsItem *p_mechanicsItem;
};


/**
@author David Saxton
*/
class RectangularOverlay : public ResizeOverlay
{
Q_OBJECT
public:
	RectangularOverlay( Item *item, int xsnap = 1, int ysnap = 1 );
	void removeTopMiddle();
	void removeBotMiddle();
	/**
	 * Get the size rectangle from the position of the handles. If the size
	 * is invalid (e.g. the parent Item does not consider it a valid size,
	 * then *ok is set to false; otherwise to true.
	 * @returns the sizerect, regardless of whether or not it is valid
	 */
	QRect getSizeRect( bool *ok = nullptr, bool *widthOk = nullptr, bool *heightOk = nullptr ) const;

	bool isValidXPos( ResizeHandle *rh ) override;
	bool isValidYPos( ResizeHandle *rh ) override;

public slots:
	void slotUpdateResizeHandles();

protected slots:
	void slotResizeHandleMoved( int id, double dx, double dy ) override;


protected:
	ResizeHandle *m_tl;
	ResizeHandle *m_tm;
	ResizeHandle *m_tr;
	ResizeHandle *m_mr;
	ResizeHandle *m_br;
	ResizeHandle *m_bm;
	ResizeHandle *m_bl;
	ResizeHandle *m_ml;
};


/**
@author David Saxton
*/
class LineOverlay : public ResizeOverlay
{
	Q_OBJECT
	public:
		LineOverlay( Item * parent );
		QPoint startPoint() const;
		QPoint endPoint() const;

	public slots:
		void slotUpdateResizeHandles();

	protected slots:
		void slotResizeHandleMoved( int id, double dx, double dy ) override;
		

	protected:
		ResizeHandle * m_pStart;
		ResizeHandle * m_pEnd;
};

#endif
