#include <d3dx9.h>
#include <string.h>
#include "mof/Matrix2D.hpp"
#include "mof/ConsoleIO.hpp"

const int DIMENSION = 2;


mof::Matrix2D::Matrix2D()
: m_pImpl(new Array)
{
	for(int i = 0 ; i <= DIMENSION ; ++i){
		for(int j = 0 ; j <= DIMENSION ; ++j){
			if(i == j)m_pImpl->elements[i][j] = 1;
			else m_pImpl->elements[i][j] = 0;
		}
	}
}

mof::Matrix2D::Matrix2D(const Matrix2D& obj )
: m_pImpl(new Array)
{
	*m_pImpl = *obj.m_pImpl;
}

mof::Matrix2D::Matrix2D(const mof::Matrix2D::Array & arr)
: m_pImpl(new Array)
{
	*m_pImpl = arr;
}


mof::Matrix2D::~Matrix2D(){
}


mof::real mof::Matrix2D::at(int row , int column) const{
	return m_pImpl->elements[row][column];
}

mof::Matrix2D::Array mof::Matrix2D::getArray() const{
	return *m_pImpl;
}


mof::Matrix2D mof::Matrix2D::createIdentity(){
	return mof::Matrix2D();
}


mof::Matrix2D mof::Matrix2D::createTransposed(mof::Matrix2D& matrix){
	Matrix2D transposed;
	for(int i = 0 ; i <= DIMENSION ; i++){
		for(int j = 0 ; j <= DIMENSION ; j++){
			transposed.m_pImpl->elements[j][i] = matrix.m_pImpl->elements[i][j];
		}
	}
	return transposed;
}



mof::Matrix2D mof::Matrix2D::createRotation(const mof::Vector2D& position){
	Matrix2D matrix;
	matrix.m_pImpl->elements[DIMENSION][0] = position.x;
	matrix.m_pImpl->elements[DIMENSION][1] = position.y;
	return matrix;
}


mof::Matrix2D mof::Matrix2D::createTranslation(const mof::Vector2D& position){
	Matrix2D matrix;
	matrix.m_pImpl->elements[DIMENSION][0] = position.x;
	matrix.m_pImpl->elements[DIMENSION][1] = position.y;
	return matrix;
}

mof::Matrix2D mof::Matrix2D::createScaling(const mof::Vector2D& scaling){
	Matrix2D matrix;
	matrix.m_pImpl->elements[0][0] = scaling.x;
	matrix.m_pImpl->elements[1][1] = scaling.y;
	return matrix;
}


mof::Vector2D mof::Matrix2D::getTranslation(const mof::Matrix2D& matrix){
	return mof::Vector2D(
		matrix.m_pImpl->elements[DIMENSION][0] ,
		matrix.m_pImpl->elements[DIMENSION][1]
		);
}

mof::Vector2D mof::Matrix2D::getDiagonal() const{
	return mof::Vector2D(
		m_pImpl->elements[0][0] ,
		m_pImpl->elements[1][1]
		);
}

		
mof::Matrix2D mof::Matrix2D::operator *(const mof::Matrix2D& matrix) const{
	mof::Matrix2D multiplied;
	for(int i = 0 ; i <= DIMENSION ; i++){
		for(int j = 0 ; j <= DIMENSION ; j++){
			mof::real sum = 0;
			for(int k = 0 ; k <= DIMENSION ; k++){
				sum += m_pImpl->elements[i][k] * matrix.m_pImpl->elements[k][j];
			}
			multiplied.m_pImpl->elements[i][j] = sum;
		}
	}
	return multiplied;
}


mof::Matrix2D mof::Matrix2D::operator +(const mof::Matrix2D& matrix) const{
	mof::Matrix2D result;
	for(int i = 0 ; i <= DIMENSION ; i++){
		for(int j = 0 ; j <= DIMENSION ; j++){
			result.m_pImpl->elements[i][j] = 
				m_pImpl->elements[i][j] + matrix.m_pImpl->elements[i][j];
		}
	}
	return result;
}


mof::Matrix2D mof::Matrix2D::operator -(const mof::Matrix2D& matrix) const{
	mof::Matrix2D result;
	for(int i = 0 ; i <= DIMENSION ; i++){
		for(int j = 0 ; j <= DIMENSION ; j++){
			result.m_pImpl->elements[i][j] = 
				m_pImpl->elements[i][j] - matrix.m_pImpl->elements[i][j];
		}
	}
	return result;
}


mof::Matrix2D mof::operator *(const mof::Matrix2D& matrix , mof::real f){
	mof::Matrix2D result;
	for(int i = 0 ; i <= DIMENSION ; i++){
		for(int j = 0 ; j <= DIMENSION ; j++){
			result.m_pImpl->elements[i][j] = 
				matrix.m_pImpl->elements[i][j] * f;
		}
	}
	return result;
}
		
mof::Matrix2D mof::operator *(mof::real f , mof::Matrix2D const& matrix){
	return matrix * f;
}


mof::Vector2D mof::operator *(const mof::Vector2D& vec , const mof::Matrix2D& matrix) {
	mof::real input[3] = {vec.x , vec.y , 1};
	mof::real output[3];
	for(int i = 0 ; i < DIMENSION ; i++){
		mof::real sum = 0;
		for(int k = 0 ; k <= DIMENSION ; k++){
			sum += input[k] * matrix.at(k , i);
		}
		output[i] = sum;
		
	}
	return mof::Vector2D(output[0] , output[1]);
}


mof::Rectangle<int> mof::Matrix2D::toBoundingBox(){
	mof::Vector2D begin = mof::Vector2D(0 , 0) * (*this);
	mof::Vector2D end = mof::Vector2D(1 , 1) * (*this);
	return mof::Rectangle<int>( (int)begin.x , (int)begin.y , (int)end.x , (int)end.y );
}
