// oMatlab.cpp : Defines the entry point for the DLL application.
//

//#include "stdafx.h" //no need to use precompiled header
#define	_OMATLAB_IMPLEMENTATION_
#include "libmatlab.h"
#include "mclmcr.h"
#include "oMatlab.h"

enum {
	MATLAB_UNINIT = 0,
	MATLAB_INITED,
};
int	g_nModuleState;
static	void	_init_module_state()
{
	g_nModuleState = MATLAB_UNINIT;
	mclInitializeApplication(NULL, 0);
}

static	bool	_reset_module_state()
{
	g_nModuleState = MATLAB_UNINIT;
	mclTerminateApplication();
	return true;
}

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	//Originlab begin
	switch(ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		_init_module_state();
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_PROCESS_DETACH:
		_reset_module_state();
		break;
	case DLL_THREAD_DETACH:
		break;
	}
	//Originlab end
    return TRUE;
}

OMATLAB_API	bool	omatlab_init()
{
	if ( g_nModuleState == MATLAB_INITED )
		return true;
	/*	///Sophy 9/27/2010, I move to DLL_PROCESS_ATTACH in DllMain to allow multiple call of omatlab_init and omatlab_free, since mclInitializeApplication succeed only one time.
	///it will fail after call mclTerminateApplication, so should not call mclTerminateApplication in omatlab_free
	if ( !mclInitializeApplication(NULL, 0) )
		return false;
	*/
	if ( !libmatlabInitialize() )
		return false;

	g_nModuleState = MATLAB_INITED;
	return true;
}

OMATLAB_API	bool	omatlab_free()
{
	if ( g_nModuleState == MATLAB_UNINIT )
		return false;

	libmatlabTerminate();
	/*	///Sophy 9/27/2010 move to DLL_PROCESS_DETACH only cleanup when dll is unload.
	mclTerminateApplication();
	*/
	g_nModuleState = MATLAB_UNINIT;

	return true;
}

#define	BUFFER_CHECK_RETURN(_Ptr)	if ( _Ptr == NULL )return false;
#define MODULE_CHECK_RETURN			if ( g_nModuleState == MATLAB_UNINIT )return false;
OMATLAB_API	bool	omatlab_add_matrix(double* mat1, int nRows1, int nCols1, double* mat2, int nRows2, int nCols2, double* matOut)
{
	if ( nRows1 < 1 || nCols1 < 1 )
		return false;
	if ( nRows1 != nRows2 || nCols1 != nCols2 )
		return false;

	MODULE_CHECK_RETURN

	BUFFER_CHECK_RETURN(mat1)
	BUFFER_CHECK_RETURN(mat2)
	BUFFER_CHECK_RETURN(matOut)

	mxArray *mxIn1, *mxIn2;
	mxIn1 = mxCreateDoubleMatrix(nRows1, nCols1, mxREAL);
	mxIn2 = mxCreateDoubleMatrix(nRows2, nCols2, mxREAL);
	memcpy(mxGetPr(mxIn1), mat1, nRows1 * nCols1 * sizeof(double));
	memcpy(mxGetPr(mxIn2), mat2, nRows2 * nCols2 * sizeof(double));
	mxArray *mxOut;
	mxOut = mxCreateDoubleMatrix(nRows1, nCols1, mxREAL);
	mlfAddmatrix(1, &mxOut, mxIn1, mxIn2);
	memcpy(matOut, mxGetPr(mxOut), nRows1 * nCols1 * sizeof(double));

	mxDestroyArray(mxOut);
	mxDestroyArray(mxIn1);
	mxDestroyArray(mxIn2);

	return true;
}

#define GAUSS_PARAMS	4
OMATLAB_API	bool	omatlab_gauss_fit(double* xIn, double* yIn, int nSize, double* vParams, double* xOut, double* yOut, int nOutSize)
{
	if ( nSize < 1 || nOutSize < 1 )
		return false;

	MODULE_CHECK_RETURN

	BUFFER_CHECK_RETURN(xIn)
	BUFFER_CHECK_RETURN(yIn)
	BUFFER_CHECK_RETURN(vParams)
	BUFFER_CHECK_RETURN(xOut)
	BUFFER_CHECK_RETURN(yOut)

	mxArray *mxXIn, *mxYIn, *mxXOut, *mxYOut, *mxParams;
	mxXIn = mxCreateDoubleMatrix(nSize, 1, mxREAL);
	mxYIn = mxCreateDoubleMatrix(nSize, 1, mxREAL);
	mxParams = mxCreateDoubleMatrix(GAUSS_PARAMS, 1, mxREAL);
	memcpy(mxGetPr(mxXIn), xIn, nSize * sizeof(double));
	memcpy(mxGetPr(mxYIn), yIn, nSize * sizeof(double));
	memcpy(mxGetPr(mxParams), vParams, GAUSS_PARAMS * sizeof(double));

	mxXOut = mxCreateDoubleMatrix(nOutSize, 1, mxREAL);
	memcpy(mxGetPr(mxXOut), xOut, nOutSize * sizeof(double));
	mxYOut = mxCreateDoubleMatrix(nOutSize, 1, mxREAL);
	
	mxArray *mxParamsOut;
	mxParamsOut = mxCreateDoubleMatrix(GAUSS_PARAMS, 1, mxREAL);
	mlfFitgau(2, &mxParamsOut, &mxYOut, mxXIn, mxYIn, mxParams, mxXOut);
	memcpy(vParams, mxGetPr(mxParamsOut), GAUSS_PARAMS * sizeof(double));
	memcpy(xOut, mxGetPr(mxXOut), nOutSize * sizeof(double));
	memcpy(yOut, mxGetPr(mxYOut), nOutSize * sizeof(double));
	return true;
}