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

// TODO Matrix-Funktionen implementieren
/* Erstellt eine Matrix mit den gegebenen Zeilen und Spalten */
Matrix createMatrix(unsigned int rows, unsigned int cols)
{

}

/* Löscht eine Matrix */
void clearMatrix(Matrix *matrix)
{
    
}

/* Setzt einen Wert in die Matrix an der gegebenen Stelle */
void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{
    
}
/* Ausgeben eines Wertes aus der Matrix
    * matrix Die Matrix
    * rowIdx Der Zeilenindex
    * colIdx Der Spaltenindex
    * Der Wert an der Stelle (rowIdx, colIdx) wird zurückgegeben
    * Wenn der Index außerhalb der Matrix liegt ist es nicht möglich den Wert auszugeben
    */
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{
    if(matrix.buffer == NULL || matrix.rows <= rowIdx || matrix.cols <= colIdx)
    {
      return UNDEFINED_MATRIX_VALUE;
    }
    return matrix.buffer[rowIdx * matrix.cols + colIdx];
    /*Da im Speicher eindimensional anneinandergereit [000111222333] (.rows = 4, .cols = 3); 
    Reihenindex (fängt bei null an) * Anzahl der Spalten der Matrix (2(3.Reihe)*3 = Stelle 6)[222333] -> überspringen bis zur gewünschten Zeile;
    dann plus Spaltenindex = gewünschter Wert im Speicher*/
}

/* Addition von zwei Matrizen
    * matrix1 Die erste Matrix
    * matrix2 Die zweite Matrix
    * Die Addition der beiden Matrizen wird zurückgegeben
    * Wenn die Matrizen nicht die gleiche Gräße haben 
      oder eine Matrix kein Spaltenvektor ist (Broadcasting), wird NULL zurückgegeben
    */
Matrix add(const Matrix matrix1, const Matrix matrix2)
{
  if (matrix1.rows == matrix2.rows && matrix1.cols == matrix2.cols)
  {
    Matrix addiert = createMatrix(matrix1.rows, matrix1.cols);
    for(int i = 0; i < matrix1.rows; i++)
    {
      for(int j = 0; j < matrix1.cols; j++)
      {
        MatrixType wert = getMatrixAt(matrix1, i, j) + getMatrixAt(matrix2, i, j);
        setMatrixAt(wert, addiert, i, j);
      }
    }
    return addiert;
  }
  else if(matrix1.rows == matrix2.rows && matrix1.cols == 1 && matrix2.cols > 0)
  {
    Matrix addiert = createMatrix(matrix2.rows, matrix2.cols);
    for(int i = 0; i < matrix2.rows; i++)
    {
      for(int j = 0; j < matrix2.cols; j++)
      {
        MatrixType wert = getMatrixAt(matrix1, i, 0) + getMatrixAt(matrix2, i, j);
        setMatrixAt(wert, addiert, i, j);
      }
    }
    return addiert;
  }
  else if(matrix1.rows == matrix2.rows && matrix2.cols == 1 && matrix1.cols > 0)
  {
    Matrix addiert = createMatrix(matrix1.rows, matrix1.cols);
    for(int i = 0; i < matrix1.rows; i++)
    {
      for(int j = 0; j < matrix1.cols; j++)
      {
        MatrixType wert = getMatrixAt(matrix1, i, j) + getMatrixAt(matrix2, i, 0);
        setMatrixAt(wert, addiert, i, j);
      }
    }
    return addiert;
  }
  else
  {
    Matrix addiert = {0, 0, UNDEFINED_MATRIX_VALUE};
    return addiert;
  }
  

}
/* Multiplikation von zwei Matrizen
    * matrix1 Die erste Matrix
    * matrix2 Die zweite Matrix
    * Die Multiplikation der beiden Matrizen wird zurückgegeben
    * l x m * m x n = l x n -> Spalte der ersten Matrix muss gleich der Zeile der zweiten Matrix sein
      -> wenn das nicht der Fall ist, wird NULL zurückgegeben
    */
Matrix multiply(const Matrix matrix1, const Matrix matrix2)
{
    if(matrix1.cols == matrix2.rows)
    {
      Matrix mult = createMatrix(matrix1.rows, matrix2.cols);
      for(int i = 0; i < matrix1.rows; i++)
      {
        for(int j = 0; j < matrix2.cols; j++)
        {
          MatrixType wert = 0;
          for(int k = 0; k < matrix2.rows; k++)
          {
            wert = getMatrixAt(matrix1, i, k) * getMatrixAt(matrix2, k, j);
            MatrixType res = wert;
            wert = res + wert;
          }
          setMatrixAt(wert, mult, i, j);
        }
      }
      return mult;
    }
    else
    {
      Matrix mult = {0, 0, UNDEFINED_MATRIX_VALUE};
      return mult;
    }
}

