#include "Matrix.h"

Matrix::Matrix() : _matrix{ 0 }
{	
}

Matrix::Matrix(std::initializer_list<float> inputList) : _matrix{ 0 }
{
	if (inputList.size() != ROWS * COLS)
	{
		throw "You did not provide enough points to create the matrix";
	}

	auto listContainer = inputList.begin();
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			_matrix[i][j] = *listContainer++;
		}
	}
}

Matrix::Matrix(const float arrayIn[ROWS][COLS])
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			_matrix[i][j] = arrayIn[i][j];
		}
	}
}

Matrix::Matrix(const Matrix& other)
{
	Copy(other);
}

Matrix::~Matrix()
{
	
}

float Matrix::GetM(const int row, const int column) const
{
	return GetMatrixCell(row, column);
}

float Matrix::GetMatrixCell(const int row, const int column) const
{
	return _matrix[row][column];
}

void Matrix::SetM(const int row, const int column, const float value)
{
	SetMatrixCell(row, column, value);
}

void Matrix::SetMatrixCell(const int row, const int column, const float value)
{
	_matrix[row][column] = value;
}

void Matrix::FromArray(const float arrayIn[ROWS][COLS])
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			_matrix[i][j] = arrayIn[i][j];
		}
	}
}

Matrix& Matrix::operator= (const Matrix& rhs)
{
	if (this != &rhs)
	{
		Copy(rhs);
	}
	return *this;
}

Matrix& Matrix::operator*= (const Matrix& rhs)
{
	*this = *this * rhs;
	return *this;
}

bool Matrix::operator== (const Matrix& other) const
{	
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			if (_matrix[i][j] != other.GetMatrixCell(i, j))
			{
				return false;
			}
		}
	}
	return true;
}

const Matrix Matrix::operator*(const Matrix& other) const
{
	Matrix result;
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			float resultValue = 0;
			for (int k = 0; k < ROWS; k++)
			{
				resultValue += _matrix[i][k] * other.GetMatrixCell(k, j);
			}
			result.SetMatrixCell(i, j, resultValue);
		}
	}
	return result;
}

const Vertex Matrix::operator*(const Vertex& other) const
{	
	Vertex newVertex(other);

	newVertex.SetX(_matrix[0][0] * other.GetX() + _matrix[0][1] * other.GetY() + _matrix[0][2] * other.GetZ() + _matrix[0][3] * other.GetW());
	newVertex.SetY(_matrix[1][0] * other.GetX() + _matrix[1][1] * other.GetY() + _matrix[1][2] * other.GetZ() + _matrix[1][3] * other.GetW());
	newVertex.SetZ(_matrix[2][0] * other.GetX() + _matrix[2][1] * other.GetY() + _matrix[2][2] * other.GetZ() + _matrix[2][3] * other.GetW());
	newVertex.SetW(_matrix[3][0] * other.GetX() + _matrix[3][1] * other.GetY() + _matrix[3][2] * other.GetZ() + _matrix[3][3] * other.GetW());
	return newVertex;
}

Matrix Matrix::IdentityMatrix()
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			float currentValue = 0.0f;
			if (j == i)
			{
				currentValue = 1.0f;
			}
			_matrix[i][j] = currentValue;
		}
	}

	return _matrix;
}

void Matrix::Copy(const Matrix& other)
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			_matrix[i][j] = other.GetMatrixCell(i, j);
		}
	}
}