#ifndef AURORA_FUNCTION2D_H
#define AURORA_FUNCTION2D_H

#include <cstdio>
#include <initializer_list>

#include "Matrix.h"
#include "Function1D.h"

namespace Aurora
{
    enum FunctionDirection
    {
        Column,
        Row,
        All
    };
    double immse(const Matrix &aImageA, const Matrix &aImageB);
    Matrix inv(const Matrix &aMatrix);
    Matrix inv(Matrix &&aMatrix);
    Matrix interp2(const Matrix &aX, const Matrix &aY, const Matrix &aV, const Matrix &aX1, const Matrix &aY1, InterpnMethod aMethod);
    Matrix interpn(const Matrix &aX, const Matrix &aY, const Matrix &aV, const Matrix &aX1, const Matrix &aY1, InterpnMethod aMethod);
    Matrix std(const Matrix &aMatrix);

    /**
     * 求矩阵最小值，可按行、列、单元, 目前不支持三维，不支持复数
     * @param aMatrix  目标矩阵
     * @param direction 方向，Column, Row, All
     * @return
     */
    Matrix min(const Matrix &aMatrix, FunctionDirection direction = Column);

    Matrix min(const Matrix &aMatrix, FunctionDirection direction, long &rowIdx, long &colIdx);

    /**
     * 求矩阵最小值，可按行、列、单元, 目前不支持三维，不支持复数
     * @param aMatrix  目标矩阵
     * @param direction 方向，Column, Row, All
     * @return 最大值矩阵
     */
    Matrix max(const Matrix &aMatrix, FunctionDirection direction = Column);

    Matrix max(const Matrix &aMatrix, FunctionDirection direction, long &rowIdx, long &colIdx);

    /**
     * 比较两个矩阵，求对应位置的最小值，不支持三维
     * @attention 矩阵形状不一样时，如A为[MxN],则B应为标量或[1xN]的行向量
     * @param aMatrix 目标矩阵1
     * @param aOther 目标矩阵2
     * @return 最小值矩阵
     */
    Matrix min(const Matrix &aMatrix, const Matrix &aOther);

        /**
     * 比较两个矩阵，求对应位置的最大值，不支持三维
     * @attention 矩阵形状不一样时，如A为[MxN],则B应为标量或[1xN]的行向量
     * @param aMatrix 目标矩阵1
     * @param aOther 目标矩阵2
     * @return 最大值矩阵
     */
    Matrix max(const Matrix &aMatrix, const Matrix &aOther);

    /**
     * 求矩阵和，可按行、列、单元, 目前不支持三维
     * @param aMatrix  目标矩阵
     * @param direction 方向，Column, Row, All
     * @return 求和结果矩阵
     */
    Matrix sum(const Matrix &aMatrix, FunctionDirection direction = Column);

    /**
     * 求矩阵平均值，可按行、列、单元, 目前不支持三维，不支持复数
     * @param aMatrix 目标矩阵
     * @param direction 方向，Column, Row, All
     * @param aIncludeNan 是否包含nan
     * @return 平均值矩阵
     */
    Matrix mean(const Matrix &aMatrix, FunctionDirection direction = Column, bool aIncludeNan = true);

    /**
     * 矩阵排序 按列, 目前不支持三维，不支持复数
     * @param aMatrix 目标矩阵
     * @return 排序后矩阵
     */
    Matrix sort(const Matrix &aMatrix);

    /**
     * 矩阵排序 按列, 目前不支持三维，不支持复数
     * @param aMatrix 目标矩阵
     * @return 排序后矩阵
     */
    Matrix sort(Matrix &&aMatrix);

    /**
     * 矩阵排序 按行, 目前不支持三维，不支持复数
     * @param aMatrix 目标矩阵
     * @return 排序后矩阵
     */
    Matrix sortrows(const Matrix &aMatrix);

    /**
     * 矩阵排序 按行, 目前不支持三维，不支持复数
     * @param aMatrix 目标矩阵
     * @return 排序后矩阵
     */
    Matrix sortrows(Matrix &&aMatrix);

    /**
     * 对矩阵求中间值 按列, 目前不支持三维，不支持复数
     * @param aMatrix 目标矩阵
     * @return 中值矩阵
     */
    Matrix median(const Matrix &aMatrix);

    /**
     * FFT,支持到2维，输入可以是常数可以是复数，输出必是复数
     * @param aMatrix 目标矩阵
     * @param aFFTSize 目标矩阵需要处理的长度，默认为-1即全部
     * @return fft后的复数矩阵
     */
    Matrix fft(const Matrix &aMatrix, long aFFTSize = -1);

    /**
    * fftshift,在原有数据上进行修改，将fft的数据的前半部分和后半部分交换，支持2D数据
    * @param aMatrix 
    */
    void fftshift(Matrix &aMatrix);

    /**
     * 逆fft，支持到2维，输入必须是复数，输出必是复数
     * @attention 如有需要可使用real去除虚部
     * @param aMatrix
     * @return ifft后的复数矩阵
     */
    Matrix ifft(const Matrix &aMatrix);

    /**
     * Symmetric逆fft，支持到2维，输入必须是复数，输出必是实数
     * @param aMatrix
     * @return ifft后的实数矩阵
     */
    Matrix ifft_symmetric(const Matrix &aMatrix,long length);

    /**
     * hilbert，支持到2维，输入必须是复数，输出必是复数
     * @param aMatrix
     * @return
     */
    Matrix hilbert(const Matrix &aMatrix);

    /**
     * prod，支持到2维
     * @param aMatrix
     * @return
     */
    Matrix prod(const Matrix &aMatrix);

    Matrix dot(const Matrix &aMatrix, const Matrix &aOther, FunctionDirection direction = Column);

    /**
     * 转换下标为索引值
     * @attention 索引值按照其实为1与matlab对应，在C++中使用需要-1
     * @param aVMatrixSize
     * @param aSliceIdxs
     * @return
     */
    Matrix sub2ind(const Matrix &aVMatrixSize, std::initializer_list<Matrix> aSliceIdxs);
    
};

#endif // AURORA_FUNCTION2D_H
