#include <stdio.h>
#include <stdlib.h>

void swapBytes(void *outData, const void *inData, size_t nBytes, int nItems){
	int i,j;

	const unsigned char *inPtr;
	unsigned char *outPtr;
	inPtr=(unsigned char *)inData;
	outPtr=(unsigned char *)outData;
	//printf("%i %i\n",nItems,nBytes);

	for(i=0;i<nItems;i++){
		for(j=0;j<nBytes;j++){
			outPtr[j] = inPtr[nBytes -j -1];
			//printf("%i %i %i\n",i,j,(int)(((void *)&outPtr[j])-outData));
		}
		inPtr += nBytes; outPtr += nBytes;		
	}
}

size_t fwriteRev(const void *ptr, size_t size, size_t nmemb, FILE *stream){
	void *tmp;
	int ret;

	tmp = malloc(size*nmemb);
	if(!tmp)return -1;
		
	swapBytes(tmp,ptr,size,nmemb);	
	ret = fwrite(tmp,size,nmemb,stream);
	free(tmp);

	return ret;
}

size_t freadRev(void *ptr, size_t size, size_t nmemb, FILE *stream){
	void *tmp;
	int ret;

	tmp = malloc(size*nmemb);
	if(!tmp)return -1;

	ret = fread(tmp,size,nmemb,stream);

	swapBytes(ptr,tmp,size,nmemb);	
	free(tmp);

	return ret;
}


int bmfWrite(char *fileName, int rows, int cols, double *rowHead,double *colHead, double *data, int isColMajor){
	FILE *f;
	int i,j;
	double zero = 0;

	f=fopen(fileName,"wb");
	if(!f)return 2;


	rows += (rowHead != NULL)?1:0;
	cols += (colHead != NULL)?1:0;	
	fwriteRev(&rows,4,1,f);
	fwriteRev(&cols,4,1,f);
	rows -= (rowHead != NULL)?1:0;
	cols -= (colHead != NULL)?1:0;


	//write the column headers (and a 0 that sits in the corner)
	if(colHead != NULL){
		fwriteRev(&zero,sizeof(double),1,f);
		fwriteRev(colHead,sizeof(double),cols,f);
	}


	if(isColMajor){
		for(i=0;i<rows;i++){
			if(rowHead != NULL)
				fwriteRev(&rowHead[i],sizeof(double),1,f);
			for(j=0;j<cols;j++)
				fwriteRev(data + (j*rows+i), sizeof(double), 1 ,f);
			
		}
	}else{
		for(i=0;i<rows;i++){
			if(rowHead != NULL)
				fwriteRev(&rowHead[i],sizeof(double),1,f);
			fwriteRev(data + (i*cols), sizeof(double), cols ,f);
		}
	}

	fclose(f);

	return 0;
}

int bmfLoad(char *fileName, int *rowsPtr, int *colsPtr, double **dataPtr, int dataAsColMajor){
	FILE *f;
	int i,j;
	int rows,cols;
	double *data;

	*rowsPtr = 0;
	*colsPtr = 0;
	*dataPtr = NULL;
	
	f=fopen(fileName,"rb");
	if(!f)return 2;

	freadRev(&rows,4,1,f);
	freadRev(&cols,4,1,f);
	
	data=(double *)malloc(rows*cols*sizeof(double));
	if(!data){ return 1; }

	if(dataAsColMajor){
		for(i=0;i<rows;i++){
			for(j=0;j<cols;j++)
				freadRev(data + (j*rows) + i, sizeof(double), 1, f);
		}
	}else{
		for(i=0;i<rows;i++)
			freadRev(data + (i*cols),sizeof(double),cols,f);
	}

	fclose(f);

	*rowsPtr = rows;
	*colsPtr = cols;
	*dataPtr = data;
	
	return 0;
}

int bmfLoadWithHeaders(char *fileName, int *rowsPtr, int *colsPtr, double **rowHeadPtr, double **colHeadPtr, double **dataPtr){
	FILE *f;
	int i;
	int rows,cols;
	double *rowHead;
	double *colHead;
	double *data;
	double zero;

	*rowsPtr = 0;
	*colsPtr = 0;
	*rowHeadPtr = NULL;
	*colHeadPtr = NULL;
	*dataPtr = NULL;
	
	f=fopen(fileName,"rb");
	if(!f)return 2;

	freadRev(&rows,4,1,f);
	freadRev(&cols,4,1,f);
	cols--;
	if(cols > 1)rows--;
	
	rowHead=(double *)malloc(rows*sizeof(double));
	if(!rowHead)return 1;
	colHead=(double *)malloc(cols*sizeof(double));
	if(!colHead){ free(rowHead); return 1; }
	data=(double *)malloc(rows*cols*sizeof(double));
	if(!data){ free(colHead); free(rowHead); return 1; }

	if(cols <= 1)
		colHead[0] = 0;
	else{
		freadRev(&zero,sizeof(double),1,f);
		freadRev(colHead,sizeof(double),cols,f);
	}

	for(i=0;i<rows;i++){
		freadRev(&rowHead[i],sizeof(double),1,f);
		freadRev(data + (i*cols),sizeof(double),cols,f);
	}

	fclose(f);

	*rowsPtr = rows;
	*colsPtr = cols;
	*rowHeadPtr = rowHead;
	*colHeadPtr = colHead;
	*dataPtr = data;
	
	return 0;
}
