/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/

#ifndef GUIDEMANAGERCORE_H
#define GUIDEMANAGERCORE_H

#include <QPair>

#include "undoobject.h"
#include "scribusapi.h"

class QColor;
class ScPage;
class ScPainter;
class UndoManager;
class ScribusDoc;

using Guides = QList<double>;

/*! \brief Core manipulation with the guides.
Basic idea:
- guides are handled "on the fly", no by modal dialog.
- 2 types of guides: Standard = created one by one.
Auto = created by automatic division algorithms.
Automatic guides are kept by 2 ways: 1st its parameters (m_horizontalAutoCount etc.)
to setup the GUI and compute guides itself. 2nd the horizontalAutoG etc. set
with precomputed values from GUI actions for drawing.
- user can move only Stnadard ones
- Auto guides can be deleted only in manipulation dialog
- Auto guides are painted in different color / (probably) with diff. line.
\author Petr vanek <petr@scribus.info>
*/
class SCRIBUS_API GuideManagerCore
{
public:
	GuideManagerCore();
	GuideManagerCore(ScPage* parentPage);
	~GuideManagerCore() = default;

	enum GuideType {Standard, Auto};

	void addHorizontal(double value, GuideType type);
	void addHorizontals(Guides values, GuideType type);
	void addVertical(double value, GuideType type);
	void addVerticals(Guides values, GuideType type);
	void deleteHorizontal(double value, GuideType type);
	void deleteVertical(double value, GuideType type);
	void moveHorizontal(double from, double to, GuideType type);
	void moveVertical(double from, double to, GuideType type);

	Guides horizontals(GuideType type) const;
	Guides verticals(GuideType type) const;
	double horizontal(uint ix, GuideType type) const;
	double vertical(uint ix, GuideType type) const;

	Guides getAutoHorizontals(const ScPage* page = nullptr) const;
	Guides getAutoVerticals(const ScPage* page = nullptr) const;

	void clearHorizontals(GuideType type);
	void clearVerticals(GuideType type);

	void copy(GuideManagerCore *target) const;
	void copy(GuideManagerCore *target, GuideType type) const;

	void drawPage(ScPainter *p, ScribusDoc *doc, double lineWidth) const;

	int isMouseOnHorizontal(double low, double high, GuideType type) const;
	int isMouseOnVertical(double low, double high, GuideType type) const;

	void setPage(ScPage *p);

	QPair<double, double> topLeft(double x, double y) const;
	QPair<double, double> topRight(double x, double y) const;
	QPair<double, double> bottomLeft(double x, double y) const;
	QPair<double, double> bottomRight(double x, double y) const;

	//! \brief Properties for Auto guides remembrance. See GuideManager.
	int horizontalAutoCount() const { return m_horizontalAutoCount; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	void setHorizontalAutoCount(int val) { m_horizontalAutoCount = val; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	int verticalAutoCount() const { return m_verticalAutoCount; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	void setVerticalAutoCount(int val) { m_verticalAutoCount = val; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	double horizontalAutoGap() const { return m_horizontalAutoGap; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	double verticalAutoGap() const { return m_verticalAutoGap; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	void setHorizontalAutoGap(double gap) { m_horizontalAutoGap = gap; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	void setVerticalAutoGap(double gap) { m_verticalAutoGap = gap; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	int horizontalAutoRefer() const { return m_horizontalAutoRefer; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	void setHorizontalAutoRefer(int val) { m_horizontalAutoRefer = val; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	int verticalAutoRefer() const { return m_verticalAutoRefer; }
	//! \brief Properties for Auto guides remembrance. See GuideManager.
	void setVerticalAutoRefer(int val) { m_verticalAutoRefer = val; }

	/*! \brief Recalculate the selection position and measurements for the current page.
	It's used for automatic guides position. It's called for every
	selection GUI widgets change to handle selection change only
	when needed. */
	void resetSelectionForPage(ScPage* page);

	/*! \brief Selection/group coordinates
	It's used to simulate the original selection "freezed in time"
	for parent page */
	double gx {0.0};
	double gy {0.0};
	double gw {0.0};
	double gh {0.0};


private:
	UndoManager * const m_undoManager;
	ScPage* m_page {nullptr};
	Guides m_horizontalStdG;
	Guides m_verticalStdG;
	Guides m_horizontalAutoG;
	Guides m_verticalAutoG;

	double m_horizontalAutoGap {0.0};
	double m_verticalAutoGap {0.0};
	int m_horizontalAutoCount {0};
	int m_verticalAutoCount {0};
	int m_horizontalAutoRefer {0};
	int m_verticalAutoRefer {0};

	double closestHorAbove(double y) const;
	double closestHorBelow(double y) const;
	double closestVertLeft(double x) const;
	double closestVertRight(double x) const;
};


/*! \brief A separate class for Guides IO operations in reading or closing
the documents.
\author Petr Vanek <petr@scribus.info>
*/
class SCRIBUS_API GuideManagerIO
{
	public:
		GuideManagerIO() = default;
		~GuideManagerIO() = default;

		/*! \brief Read the guides from XML attribute (file opening).
		It's statis method sou you can call it without instance initialized:
		GuideManagerIO::readHorizontalGuides(foo blah);
		\param guideString a string with all values separated by space (' '). E.g. "1.0 23.17 6"
		\param page a reference to the Page object to append the separated guideString values
		\param type Guide type to load
		\param useOldGuides A little bit hacking here. The guides were stored in a little mess
		in the ancient times. So when is the obsolete XML attribute found in reading document
		the old reading method is used. All guides are saved in new format then. */
		static void readHorizontalGuides(const QString& guideString,
										 ScPage *page,
										 GuideManagerCore::GuideType type,
										 bool useOldGuides = false);

		/*! \brief Read the guides from XML attribute (file opening).
		It's statis method sou you can call it without instance initialized:
		GuideManagerIO::readVerticalGuides(foo blah);
		\param guideString a string with all values separated by space (' '). E.g. "1.0 23.17 6"
		\param page a reference to the Page object to append the separated guideString values
		\param type Guide type to load
		\param useOldGuides A little bit hacking here. The guides were stored in a little mess
		in the ancient times. So when is the obsolete XML attribute found in reading document
		the old reading method is used. All guides are saved in new format then. */
		static void readVerticalGuides(const QString& guideString,
									   ScPage *page,
									   GuideManagerCore::GuideType type,
									   bool useOldGuides = false);

		static QString writeHorizontalGuides(const ScPage *page, GuideManagerCore::GuideType type);
		static QString writeVerticalGuides(const ScPage *page, GuideManagerCore::GuideType type);

		static void readSelection(const QString& guideString, ScPage *page);
		static QString writeSelection(const ScPage *page);
};

#endif
