#include "MatlabReader.h"
#include "Function.h"


double* calculateArray(matvar_t* aMatVar, const char* aFieldName)
{
    matvar_t* matVar = Mat_VarGetStructFieldByName(aMatVar,aFieldName,0);
    double* data = reinterpret_cast<double*>(matVar->data);
    unsigned long long dataLength = matVar->nbytes / static_cast<unsigned long long>(matVar->data_size);
    double* result = new double[dataLength];
    std::copy(data,data+dataLength,result);
    Mat_VarFree(matVar);
    return result;
}

MatlabReader::MatlabReader(const std::string& aFilePath)
{
    mMatFile = Mat_Open(aFilePath.c_str(), MAT_ACC_RDONLY);
}

MatlabReader::~MatlabReader()
{
    Mat_Close(mMatFile);
}

std::shared_ptr<short> MatlabReader::readint16(const std::string& aFieldName, size_t& aSize)
{
    if(mMatFile == nullptr)
    {
        return std::shared_ptr<short>();
    }
    matvar_t* matvar = Mat_VarRead(mMatFile, aFieldName.c_str());
    aSize =  matvar->nbytes / static_cast<unsigned long long>(matvar->data_size);
    short* resultDara = new short[aSize];
    std::copy((short*)matvar->data,(short*)matvar->data+aSize,resultDara);
    std::shared_ptr<short> result();
    return std::shared_ptr<short>(resultDara,std::default_delete<short[]>());
}

Aurora::Matrix MatlabReader::read(const std::string& aFieldName)
{
    if(mMatFile == nullptr)
    {
        return Aurora::Matrix();
    }

    matvar_t* matvar = Mat_VarRead(mMatFile, aFieldName.c_str());
    if (matvar == nullptr)
    {
        return Aurora::Matrix();
    }
    bool isComplex = matvar->isComplex;
    int rank = matvar->rank;
    int rows = 1, columns = 1, slices = 1;
    if (rank >= 3)
    {
        slices = matvar->dims[2];
    }
    if (rank >= 2)
    {
        columns = matvar->dims[1];
    }
    if (rank >= 1)
    {
        rows = matvar->dims[0];
    }
    Aurora::Matrix result;
    if(!isComplex)
    {
        size_t matrixSize =  matvar->nbytes / static_cast<unsigned long long>(matvar->data_size);
        double* matrixData = new double[matrixSize];
        switch(matvar->data_type )
        {
            case MAT_T_INT16:
            {
                std::copy((short*)matvar->data, (short*)matvar->data + matrixSize, matrixData);
                break;
            }
            case MAT_T_UINT16:
            {
                std::copy((unsigned short*)matvar->data, (unsigned short*)matvar->data + matrixSize, matrixData);
                break;
            }
            case MAT_T_INT32:
            {
                std::copy((int*)matvar->data, (int*)matvar->data + matrixSize, matrixData);
                break;
            }
            case MAT_T_DOUBLE:
            {
                std::copy((double*)matvar->data, (double*)matvar->data + matrixSize, matrixData);
                break;
            }
        }
        result = Aurora::Matrix::fromRawData(matrixData,rows,columns,slices);
    }
    else
    {
        mat_complex_split_t* complexDouble = reinterpret_cast<mat_complex_split_t*>(matvar->data);
        unsigned long long dataLength =  matvar->nbytes / static_cast<unsigned long long>(matvar->data_size);
        double* data = Aurora::malloc(dataLength, true);
        double* re = reinterpret_cast<double*>(complexDouble->Re);
        double* im = reinterpret_cast<double*>(complexDouble->Im);
        for ( unsigned long long i = 0; i < dataLength; i++)
        {
            data[2*i] = re[i];
            data[2*i+1] = im[i];
        }
        result = Aurora::Matrix::fromRawData(data,rows,columns,slices,Aurora::Complex);

    }
    Mat_VarFree(matvar);
    return result;
}

std::vector<Aurora::Matrix> MatlabReader::read4d(const std::string& aFieldName)
{
    if(mMatFile == nullptr)
    {
        return std::vector<Aurora::Matrix>();
    }

    matvar_t* matvar = Mat_VarRead(mMatFile, aFieldName.c_str());
    if (matvar == nullptr)
    {
        return std::vector<Aurora::Matrix>();
    }
    int rank = matvar->rank;
    if (rank != 4)
    {
        return std::vector<Aurora::Matrix>();
    }
    bool isComplex = matvar->isComplex;
    int rows = matvar->dims[0];
    int columns = matvar->dims[1];
    int slices = matvar->dims[2];
    int dims4 = matvar->dims[3];
    std::vector<Aurora::Matrix> result;
    if(!isComplex)
    {
        double* matrix3d = reinterpret_cast<double*>(matvar->data);
        for(int i=0; i<dims4; ++i)
        {
            result.push_back(Aurora::Matrix::copyFromRawData(matrix3d,rows,columns,slices));
            matrix3d+=rows*columns*slices;
        }
    }
    else
    {
        return std::vector<Aurora::Matrix>();
    }
    return result;
}
