#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "matrix.h"

// TODO Matrix-Funktionen implementieren

Matrix createMatrix(unsigned int rows, unsigned int cols)
{
    Matrix newMatrix;
	if (rows == 0 || cols == 0) {
		newMatrix.rows = 0;
		newMatrix.cols = 0;
		newMatrix.buffer = NULL;
		return newMatrix;
	}

	newMatrix.rows = rows;
	newMatrix.cols = cols;

	newMatrix.buffer = calloc(rows*cols, sizeof(MatrixType));
	return newMatrix;
}

void clearMatrix(Matrix *matrix)
{
    free(matrix->buffer);
	matrix->buffer = NULL;
	matrix->rows = 0;
	matrix->cols = 0;
}

void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{
    if (rowIdx >= matrix.rows || colIdx >= matrix.cols){
        printf("Index out of bounds\n"); //Error Message because Index Input exceeds Matrix
	}
    else{
        matrix.buffer[rowIdx * matrix.cols + colIdx] = value; //Writes Value of value variable in the selected place in Matrix
	}
}

MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{
    if (rowIdx >= matrix.rows || colIdx >= matrix.cols){
       printf("Index out of bounds\n");
       return 0;
    }
    else{

       MatrixType value = matrix.buffer[rowIdx * matrix.cols + colIdx]; //Stores value of selected place of Matrix in value variable
        return value;
	}
}

Matrix add(const Matrix matrix1, const Matrix matrix2)
{

    //If rows and cols of both Matrixes are the same the normal addition starts
    if (matrix1.rows == matrix2.rows && matrix1.cols == matrix2.cols){
        Matrix MatrixErgebnis = createMatrix(matrix1.rows, matrix1.cols);
        for (unsigned int i = 0; i < matrix1.rows; i++){
            for (unsigned int j = 0; j < matrix1.cols; j++){
                //Adding Matrix Elements of same row and col index together and store in new Matrix
                MatrixErgebnis.buffer[i * matrix1.cols + j] = matrix1.buffer[i * matrix1.cols + j] + matrix2.buffer[i * matrix1.cols + j];

            }
        }
        return MatrixErgebnis;
    }

    //if matrix1 is a Vektor it adds elements to first col of matrix2
    else if (matrix1.cols == 1 && matrix1.rows == matrix2.rows){
        Matrix MatrixErgebnis = createMatrix(matrix2.rows, matrix2.cols);
        for (unsigned int i = 0; i < matrix2.rows; i++) {
          for (unsigned int j = 0; j < matrix2.cols; j++) {
            //Copy Matrix2 to ErgebnisMatrix
            MatrixErgebnis.buffer[i * matrix2.cols + j] = getMatrixAt(matrix2, i, j);
            //Adding Elements of first col of Matrix 1 (Vector) to all Elements of MatrixErgebnis
            MatrixErgebnis.buffer[i * matrix2.cols + j] += matrix1.buffer[i];
           }
        }
    return MatrixErgebnis;
    }
    //if matrix2 is a Vektor it adds elements to first col of matrix1
    else if (matrix2.cols == 1 && matrix1.rows == matrix2.rows){
        Matrix MatrixErgebnis = createMatrix(matrix1.rows, matrix1.cols);
        for (unsigned int i = 0; i < matrix1.rows; i++) {
            for (unsigned int j = 0; j < matrix1.cols; j++) {
                //Copy Matrix1 to ErgebnisMatrix
                MatrixErgebnis.buffer[i * matrix1.cols + j] = getMatrixAt(matrix1, i, j);
                //Adding Elements of first col of Matrix 2 (Vector) to first col of MatrixErgebnis
                MatrixErgebnis.buffer[i * matrix1.cols + j] += matrix2.buffer[i];

            }
        }
    return MatrixErgebnis;
    }
    else{
           printf("Matrix dimensions do not match\n"); //Error Message if dimensions of Input Matrixes are not identical
           Matrix MatrixErgebnis = createMatrix(0, 0);
           clearMatrix(&MatrixErgebnis);
           return MatrixErgebnis;
    }

}

Matrix multiply(const Matrix matrix1, const Matrix matrix2)
{
    Matrix result;
    result.rows = 0;
    result.cols = 0;
    result.buffer = NULL;

    if (matrix1.cols != matrix2.rows) {
      return result;
	}

    result.rows = matrix1.rows;
    result.cols = matrix2.cols;

        result.buffer = malloc(result.rows * result.cols * sizeof(MatrixType));

        for(int i = 0; i < result.rows; i++) {
            for(int j = 0; j < result.cols; j++) {
                MatrixType value = 0;
                for(int k = 0; k < matrix1.cols; k++) {
                    value += matrix1.buffer[i * matrix1.cols + k] * matrix2.buffer[k * matrix2.cols + j];
                }
                result.buffer[i * result.cols + j] = value;
            }
    }
    return result;
}