#ifndef matrice_hpp
#define matrice_hpp

#include <vector>
#include <map>
#include <cstring>
#include <limits>
#include <algorithm>
#include <cstdint>
#include "myException.h"

template <class TIPO>
class Matrice {
protected:
    TIPO *__dati;
    std::uint_fast64_t __size;
    std::uint_fast64_t __righe;
    std::uint_fast64_t __colonne;
public:
    Matrice(std::uint_fast64_t righe, std::uint_fast64_t colonne, TIPO init_val=TIPO()) {
        __righe = righe;
        __colonne = colonne;
        __size = __righe * __colonne;
        __dati = new TIPO[__size];
        std::fill(__dati, __dati + __size, init_val);
    }
    Matrice(std::uint_fast64_t size, TIPO init_val=TIPO()) {
        __righe = size;
        __colonne = size;
        __size = __righe * __colonne;
        __dati = new TIPO[__size];
        std::fill(__dati, __dati + __size, init_val);
    }
    
    virtual ~Matrice() {
        delete[] __dati;
    }
    
    virtual TIPO& operator()(std::uint_fast64_t row, std::uint_fast64_t col) {
        if (row >= __righe || col >= __colonne) {
            std::string err_str = "Matrice::operator= wrong position";
            throw_line(err_str);
        }
        return __dati[row * __colonne + col];
    }
    
    virtual TIPO at(std::uint_fast64_t row, std::uint_fast64_t col) const {
        if (row >= __righe || col >= __colonne) {
            std::string err_str = "Matrice::operator= wrong position";
            throw_line(err_str);
        }
        return __dati[row * __colonne + col];
    }
    
    virtual std::uint_fast64_t Rows() const {
        return __righe;
    }
    
    virtual std::uint_fast64_t Cols() const {
        return __colonne;
    }
    
    virtual bool operator==(Matrice<TIPO>& m) const {
        auto result = std::memcmp(__dati, m.__dati, sizeof(TIPO) * __size);
        bool risultato = (result == 0);
        return risultato;
    }
    
    virtual Matrice<TIPO>& operator=(Matrice<TIPO>& m) {
        if (__righe != m.__righe || __colonne != m.__colonne) {
            std::string err_str = "Matrice::operator= wrong size";
            throw_line(err_str);
        }
        std::memcpy(__dati, m.__dati, sizeof(TIPO) * __size);
        return *this;
    }
    
    virtual bool operator!=(Matrice<TIPO>& m) const {
        return !(*this == m);
    }
    
    virtual std::string to_string(char DELIMETER = ';') const {
        std::string r = "";
        for (size_t k = 0; k <  this->Rows(); ++k)
        {
            bool first = true;
            for (size_t h = 0; h < this->Cols(); ++h)
            {
                if (first)
                {
                    r += std::to_string(this->at(k, h));
                    first = false;
                }
                else
                {
                    r += DELIMETER + std::to_string(this->at(k, h));
                }
            }
            r += "\n";
        }
        
        return r;
    }
    
    virtual std::string to_string(std::vector<std::string> header, char DELIMETER = ';') const {
        std::string r = "";
        for (size_t h = 0; h < this->Cols(); ++h) {
            r += DELIMETER + header.at(h);
        }
        r += "\n";
        
        
        for (size_t k = 0; k <  this->Rows(); ++k) {
            r += header.at(k);
            for (size_t h = 0; h < this->Cols(); ++h) {
                r += DELIMETER + std::to_string(this->at(k, h));
            }
            r += "\n";
        }
        
        return r;
    }
    
};

//************************************
//************************************
//************************************

#endif /* matrice_hpp */
