#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 m;
    m.rows = rows;
    m.cols = cols;

    m.buffer = (MatrixType*)malloc(sizeof(MatrixType) * rows * cols);
    // Prüfe auf ungültige Dimensionen
    if (rows == 0 || cols == 0) {
        m.rows = 0;
        m.cols = 0;
        m.buffer = NULL;
        return m;
    }
    if (m.buffer == NULL){
    fprintf(stderr, "Error: Memory allocation failed in createMatrix!.\n");
    m.rows = 0;
    m.cols = 0;
    return m;
    }
for (unsigned int i = 0; i < rows * cols; i++){
    m.buffer[i] = 0.0f;
    }
return m;
}


void clearMatrix(Matrix *matrix)
{

        if (matrix == NULL) {
            return;
        }

        // Speicher freigeben falls vorhanden
        if (matrix->buffer != NULL) {
            free(matrix->buffer);
            matrix->buffer = NULL;
        }

        // Dimensionen zurücksetzen
        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) {
        fprintf(stderr, "Error: setMatrixAt index out of bounds.\n");
        return;
    }

    matrix.buffer[rowIdx * matrix.cols + colIdx] = value;
}

MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{
    if (rowIdx >= matrix.rows || colIdx >= matrix.cols) {
        fprintf(stderr, "Error: getMatrixAt index out of bounds.\n");
        return UNDEFINED_MATRIX_VALUE;
    }

    return matrix.buffer[rowIdx * matrix.cols + colIdx];
}

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

    // Fall 1: Normale elementweise Addition (gleiche Dimensionen)
    if (matrix1.rows == matrix2.rows && matrix1.cols == matrix2.cols) {
        result = createMatrix(matrix1.rows, matrix1.cols);
        if (result.buffer == NULL) {
            return result;
        }

        for (unsigned int i = 0; i < matrix1.rows; i++) {
            for (unsigned int j = 0; j < matrix1.cols; j++) {
                setMatrixAt(
                    getMatrixAt(matrix1, i, j) + getMatrixAt(matrix2, i, j),
                    result,
                    i, j
                );
            }
        }
        return result;
    }

    // Fall 2: Broadcasting - matrix2 ist Spaltenvektor (cols=1)
    else if (matrix1.rows == matrix2.rows && matrix2.cols == 1) {
        result = createMatrix(matrix1.rows, matrix1.cols);
        if (result.buffer == NULL) {
            return result;
        }

        for (unsigned int i = 0; i < matrix1.rows; i++) {
            for (unsigned int j = 0; j < matrix1.cols; j++) {
                // matrix2 hat nur 1 Spalte (Index 0), wird über alle Spalten verteilt
                setMatrixAt(
                    getMatrixAt(matrix1, i, j) + getMatrixAt(matrix2, i, 0),
                    result,
                    i, j
                );
            }
        }
        return result;
    }

    // Fall 3: Broadcasting - matrix1 ist Spaltenvektor (cols=1)
    else if (matrix2.rows == matrix1.rows && matrix1.cols == 1) {
        result = createMatrix(matrix2.rows, matrix2.cols);
        if (result.buffer == NULL) {
            return result;
        }

        for (unsigned int i = 0; i < matrix2.rows; i++) {
            for (unsigned int j = 0; j < matrix2.cols; j++) {
                // matrix1 hat nur 1 Spalte (Index 0), wird über alle Spalten verteilt
                setMatrixAt(
                    getMatrixAt(matrix1, i, 0) + getMatrixAt(matrix2, i, j),
                    result,
                    i, j
                );
            }
        }
        return result;
    }

    // Fall 4: Ungültige Dimensionen
    else {
        fprintf(stderr, "Error: Matrix dimensions do not match for addition.\n");
        Matrix empty = {0, 0, NULL};
        return empty;
    }
}



Matrix multiply(const Matrix matrix1, const Matrix matrix2)
{
  if (matrix1.cols != matrix2.rows) {
        fprintf(stderr, "Error: Invalid matrix dimensions for multiplication.\n");
        Matrix empty = {0, 0, NULL};
        return empty;
    }

    Matrix result = createMatrix(matrix1.rows, matrix2.cols);
    if (result.buffer == NULL) {
        return result;
    }

    for (unsigned int i = 0; i < matrix1.rows; i++) {
        for (unsigned int j = 0; j < matrix2.cols; j++) {
            MatrixType sum = 0.0f;

            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;
}