#pragma once

#include "ap_AggPlusEnums.h"
#include "ap_AggPlusTypes.h"
#include "agg_trans_affine.h"

namespace NSHtmlRenderer
{
	class CMatrix  
	{
	public:
		CMatrix(double m11, double m12, double m21, double m22, double dx, double dy) : m_agg_mtx(m11, m12, m21, m22, dx, dy)
		{
		}

		CMatrix() : m_agg_mtx() 
		{
		}
		
		~CMatrix()
		{
		}
		
		void Translate(double offsetX, double offsetY, Aggplus::MatrixOrder order = Aggplus::MatrixOrderPrepend)
		{
			if (order == Aggplus::MatrixOrderPrepend) 
			{ 
				m_agg_mtx.premultiply(agg::trans_affine_translation(offsetX, offsetY)); 
			}
			else                            
			{ 
				m_agg_mtx.multiply(agg::trans_affine_translation(offsetX, offsetY)); 
			}
		}

		void Scale(double scaleX, double scaleY, Aggplus::MatrixOrder order = Aggplus::MatrixOrderPrepend)
		{	
			if (order == Aggplus::MatrixOrderPrepend) 
			{ 
				m_agg_mtx.premultiply(agg::trans_affine_scaling(scaleX, scaleY)); 
			}
			else                            
			{ 
				m_agg_mtx.multiply(agg::trans_affine_scaling(scaleX, scaleY)); 
			}
		}

		void Shear(double shearX, double shearY, Aggplus::MatrixOrder order = Aggplus::MatrixOrderPrepend)
		{
			if (order == Aggplus::MatrixOrderPrepend) 
			{ 
				m_agg_mtx.premultiply(agg::trans_affine_skewing(shearX, shearY)); 
			}
			else                            
			{ 
				m_agg_mtx.multiply(agg::trans_affine_skewing(shearX, shearY)); 
			}
		}

		void TransformPoint(double& x, double& y)
		{
			m_agg_mtx.transform(&x, &y);
		}

		void Rotate(double angle, Aggplus::MatrixOrder order = Aggplus::MatrixOrderPrepend)
		{
			if (order == Aggplus::MatrixOrderPrepend) 
			{ 
				m_agg_mtx.premultiply(agg::trans_affine_rotation(agg::deg2rad(angle))); 
			}
			else
			{ 
				m_agg_mtx.multiply(agg::trans_affine_rotation(agg::deg2rad(angle))); 
			}
		}

		void RotateAt(double angle, double x, double y, Aggplus::MatrixOrder order = Aggplus::MatrixOrderPrepend)
		{
			Translate(-x, -y, order);
			if (order == Aggplus::MatrixOrderPrepend) 
			{ 
				m_agg_mtx.premultiply(agg::trans_affine_rotation(agg::deg2rad(angle))); 
			}
			else                            
			{ 
				m_agg_mtx.multiply(agg::trans_affine_rotation(agg::deg2rad(angle))); 
			}
			Translate(x, y, order);
		}

		void Multiply(const CMatrix* matrix, Aggplus::MatrixOrder order = Aggplus::MatrixOrderPrepend)
		{
			if (order == Aggplus::MatrixOrderPrepend) 
			{ 
				m_agg_mtx.premultiply(matrix->m_agg_mtx); 
			}
			else
			{ 
				m_agg_mtx.multiply(matrix->m_agg_mtx); 
			}
		}

		double OffsetX() const
		{
			double M[6]; m_agg_mtx.store_to(M);
			return (M[4]);
		}
		double OffsetY() const
		{
			double M[6]; m_agg_mtx.store_to(M);
			return (M[5]);
		}
		
		bool GetElements(float* m) const
		{
			double M[6]; m_agg_mtx.store_to(M);
			m[0]=(float)M[0];
			m[1]=(float)M[1];
			m[2]=(float)M[2];
			m[3]=(float)M[3];
			m[4]=(float)M[4];
			m[5]=(float)M[5];
			return true;
		}

		bool GetElements(double* m) const
		{
			m_agg_mtx.store_to(m);
			return true;
		}

		void Reset()
		{
			m_agg_mtx.reset();
		}

		const CMatrix& operator=(const CMatrix& Src)
		{
			m_agg_mtx	= Src.m_agg_mtx;
			return		*this;
		}

		AVSINLINE bool IsEqualMain(const CMatrix* pMatrix)
		{
			if (fabs(m_agg_mtx.sx - pMatrix->m_agg_mtx.sx) < 0.001 && 
				fabs(m_agg_mtx.sy - pMatrix->m_agg_mtx.sy) < 0.001 &&
				fabs(m_agg_mtx.shx - pMatrix->m_agg_mtx.shx) < 0.001 &&
				fabs(m_agg_mtx.shy - pMatrix->m_agg_mtx.shy) < 0.001)
				return true;
			return false;
		}

		bool Invert()
		{
			double d = m_agg_mtx.determinant();
			if (0.0001 >= abs(d))
				return false;

			m_agg_mtx.invert();
			return true;
		}

		//Temp
		//Used in X_BrushLinearGradient constructor
		double z_Rotation() const 
		{ 
			return agg::rad2deg(m_agg_mtx.rotation()); 
		}

	public:
		agg::trans_affine m_agg_mtx;
	};
}