#include "mexCellItem.h"

void mexCellItem::setOffsets()
{
	const int dimNum = mxGetNumberOfDimensions(_ptr);
	const size_t * dims = mxGetDimensions(_ptr);
	offsets = mexGetOffsetArray(dimNum, dims);
}

void mexCellItem::setFieldNums()
{
	if (mxGetClassID(_ptr) != mxSTRUCT_CLASS) return;
	cellFieldNums = new list<field_address>();

	int number_of_fields = mxGetNumberOfFields(_ptr);
	int total_num_of_elements = mxGetNumberOfElements(_ptr);
	for (int index = 0; index < total_num_of_elements; index++)
	{
		for (int field_index = 0; field_index < number_of_fields; field_index++)
		{
			const char* name = mxGetFieldNameByNumber(_ptr, field_index);
			field_address address;
			address.field_index = field_index;
			address.field_name = name;
			address.struct_index = index;
			cellFieldNums->push_front(address);
		}
	}
}

int mexCellItem::getFieldNum(const char * name, int index)
{
	for (auto it = mexCellItem::cellFieldNums->begin(); it != mexCellItem::cellFieldNums->end(); ++it)
	{
		if (it->struct_index == index && (!strcmp(it->field_name, name)))
			return it->field_index;
	}
	return -1;
}

mexCellItem::mexCellItem(const mxArray * cell_ptr)
{
	_ptr = cell_ptr;
	setOffsets();
	setFieldNums();
}

double mexCellItem::getDouble()
{
	return mexCellItem::getDouble(0, 0);
}
double mexCellItem::getDouble(int i)
{
	return mexCellItem::getDouble(i, 0);
}
double mexCellItem::getDouble(int i, int j)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j);
	return ((double*)mxGetData(_ptr))[index];
}
double mexCellItem::getDouble(int i, int j, int k)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k);
	return ((double*)mxGetData(_ptr))[index];
}
double mexCellItem::getDouble(int i, int j, int k, int l)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l);
	return ((double*)mxGetData(_ptr))[index];
}
double mexCellItem::getDouble(int i, int j, int k, int l, int m)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l, m);
	return ((double*)mxGetData(_ptr))[index];
}

int mexCellItem::getInt()
{
	return getInt(0, 0);
}
int mexCellItem::getInt(int i)
{
	return getInt(i, 0);
}
int mexCellItem::getInt(int i, int j)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j);
	return (int)((double*)mxGetData(_ptr))[index];
}
int mexCellItem::getInt(int i, int j, int k)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k);
	return(int)((double*)mxGetData(_ptr))[index];
}
int mexCellItem::getInt(int i, int j, int k, int l)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l);
	return (int)((double*)mxGetData(_ptr))[index];
}
int mexCellItem::getInt(int i, int j, int k, int l, int m)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l, m);
	return (int)((double*)mxGetData(_ptr))[index];
}

mxLogical mexCellItem::getBool()
{
	return getBool(0, 0);
}
mxLogical mexCellItem::getBool(int i)
{
	return getBool(i, 0);
}
mxLogical mexCellItem::getBool(int i, int j)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j);
	return ((mxLogical*)mxGetData(_ptr))[index];
}
mxLogical mexCellItem::getBool(int i, int j, int k)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k);
	return ((mxLogical*)mxGetData(_ptr))[index];
}
mxLogical mexCellItem::getBool(int i, int j, int k, int l)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l);
	return ((mxLogical*)mxGetData(_ptr))[index];
}
mxLogical mexCellItem::getBool(int i, int j, int k, int l, int m)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l, m);
	return ((mxLogical*)mxGetData(_ptr))[index];
}


mexCellItem * mexCellItem::getCell()
{
	return getCell(0, 0);
}
mexCellItem * mexCellItem::getCell(int i)
{
	return getCell(i, 0);
}
mexCellItem * mexCellItem::getCell(int i, int j)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j);
	mxArray * arr  = mxGetCell(_ptr, index);
	return new mexCellItem(arr);
}
mexCellItem * mexCellItem::getCell(int i, int j, int k)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k);
	mxArray * arr = mxGetCell(_ptr, index);
	return new mexCellItem(arr);
}
mexCellItem * mexCellItem::getCell(int i, int j, int k, int l)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l);
	mxArray * arr = mxGetCell(_ptr, index);
	return new mexCellItem(arr);
}
mexCellItem * mexCellItem::getCell(int i, int j, int k, int l, int m)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l, m);
	mxArray * arr = mxGetCell(_ptr, index);
	return new mexCellItem(arr);
}


