#include "inputAnalyzer.h"

static void analyze_cell(const mxArray *cell_array_ptr)
{
	mwSize total_num_of_cells;
	mwIndex index;
	const mxArray *cell_element_ptr;

	total_num_of_cells = mxGetNumberOfElements(cell_array_ptr);
	mexPrintf("total num of cells = %d\n", total_num_of_cells);
	mexPrintf("\n");

	for (index = 0; index<total_num_of_cells; index++) {
		mexPrintf("\n\n\t\tCell Element: ");
		display_subscript(cell_array_ptr, index);
		mexPrintf("\n");
		cell_element_ptr = mxGetCell(cell_array_ptr, index);
		if (cell_element_ptr == NULL) {
			mexPrintf("\tEmpty Cell\n");
		}
		else {
			mexPrintf("------------------------------------------------\n");
			get_characteristics(cell_element_ptr);
			analyze_class(cell_element_ptr);
			mexPrintf("\n");
		}
	}
	mexPrintf("\n");
}

static void analyze_structure(const mxArray *structure_array_ptr)
{
	mwSize total_num_of_elements;
	mwIndex index;
	int number_of_fields, field_index;
	const char  *field_name;
	const mxArray *field_array_ptr;


	mexPrintf("\n");
	total_num_of_elements = mxGetNumberOfElements(structure_array_ptr);
	number_of_fields = mxGetNumberOfFields(structure_array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {

		for (field_index = 0; field_index<number_of_fields; field_index++) {
			mexPrintf("\n\t\t");
			display_subscript(structure_array_ptr, index);
			field_name = mxGetFieldNameByNumber(structure_array_ptr,
				field_index);
			mexPrintf(".%s\n", field_name);
			field_array_ptr = mxGetFieldByNumber(structure_array_ptr,
				index,
				field_index);
			if (field_array_ptr == NULL) {
				mexPrintf("\tEmpty Field\n");
			}
			else {
				mexPrintf("------------------------------------------------\n");
				get_characteristics(field_array_ptr);
				analyze_class(field_array_ptr);
				mexPrintf("\n");
			}
		}
		mexPrintf("\n\n");
	}


}

static void analyze_string(const mxArray *string_array_ptr)
{
	char *buf;
	mwSize number_of_dimensions, buflen;
	const mwSize *dims;
	mwSize d, page, total_number_of_pages, elements_per_page;

	buflen = mxGetNumberOfElements(string_array_ptr) + 1;
	buf = (char*)mxCalloc(buflen, sizeof(char));

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

	dims = mxGetDimensions(string_array_ptr);
	number_of_dimensions = mxGetNumberOfDimensions(string_array_ptr);

	elements_per_page = dims[0] * dims[1];
	total_number_of_pages = 1;
	for (d = 2; d<number_of_dimensions; d++) {
		total_number_of_pages *= dims[d];
	}

	for (page = 0; page < total_number_of_pages; page++) {
		mwSize row;
		for (row = 0; row<dims[0]; row++) {
			mwSize column;
			mwSize index = (page * elements_per_page) + row;
			mexPrintf("\t");
			display_subscript(string_array_ptr, index);
			mexPrintf(" ");

			for (column = 0; column<dims[1]; column++) {
				mexPrintf("%c", buf[index]);
				index += dims[0];
			}
			mexPrintf("\n");

		}

	}
}

static void analyze_sparse(const mxArray *array_ptr)
{
	double  *pr, *pi;
	mwIndex  *ir, *jc;
	mwSize      col, total = 0;
	mwIndex   starting_row_index, stopping_row_index, current_row_index;
	mwSize      n;

	pr = mxGetPr(array_ptr);
	pi = mxGetPi(array_ptr);
	ir = mxGetIr(array_ptr);
	jc = mxGetJc(array_ptr);

	n = mxGetN(array_ptr);
	for (col = 0; col<n; col++) {
		starting_row_index = jc[col];
		stopping_row_index = jc[col + 1];
		if (starting_row_index == stopping_row_index)
			continue;
		else {
			for (current_row_index = starting_row_index;
				current_row_index < stopping_row_index;
				current_row_index++) {
				if (mxIsComplex(array_ptr)) {
					mexPrintf("\t(%u,%u) = %g+%g i\n",
						ir[current_row_index] + 1,
						col + 1, pr[total], pi[total]);
					total++;
				}
				else {
					mexPrintf("\t(%u,%u) = %g\n",
						ir[current_row_index] + 1,
						col + 1, pr[total++]);
				}
			}
		}
	}
}

static void analyze_int8(const mxArray *array_ptr)
{
	signed char   *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (signed char *)mxGetData(array_ptr);
	pi = (signed char *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %d + %di\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %d\n", *pr++);
		}
	}
}

