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

// TODO Matrix-Funktionen implementieren

#define EMPTY_CHAR 0


Matrix createMatrix(unsigned int rows, unsigned int cols)        
{
  Matrix matrix;
  matrix.rows = rows;
  matrix.cols = cols;
  matrix.buffer = EMPTY_CHAR;

  if(rows == 0 || cols == 0)
  {
    Matrix emptyMatix = {
      .rows = 0,
      .cols = 0,
      .buffer = NULL};
    return emptyMatix;
  
  }

  if(rows > 0 && cols > 0) 
  {
    matrix.buffer = (MatrixType*) calloc(rows * cols, sizeof(MatrixType));
  }
  return matrix;
}




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




void setMatrixAt(MatrixType value, Matrix *matrix, unsigned int rowIdx, unsigned int colIdx)
{
  if(matrix && matrix->buffer && rowIdx < matrix->rows && colIdx < matrix->cols)
  {
    matrix->buffer[rowIdx * matrix->cols + colIdx] = value;
  }
    
}



MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{
  if(matrix.buffer && rowIdx < matrix.rows && colIdx < matrix.cols)
  {
    return matrix.buffer[rowIdx * matrix.cols + colIdx];
  }
  return UNDEFINED_MATRIX_VALUE;
}




Matrix add(const Matrix matrix1, const Matrix matrix2)
{
  unsigned int resRows = 0;
  unsigned int resCols = 0;

  //Ergebniszeilenbestimmung
  if(matrix1.rows == matrix2.rows)
  {
    resRows = matrix1.rows;
  }else if(matrix1.rows == 1)
  {
    resRows = matrix2.rows;
  }else if(matrix2.rows == 1)
  {
    resRows = matrix1.rows;
  }else
  {
    return createMatrix(0, 0);
  }

  //Ergebnisspaltenbestimmung
  if(matrix1.cols == matrix2.cols)
  {
    resCols = matrix1.cols;
  }else if(matrix1.cols == 1)
  {
    resCols = matrix2.cols;
  }else if(matrix2.cols == 1)
  {
    resCols = matrix1.cols;
  }else
  {
    return createMatrix(0,0);
  }

  //Ergebnismatrix
  Matrix result = createMatrix(resRows, resCols);
  if(result.buffer == NULL && (resRows > 0 && resCols > 0))
  {
    return createMatrix(0, 0);
  }


  for(unsigned int i = 0; i < resRows; ++i)
  {
    for(unsigned int j = 0; j < resCols; ++j)
    {
      unsigned int i1 = (matrix1.rows == 1) ? 0 : i;
      unsigned int j1 = (matrix1.cols == 1) ? 0 : j;

      unsigned int i2 = (matrix2.rows == 1) ? 0 : i;
      unsigned int j2 = (matrix2.cols == 1) ? 0 : j;


      MatrixType val = getMatrixAt(matrix1, i1, j1) + getMatrixAt(matrix2, i2, j2);
      setMatrixAt(val, &result, i, j);
    }
  }
  return result;
}




Matrix multiply(const Matrix matrix1, const Matrix matrix2)
{
  if(matrix1.cols != matrix2.rows)
  {
    return createMatrix(0, 0);
  }
  Matrix result = createMatrix(matrix1.rows, matrix2.cols);
  for(unsigned int i = 0; i < result.rows; ++i)
  {
    for(unsigned int j = 0; j < result.cols; ++j)
    {
      MatrixType sum = 0;
      for(unsigned int k = 0; k < matrix1.cols; ++k)
      {
        sum += getMatrixAt(matrix1, i, k) * getMatrixAt(matrix2, k, j);
      }
      setMatrixAt(sum, &result, i, j);
    }
  }
  return result;
    
}
