/*
    Scan Tailor - Interactive post-processing tool for scanned pages.
    Copyright (C)  Joseph Artsimovich <joseph.artsimovich@gmail.com>

    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 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 General Public License for more details.

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

#ifndef PAGELAYOUT_H_
#define PAGELAYOUT_H_

#include "PageId.h"
#include "LayoutType.h"
#include <QPolygonF>
#include <QLineF>
#include <QString>

class QRectF;
class QTransform;
class QDomElement;
class QDomDocument;

namespace page_split
{

/**
 * The page layout comprises the following:
 * \li A rectangular outline, possibly affine-transformed.
 * \li Layout type indicator.
 * \li Zero, 1 or 2 cutters.
 *
 * Cutters are lines with *arbitrary endpoints* that have different meaning
 * depending on layout type.  The SINGLE_PAGE_UNCUT layout doesn't have any
 * cutters.  The TWO_PAGES layout has one cutter that splits the outline into
 * two pages.  The SINGLE_PAGE_CUT layout has two cutters that cut the outline
 * from two sides.  They don't have a special identity like being a left or
 * a right cutter.  Swapping them won't change the area they bound, and that
 * area is the only thing we care about.
 */
class PageLayout
{
public:
	enum Type {
		SINGLE_PAGE_UNCUT,
		SINGLE_PAGE_CUT,
		TWO_PAGES
	};
	
	/**
	 * \brief Constructs a null layout.
	 */
	PageLayout();

	/**
	 * \brief Constructs a SINGLE_PAGE_UNCUT layout.
	 */
	PageLayout(QRectF const& full_rect);
	
	/**
	 * \brief Constructs a SINGLE_PAGE_CUT layout.
	 */
	PageLayout(QRectF const& full_rect, QLineF const& cutter1, QLineF const& cutter2);

	/**
	 * \brief Constructs a TWO_PAGES layout.
	 */
	PageLayout(QRectF const full_rect, QLineF const& split_line);
	
	/**
	 * \brief Construct a page layout based on XML data.
	 */
	PageLayout(QDomElement const& layout_el);
	
	bool isNull() const { return m_uncutOutline.isEmpty(); }

	Type type() const { return m_type; }

	/**
	 * \brief Sets layout type and ensures the internal state
	 *        is consistent with the new type.
	 */
	void setType(Type type);

	LayoutType toLayoutType() const;

	QPolygonF const& uncutOutline() const { return m_uncutOutline; }

	/**
	 * We don't provide a method to set a polygon, but only a rectangle
	 * because we want to make sure the polygon stored internally corresponds
	 * to a rectangle.  For example, we want to be sure vertices 0 and 3
	 * comprise the line corresponding to a left edge of a rectangle.
	 */
	void setUncutOutline(QRectF const& outline);

	/**
	 * \brief Get a cutter line.
	 *
	 * \param idx Cutter index, from 0 inclusive to numCutters() exclusive.
	 * \return The cutter line with *arbitrary* endpoints.
	 */
	QLineF const& cutterLine(int idx) const;

	/**
	 * \brief Set a cutter line.
	 *
	 * \param idx Cutter index, from 0 inclusive to numCutters() exclusive.
	 * \param cutter The new cutter line with *arbitrary* endpoints.
	 */
	void setCutterLine(int idx, QLineF const& cutter);

	/**
	 * Unlike cutterLine(), which returns a line segment with arbitrary
	 * endpoints, inscribedCutterLine() returns a line segment with endpoints
	 * touching the edges of the outline polygon.
	 *
	 * \param idx Cutter index, from 0 inclusive to numCutters() exclusive.
	 * \return The cutter line segment with endpoints touching the outline polygon.
	 */
	QLineF inscribedCutterLine(int idx) const;

	/**
	 * \brief Return the number of cutters (0, 1 or 2) for the current layout type.
	 */
	int numCutters() const;

	/**
	 * \brief Get the number of pages (1 or 2) for this layout.
	 */
	int numSubPages() const;
	
	/**
	 * \brief For single page layouts, return the outline of that page,
	 *        otherwise return QPolygonF().
	 */
	QPolygonF singlePageOutline() const;
	
	/**
	 * \brief For two page layouts, return the outline of the left page,
	 *        otherwise return QPolygonF().
	 */
	QPolygonF leftPageOutline() const;
	
	/**
	 * \brief For two pages layouts, return the outline of the right page,
	 *        otherwise return QPolygonF().
	 */
	QPolygonF rightPageOutline() const;
	
	/**
	 * \brief Determines and calls the appropriate *PageOutline() method.
	 */
	QPolygonF pageOutline(PageId::SubPage page) const;
	
	/**
	 * Returns an affine-transformed version of this layout.
	 */
	PageLayout transformed(QTransform const& xform) const;
	
	QDomElement toXml(QDomDocument& doc, QString const& name) const;
private:
	PageLayout(QPolygonF const& outline, QLineF const& cutter1,
		QLineF const& cutter2, Type type);
	
	static Type typeFromString(QString const& str);
	
	static QString typeToString(Type type);

	static QLineF extendToCover(QLineF const& line, QPolygonF const& poly);

	static void ensureSameDirection(QLineF const& line1, QLineF& line2);

	static void maybeAddIntersectionPoint(
		QPolygonF& poly, QLineF const& line1, QLineF const& line2);
	
	/**
	 * This polygon always corresponds to a rectangle, unless it's empty.
	 * Its vertex 0 corresponds to top-left vertex of a rectangle, and
	 * it goes clockwise from there, ending at vertex 3.
	 */
	QPolygonF m_uncutOutline;

	/**
	 * In case of two page layouts, both cutters refer to the split line,
	 * and both are supposed to be equal.
	 */
	QLineF m_cutter1;
	QLineF m_cutter2;
	
	Type m_type;
};

} // namespace page_split

#endif