static void analyze_uint8(const mxArray *array_ptr)
{
	unsigned char *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (unsigned char *)mxGetData(array_ptr);
	pi = (unsigned char *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %u + %ui\n", *pr, *pi++);
		}
		else {
			mexPrintf(" = %u\n", *pr++);
		}
	}
}

static void analyze_int16(const mxArray *array_ptr)
{
	short int *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (short int *)mxGetData(array_ptr);
	pi = (short int *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %d + %di\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %d\n", *pr++);
		}
	}
}

static void analyze_uint16(const mxArray *array_ptr)
{
	unsigned short int *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (unsigned short int *)mxGetData(array_ptr);
	pi = (unsigned short int *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %u + %ui\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %u\n", *pr++);
		}
	}
}

static void analyze_int32(const mxArray *array_ptr)
{
	int *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (int *)mxGetData(array_ptr);
	pi = (int *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %d + %di\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %d\n", *pr++);
		}
	}
}

static void analyze_uint32(const mxArray *array_ptr)
{
	unsigned int *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (unsigned int *)mxGetData(array_ptr);
	pi = (unsigned int *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %u + %ui\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %u\n", *pr++);
		}
	}
}

static void analyze_int64(const mxArray *array_ptr)
{
	int64_T *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (int64_T *)mxGetData(array_ptr);
	pi = (int64_T *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %" FMT64 "d + %" FMT64 "di\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %" FMT64 "d\n", *pr++);
		}
	}
}

static void analyze_uint64(const mxArray *array_ptr)
{
	uint64_T *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (uint64_T *)mxGetData(array_ptr);
	pi = (uint64_T *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %" FMT64 "u + %" FMT64 "ui\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %" FMT64 "u\n", *pr++);
		}
	}
}

static void analyze_single(const mxArray *array_ptr)
{
	float *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (float *)mxGetData(array_ptr);
	pi = (float *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %g + %gi\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %g\n", *pr++);
		}
	}
}

static void analyze_double(const mxArray *array_ptr)
{
	double *pr, *pi;
	mwSize total_num_of_elements, index;

	pr = (double *)mxGetData(array_ptr);
	pi = (double *)mxGetImagData(array_ptr);
	total_num_of_elements = mxGetNumberOfElements(array_ptr);
	mexPrintf("totalnum = %i\n", total_num_of_elements);
	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		if (mxIsComplex(array_ptr)) {
			mexPrintf(" = %g + %gi\n", *pr++, *pi++);
		}
		else {
			mexPrintf(" = %g\n", *pr++);
		}
	}
}

static void analyze_logical(const mxArray *array_ptr)
{
	mxLogical *pr, *pi;
	mwSize total_num_of_elements, index;
	total_num_of_elements = mxGetNumberOfElements(array_ptr);
	pr = (mxLogical *)mxGetData(array_ptr);
	pi = (mxLogical *)mxGetImagData(array_ptr);

	for (index = 0; index<total_num_of_elements; index++) {
		mexPrintf("\t");
		display_subscript(array_ptr, index);
		mexPrintf("= %s\n", *pr++ ? "true" : "false");
	}
}

static void analyze_full(const mxArray *numeric_array_ptr)
{
	mxClassID   category;

	category = mxGetClassID(numeric_array_ptr);
	switch (category) {
	case mxINT8_CLASS:   analyze_int8(numeric_array_ptr);   break;
	case mxUINT8_CLASS:  analyze_uint8(numeric_array_ptr);  break;
	case mxINT16_CLASS:  analyze_int16(numeric_array_ptr);  break;
	case mxUINT16_CLASS: analyze_uint16(numeric_array_ptr); break;
	case mxINT32_CLASS:  analyze_int32(numeric_array_ptr);  break;
	case mxUINT32_CLASS: analyze_uint32(numeric_array_ptr); break;
	case mxINT64_CLASS:  analyze_int64(numeric_array_ptr);  break;
	case mxUINT64_CLASS: analyze_uint64(numeric_array_ptr); break;
	case mxSINGLE_CLASS: analyze_single(numeric_array_ptr); break;
	case mxDOUBLE_CLASS: analyze_double(numeric_array_ptr); break;
	default: break;
	}
}

