/*
 * @Author: iR
 * @Date: 2022-03-11 16:44:46
 * @LastEditors: iR
 * @LastEditTime: 2022-03-15 17:02:42
 * @FilePath: \Code\inc\matrix.hpp
 * @Description:
 *
 * Copyright (c) 2022 by iR, All Rights Reserved.
 */
#ifndef _MATRIX_HPP_
#define _MATRIX_HPP_

#include <iostream>
#include <stdio.h>

#include "../inc/vector.hpp"

template <class T>
class MatBase
{ // 定义基类，其中包含了矩阵元素读取的纯虚函数
public:
    virtual T &at(int y, int x) = 0;             // 定义纯虚函数
    virtual const T &at(int y, int x) const = 0; // 定义纯虚函数
};

template <class T>
class Matrix : public MatBase<T>
{
private:
    int _height;
    int _width;
    Vector<T> *_mat;

protected:
public:
    ~Matrix()
    {
        delete _mat;
    }
    /**
     * @description: 极度缺省初始化, 大小为2*2, 初值为0
     * @param {int} w
     * @param {int} h
     */
    Matrix();
    /**
     * @description: 缺省初始化，初值为0
     * @param {int} w
     * @param {int} h
     */
    Matrix(int w, int h);

    /**
     * @description: 以相同值初始化矩阵元素
     * @param {int} w
     * @param {int} h
     * @param {T} init_value
     */
    Matrix(int w, int h, T init_value);

    /**
     * @description: 以顺序访问数组的值初始化矩阵元素
     * 必须给出正确的数组,因为在函数内无法判定数组是否正确
     * @param {int} w
     * @param {int} h
     * @param {const T} elements[]
     */
    Matrix(int w, int h, const T elements[]);

    // /**
    //  * @description: 以顺序访问指针的值初始化矩阵元素
    //  * @note: 必须给出正确的数组,因为在函数内无法判定数组是否正确
    //  * @param {int} w
    //  * @param {int} h
    //  * @param {T*} data
    //  */
    // Matrix(int w, int h, T *data);

    /**
     * @description: 以另一个矩阵初始化当前矩阵的元素，实际是元素拷贝
     * @param {Matrix} &another
     */
    Matrix(const Matrix &another);

    /**
     * @description: 只读访问内容
     * @param {int} w
     * @param {int} h
     * @return {*}
     */
    const T get(int w, int h) const;

    /**
     * @description: []运算符重载
     */
    const T operator[](int i) const { return _mat->get(i); }

    /**
     * @description: =运算符重载
     */
    void operator=(const Matrix &b);

    /**
     * @description: +=运算符重载
     * @note: 即 a += b，其中*this即为a
     * @param {*}
     * @return None
     */
    void operator+=(const Matrix &b);
    void operator+=(T &a)
    {
        for (int i = 0; i < _width * _height;i++)
        {
            _mat[i] = a;
        }
    }

    /**
     * @description: +=运算符重载
     * @note:即 c = a + b，其中*this即为a，返回值即为c
     * @param {*}
     * @return None
     */
    Matrix operator+(const Matrix &b) const;

    /**
     * @description: 可读可写访问
     * @param {int} w
     * @param {int} h
     * @return {*}
     */
    T &at(int w, int h)
    {
        T &ref = _mat->get(h * _width + w);
        return ref;
    }

    /**
     * @description: 只读引用访问
     * @param {int} w
     * @param {int} h
     * @return {*}
     */
    const T &at(int w, int h) const
    {
        T &ref = _mat->get(h * _width + w);
        return ref;
    }

    const int getWidth() const { return _width; }
    const int getHeight() const { return _height; }
};

template <class T>
Matrix<T>::Matrix()
{
    _width = 2;
    _height = 2;
    _mat = new Vector<T>(2 * 2, 0);
}

template <class T>
Matrix<T>::Matrix(int w, int h)
{
    _width = w;
    _height = h;
    _mat = new Vector<T>(w * h, 0);
}

template <class T>
Matrix<T>::Matrix(int w, int h, T init_value)
{
    _width = w;
    _height = h;
    _mat = new Vector<T>(w * h, init_value);
}

template <class T>
Matrix<T>::Matrix(int w, int h, const T elements[])
{
    int size = w * h;
    try
    {
        //实际上似乎并不能在这里判断数组是否合法
        // if (w * h > size)
        //     throw "Error: The number of array elements is less than the matrix elements.\n";
        // if (w * h < size)
        //     throw "Error: The number of array elements is greater than the matrix elements.\n";

        _width = w;
        _height = h;
        _mat = new Vector<T>(w * h);
        for (int i = 0; i < size; i++)
            _mat->put(i, elements[i]);
    }
    catch (const char *msg)
    {
        std::cout << msg << "SIZE:" << size << std::endl;
        ;
    }
}

//这个方法似乎有问题
// template <class T>
// Matrix<T>::Matrix(int w, int h, T *data)
// {
//     int size = w * h;
//     try
//     {
//         //实际上似乎并不能在这里判断数组是否合法
//         // if (w * h > size)
//         //     throw "Error: The number of array elements is less than the matrix elements.\n";
//         // if (w * h < size)
//         //     throw "Error: The number of array elements is greater than the matrix elements.\n";
//         if (data == NULL)
//             throw 1;
//         _width = w;
//         _height = h;
//         _mat = new Vector<T>;
//         for (int i = 0; i < size; i++)
//             _mat->put(i, *(data+i));
//     }

//     catch (const int a)
//     {
//         if (a == 1)
//         {
//             std::cout << "Error: Empty array. Set elements to 0." << std::endl;
//             for (int i = 0; i < size; i++)
//                 _mat->put(i, 0);
//         }
//     }
// }

template <class T>
Matrix<T>::Matrix(const Matrix &another)
{
    try
    {
        _width = another.getWidth();
        _height = another.getHeight();
        _mat = new Vector<T>;
        for (int i = 0; i < _width * _height; i++)
            _mat->put(i, another[i]);
    }
    catch (const char *msg)
    {
        std::cout << msg;
    }
}

template <class T>
void Matrix<T>::operator=(const Matrix &another)
{
    try
    {
        _width = another.getWidth();
        _height = another.getHeight();
        _mat = new Vector<T>;
        for (int i = 0; i < _width * _height; i++)
            _mat->put(i, another[i]);

    }
    catch (const char *msg)
    {
        std::cout << msg;
    }
}

template <class T>
const T Matrix<T>::get(int w, int h) const
{
    return _mat->get(h * _width + w);
}

template <class T>
void Matrix<T>::operator+=(const Matrix &b)
{
    try
    {
        if (this->_height == b.getHeight() && this->_width == b.getWidth())
        {
            for (int i = 0; i < _width * _height; i++)
                this->_mat->put(i, (*this)[i] + b[i]);

            return;
        }
        throw "Error: Different size.";
    }
    catch (const char *msg)
    {
        std::cout << msg;
    }
}

template <class T>
Matrix<T> Matrix<T>::operator+(const Matrix &b) const
{
    int h = this->_height;
    int w = this->_width;
    try
    {
        if (h == b.getHeight() && w == b.getWidth())
        {
            Matrix<T> temp(w, h, T(0));
            temp += *this;
            temp += b;
            return temp;
        }

        throw "Error: Different size.";
    }
    catch (const char *msg)
    {
        std::cout << msg;
        Matrix<T> temp(w, h, T(0));
        return temp;
    }
}

#endif