/*
    Scan Tailor - Interactive post-processing tool for scanned pages.
    Copyright (C) 2007-2015  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/>.
*/

#include "OrthogonalRotation.h"
#include <QSize>
#include <QSizeF>
#include <QPointF>
#include <QTransform>
#include <QDomDocument>
#include <QDomElement>
#include <QString>
#include <assert.h>

OrthogonalRotation::OrthogonalRotation(QDomElement const& el)
:	m_degrees(0)
{
	int const degrees = el.attribute("degrees").toInt();
	switch (degrees) {
		case 0:
		case 90:
		case 180:
		case 270:
			m_degrees = degrees;
	}
}

QDomElement
OrthogonalRotation::toXml(QDomDocument& doc, QString const& name) const
{
	QDomElement el(doc.createElement(name));
	el.setAttribute("degrees", m_degrees);
	return el;
}

void
OrthogonalRotation::nextClockwiseDirection()
{
	m_degrees += 90;
	if (m_degrees == 360) {
		m_degrees = 0;
	}
}

void
OrthogonalRotation::prevClockwiseDirection()
{
	m_degrees -= 90;
	if (m_degrees == -90) {
		m_degrees = 270;
	}
}

QSize
OrthogonalRotation::rotate(QSize const& dimensions) const
{
	if (m_degrees == 90 || m_degrees == 270) {
		return QSize(dimensions.height(), dimensions.width());
	} else {
		return dimensions;
	}
}

QSize
OrthogonalRotation::unrotate(QSize const& dimensions) const
{
	return rotate(dimensions);
}

QSizeF
OrthogonalRotation::rotate(QSizeF const& dimensions) const
{
	if (m_degrees == 90 || m_degrees == 270) {
		return QSizeF(dimensions.height(), dimensions.width());
	} else {
		return dimensions;
	}
}

QSizeF
OrthogonalRotation::unrotate(QSizeF const& dimensions) const
{
	return rotate(dimensions);
}

QPointF
OrthogonalRotation::rotate(
	QPointF const& point, double const xmax, double const ymax) const
{
	QPointF rotated;
	
	switch (m_degrees) {
	case 0:
		rotated = point;
		break;
	case 90:
		rotated.setX(ymax - point.y());
		rotated.setY(point.x());
		break;
	case 180:
		rotated.setX(xmax - point.x());
		rotated.setY(ymax - point.y());
		break;
	case 270:
		rotated.setX(point.y());
		rotated.setY(xmax - point.x());
		break;
	default:
		assert(!"Unreachable");
	}
	
	return rotated;
}

QPointF
OrthogonalRotation::unrotate(
	QPointF const& point, double const xmax, double const ymax) const
{
	QPointF unrotated;
	
	switch (m_degrees) {
	case 0:
		unrotated = point;
		break;
	case 90:
		unrotated.setX(point.y());
		unrotated.setY(xmax - point.x());
		break;
	case 180:
		unrotated.setX(xmax - point.x());
		unrotated.setY(ymax - point.y());
		break;
	case 270:
		unrotated.setX(ymax - point.y());
		unrotated.setY(point.x());
		break;
	default:
		assert(!"Unreachable");
	}
	
	return unrotated;
}

QTransform
OrthogonalRotation::transform(QSizeF const& dimensions) const
{
	QTransform t;
	
	switch (m_degrees) {
	case 0:
		return t;
	case 90:
		t.translate(dimensions.height(), 0);
		break;
	case 180:
		t.translate(dimensions.width(), dimensions.height());
		break;
	case 270:
		t.translate(0, dimensions.width());
		break;
	default:
		assert(!"Unreachable");
	}
	
	t.rotate(m_degrees);
	
	return t;
}