void display_subscript(const mxArray *array_ptr, mwSize index)
{
	mwSize inner, subindex, total, d, q, number_of_dimensions;
	mwSize *subscript;
	const mwSize *dims;

	number_of_dimensions = mxGetNumberOfDimensions(array_ptr);
	subscript = (mwSize *) mxCalloc(number_of_dimensions, sizeof(mwSize));
	dims = mxGetDimensions(array_ptr);

	mexPrintf("(");
	subindex = index;
	for (d = number_of_dimensions - 1; ; d--) { 

		for (total = 1, inner = 0; inner<d; inner++)
			total *= dims[inner];

		subscript[d] = subindex / total;
		subindex = subindex % total;
		if (d == 0) {
			break;
		}
	}

	for (q = 0; q<number_of_dimensions - 1; q++) {
		mexPrintf("%d,", subscript[q] + 1);
	}
	mexPrintf("%d)", subscript[number_of_dimensions - 1] + 1);

	mxFree(subscript);
}

void get_characteristics(const mxArray *array_ptr)
{
	const char    *class_name;
	const mwSize  *dims;
	char          *shape_string;
	char          *temp_string;
	mwSize        c;
	mwSize        number_of_dimensions;
	size_t        length_of_shape_string;

	number_of_dimensions = mxGetNumberOfDimensions(array_ptr);
	dims = mxGetDimensions(array_ptr);

	shape_string = (char *)mxCalloc(number_of_dimensions * 3, sizeof(char));
	shape_string[0] = '\0';
	temp_string = (char *)mxCalloc(64, sizeof(char));

	for (c = 0; c<number_of_dimensions; c++) {
		sprintf(temp_string, "%dx", dims[c]);
		strcat(shape_string, temp_string);
	}

	length_of_shape_string = strlen(shape_string);
	shape_string[length_of_shape_string - 1] = '\0';
	if (length_of_shape_string > 16) {
		sprintf(shape_string, "%u-D", number_of_dimensions);
	}
	mexPrintf("Dimensions: %s\n", shape_string);

	class_name = mxGetClassName(array_ptr);
	mexPrintf("Class Name: %s%s\n", class_name,
		mxIsSparse(array_ptr) ? " (sparse)" : "");

	mexPrintf("------------------------------------------------\n");

	mxFree(shape_string);
}

mxClassID analyze_class(const mxArray *array_ptr)
{
	mxClassID  category;

	category = mxGetClassID(array_ptr);

	if (mxIsSparse(array_ptr)) {
		analyze_sparse(array_ptr);
	}
	else {
		switch (category) {
		case mxLOGICAL_CLASS: analyze_logical(array_ptr);    break;
		case mxCHAR_CLASS:    analyze_string(array_ptr);     break;
		case mxSTRUCT_CLASS:  analyze_structure(array_ptr);  break;
		case mxCELL_CLASS:    analyze_cell(array_ptr);       break;
		case mxUNKNOWN_CLASS:
			mexWarnMsgIdAndTxt("MATLAB:explore:unknownClass",
				"Unknown class.");                       break;
		default:              analyze_full(array_ptr);       break;
		}
	}

	return(category);
}

void mexAnalyze(int nrhs, const mxArray *prhs[])
{
	int i;

#if defined(_LP64) || defined (_WIN64)
#ifdef MX_COMPAT_32
	for (i = 0; i<nrhs; i++) {
		if (mxIsSparse(prhs[i])) {
			mexErrMsgIdAndTxt("MATLAB:explore:NoSparseCompat",
				"MEX-files compiled on a 64-bit platform that use sparse array functions need to be compiled using -largeArrayDims.");
		}
	}
#endif
#endif


	for (i = 0; i<nrhs; i++) {
		mexPrintf("\n\n");
		mexPrintf("------------------------------------------------\n");
		mexPrintf("Name: %s%d%c\n", "prhs[", i, ']');

		get_characteristics(prhs[i]);
		analyze_class(prhs[i]);
	}
}