mexCellItem * mexCellItem::getStructField(const char * fieldName)
{
	return getStructField(fieldName, 0, 0);
}
mexCellItem * mexCellItem::getStructField(const char* fieldName, int i)
{
	return getStructField(fieldName, i, 0);
}
mexCellItem * mexCellItem::getStructField(const char* fieldName, int i, int j)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j);
	int fieldNum = getFieldNum(fieldName, index);
	if (fieldNum == -1)
		mexErrMsgTxt("Requested field not found.");

	mxArray* arr = mxGetFieldByNumber(_ptr, index, fieldNum);
	return new mexCellItem(arr);
}
mexCellItem * mexCellItem::getStructField(const char* fieldName, int i, int j, int k)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k);
	int fieldNum = getFieldNum(fieldName, index);
	if (fieldNum == -1)
		mexErrMsgTxt("Requested field not found.");

	mxArray* arr = mxGetFieldByNumber(_ptr, index, fieldNum);
	return new mexCellItem(arr);
}
mexCellItem * mexCellItem::getStructField(const char* fieldName, int i, int j, int k, int l)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l);
	int fieldNum = getFieldNum(fieldName, index);
	if (fieldNum == -1)
		mexErrMsgTxt("Requested field not found.");

	mxArray* arr = mxGetFieldByNumber(_ptr, index, fieldNum);
	return new mexCellItem(arr);
}
mexCellItem * mexCellItem::getStructField(const char* fieldName, int i, int j, int k, int l, int m)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l, m);
	int fieldNum = getFieldNum(fieldName, index);
	if (fieldNum == -1)
		mexErrMsgTxt("Requested field not found.");

	mxArray* arr = mxGetFieldByNumber(_ptr, index, fieldNum);
	return new mexCellItem(arr);
}

bool mexCellItem::checkStructFieldExists(const char * fieldName)
{
	return checkStructFieldExists(fieldName, 0, 0);
}
bool mexCellItem::checkStructFieldExists(const char * fieldName, int i)
{
	return checkStructFieldExists(fieldName, i, 0);
}
bool mexCellItem::checkStructFieldExists(const char * fieldName, int i, int j)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j);
	int fieldNum = getFieldNum(fieldName, index);
	return (fieldNum != -1);
}
bool mexCellItem::checkStructFieldExists(const char * fieldName, int i, int j, int k)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k);
	int fieldNum = getFieldNum(fieldName, index);
	return (fieldNum != -1);
}
bool mexCellItem::checkStructFieldExists(const char * fieldName, int i, int j, int k, int l)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l);
	int fieldNum = getFieldNum(fieldName, index);
	return (fieldNum != -1);
}
bool mexCellItem::checkStructFieldExists(const char * fieldName, int i, int j, int k, int l, int m)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l, m);
	int fieldNum = getFieldNum(fieldName, index);
	return (fieldNum != -1);
}

bool mexCellItem::checkIndexWithinRange(int i)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i);
	int totalNumofElems = mxGetNumberOfElements(_ptr);
	return totalNumofElems > (index+1);
}
bool mexCellItem::checkIndexWithinRange(int i, int j)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j);
	int totalNumofElems = mxGetNumberOfElements(_ptr);
	return totalNumofElems > (index + 1);
}
bool mexCellItem::checkIndexWithinRange(int i, int j, int k)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k);
	int totalNumofElems = mxGetNumberOfElements(_ptr);
	return totalNumofElems > (index + 1);
}
bool mexCellItem::checkIndexWithinRange(int i, int j, int k, int l)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l);
	int totalNumofElems = mxGetNumberOfElements(_ptr);
	return totalNumofElems > (index + 1);
}
bool mexCellItem::checkIndexWithinRange(int i, int j, int k, int l, int m)
{
	int dimNum = mxGetNumberOfDimensions(_ptr);
	int index = getFlatIndex(dimNum, offsets, i, j, k, l, m);
	int totalNumofElems = mxGetNumberOfElements(_ptr);
	return totalNumofElems > (index + 1);
}

char * mexCellItem::getString()
{
	const int buflen = mxGetNumberOfElements(_ptr) + 1;
	char * buf = (char*)mxCalloc(buflen, sizeof(char));

	if (mxGetString(_ptr, buf, buflen) != 0)
		mexErrMsgIdAndTxt("MATLAB:explore:invalidStringArray",
			"Could not convert string data.");

	return buf;
}

void mexCellItem::analyze()
{
	analyze_class(_ptr);
}

const char * mexCellItem::getCellClassName()
{
	return mxGetClassName(_ptr);
}

const size_t mexCellItem::getElemCount()
{
	return mxGetNumberOfElements(_ptr);
}

void mexCellItem::printOffset()
{
	int dim = mxGetNumberOfDimensions(_ptr);
	mexPrintIntArray(dim,offsets);
}

mexCellItem::~mexCellItem()
{
	delete(cellFieldNums);
	delete(offsets);
}
