/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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/>.
 *
 */

//=============================================================================
//
// Helper struct for scaling coordinates
//
// TODO: rewrite into proper Transform class.
// Maybe implement as real matrix and/or floats if that is better/runs faster.
//
//=============================================================================

#ifndef AGS_SHARED_UTIL_SCALING_H
#define AGS_SHARED_UTIL_SCALING_H

#include "ags/shared/core/types.h"
#include "ags/shared/util/geometry.h"

namespace AGS3 {
namespace AGS {
namespace Shared {

class AxisScaling {
public:
	AxisScaling()
		: _scale(kUnit)
		, _unscale(kUnit)
		, _srcOffset(0)
		, _dstOffset(0) {
	}

	bool IsIdentity() const {
		return _scale == kUnit && _srcOffset == 0 && _dstOffset == 0;
	}

	bool IsTranslateOnly() const {
		return _scale == kUnit;
	}

	void Init(const int32_t src_length, const int32_t dst_length, const int32_t src_offset = 0, const int32_t dst_offset = 0) {
		_scale = kUnit;
		_unscale = kUnit;
		_srcOffset = src_offset;
		_dstOffset = dst_offset;

		if (src_length != 0) {
			int32_t scale = (dst_length << kShift) / src_length;
			if (scale != 0) {
				_scale = scale;
				_unscale = scale;
				int32_t scaled_val = ScaleDistance(src_length);
				if (scaled_val < dst_length)
					_scale++;
			}
		}
	}

	void SetSrcOffset(int32_t x) {
		_srcOffset = x;
	}

	void SetDstOffset(int32_t x) {
		_dstOffset = x;
	}

	inline int32_t GetSrcOffset() const {
		return _srcOffset;
	}

	inline int32_t ScalePt(int32_t x) const {
		return (((x - _srcOffset) * _scale) >> kShift) + _dstOffset;
	}
	inline int32_t ScaleDistance(int32_t x) const {
		return ((x * _scale) >> kShift);
	}
	inline int32_t UnScalePt(int32_t x) const {
		return ((x - _dstOffset) << kShift) / _unscale + _srcOffset;
	}
	inline int32_t UnScaleDistance(int32_t x) const {
		return (x << kShift) / _unscale;
	}

private:
	int32_t _scale;
	int32_t _unscale;
	int32_t _srcOffset;
	int32_t _dstOffset;
};

struct PlaneScaling {
	AxisScaling X;
	AxisScaling Y;

	bool IsIdentity() const {
		return X.IsIdentity() && Y.IsIdentity();
	}

	bool IsTranslateOnly() const {
		return X.IsTranslateOnly() && Y.IsTranslateOnly();
	}

	void Init(const Size &src_size, const Rect &dst_rect) {
		X.Init(src_size.Width, dst_rect.GetWidth(), 0, dst_rect.Left);
		Y.Init(src_size.Height, dst_rect.GetHeight(), 0, dst_rect.Top);
	}

	void Init(const Rect &src_rect, const Rect &dst_rect) {
		X.Init(src_rect.GetWidth(), dst_rect.GetWidth(), src_rect.Left, dst_rect.Left);
		Y.Init(src_rect.GetHeight(), dst_rect.GetHeight(), src_rect.Top, dst_rect.Top);
	}

	void SetSrcOffsets(int x, int y) {
		X.SetSrcOffset(x);
		Y.SetSrcOffset(y);
	}

	void SetDestOffsets(int x, int y) {
		X.SetDstOffset(x);
		Y.SetDstOffset(y);
	}

	inline Point Scale(const Point p) const {
		return Point(X.ScalePt(p.X), Y.ScalePt(p.Y));
	}

	inline Rect ScaleRange(const Rect r) const {
		return RectWH(X.ScalePt(r.Left), Y.ScalePt(r.Top), X.ScaleDistance(r.GetWidth()), Y.ScaleDistance(r.GetHeight()));
	}

	inline Point UnScale(const Point p) const {
		return Point(X.UnScalePt(p.X), Y.UnScalePt(p.Y));
	}

	inline Rect UnScaleRange(const Rect r) const {
		return RectWH(X.UnScalePt(r.Left), Y.UnScalePt(r.Top), X.UnScaleDistance(r.GetWidth()), Y.UnScaleDistance(r.GetHeight()));
	}
};

} // namespace Shared
} // namespace AGS
} // namespace AGS3

#endif
