#define S_FUNCTION_NAME     MCU
#define S_FUNCTION_LEVEL    2

#include "mcu_wrapper_conf.h"
double SIM_Sample_Time;

#define MDL_UPDATE
/* Function: mdlUpdate ====================================================
 * Abstract:
 *    This function is called once for every major integration time step.
 *    Discrete states are typically updated here, but this function is useful
 *    for performing any tasks that should only take place once per
 *    integration step.
 */
static void mdlUpdate(SimStruct *S, int_T tid)
{ 
    // get S_Func "periphery"
    const real_T *IN = ssGetInputPortRealSignal(S,0);
    real_T *DISC = ssGetDiscStates(S);
	time_T TIME = ssGetT(S);

	//---------------SIMULATE MCU---------------
	MCU_Step_Simulation(S, IN, DISC, TIME); // SIMULATE MCU
	//------------------------------------------
}//end mdlUpdate

/* Function: mdlOutputs ===================================================
 * Abstract:
 *    In this function, you compute the outputs of your S-function
 *    block. Generally outputs are placed in the output vector(s),
 *    ssGetOutputPortSignal.
 */
static void mdlOutputs(SimStruct *S, int_T tid)
{
    real_T *PORTA  = ssGetOutputPortRealSignal(S,0);
    real_T *PORTE  = ssGetOutputPortRealSignal(S,1);
	real_T *DISC = ssGetDiscStates(S);

	//-------------WRITTING GPIOS---------------
    for (int i=0; i < PORT_WIDTH; i++)
    {
	    PORTA[i] = DISC[i];
        DISC[i] = 0;

	    PORTE[i] = DISC[PORT_WIDTH + i];
        DISC[PORT_WIDTH + i] = 0;
    }
	//------------------------------------------
}//end mdlOutputs

#define MDL_CHECK_PARAMETERS   /* Change to #undef to remove function */
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
static void mdlCheckParameters(SimStruct *S)
{
	int i;

	// Проверяем и принимаем параметры и разрешаем или запрещаем их менять
	// в процессе моделирования
	for (i=0; i<1; i++)
	{
		// Input parameter must be scalar or vector of type double
		if (!mxIsDouble(ssGetSFcnParam(S,i)) || mxIsComplex(ssGetSFcnParam(S,i)) ||
			mxIsEmpty(ssGetSFcnParam(S,i)))
		{
			ssSetErrorStatus(S,"Input parameter must be of type double");
			return;
		}
		// Параметр м.б. только скаляром, вектором или матрицей
		if (mxGetNumberOfDimensions(ssGetSFcnParam(S,i)) > 2)
		{
			ssSetErrorStatus(S,"Параметр м.б. только скаляром, вектором или матрицей");
			return;
		}
//         sim_dt = mxGetPr(ssGetSFcnParam(S,0))[0];
		// Parameter not tunable
//		ssSetSFcnParamTunable(S, i, SS_PRM_NOT_TUNABLE);
		// Parameter tunable (we must create a corresponding run-time parameter)
		ssSetSFcnParamTunable(S, i, SS_PRM_TUNABLE);
		// Parameter tunable only during simulation
//		ssSetSFcnParamTunable(S, i, SS_PRM_SIM_ONLY_TUNABLE);

	}//for (i=0; i<NPARAMS; i++)

}//end mdlCheckParameters
#endif //MDL_CHECK_PARAMETERS
static void mdlInitializeSizes(SimStruct *S)
{    
    ssSetNumSFcnParams(S, 1);
    // Кол-во ожидаемых и фактических параметров должно совпадать
    if(ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S))
    {
	    // Проверяем и принимаем параметры
	    mdlCheckParameters(S);
    }
    else
    {
	    return;// Parameter mismatch will be reported by Simulink
    }

	ssSetNumContStates(S, 0);				// number of continuous states
	ssSetNumDiscStates(S, PORT_NUMB*PORT_WIDTH);	// number of discrete states

    if (!ssSetNumInputPorts(S, 1)) return; // set up input port
    ssSetInputPortWidth(S, 0, PORT_WIDTH);
    ssSetInputPortDirectFeedThrough(S, 0, 0);
    ssSetInputPortRequiredContiguous(S, 0, 1); // direct input signal access

    if (!ssSetNumOutputPorts(S, PORT_NUMB)) return; // set up output port
	for (int i = 0; i < PORT_NUMB; i++)
		ssSetOutputPortWidth(S, i, PORT_WIDTH);


    ssSetNumSampleTimes(S, 1);


	ssSetNumRWork(         S, 5);	// number of real work vector elements
	ssSetNumIWork(         S, 5);	// number of integer work vector elements
	ssSetNumPWork(         S, 0);	// number of pointer work vector elements
	ssSetNumModes(         S, 0);	// number of mode work vector elements
	ssSetNumNonsampledZCs( S, 0);	// number of nonsampled zero crossings


	ssSetRuntimeThreadSafetyCompliance(S, RUNTIME_THREAD_SAFETY_COMPLIANCE_TRUE);
    /* Take care when specifying exception free code - see sfuntmpl.doc */
    ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);


}

#define MDL_START  // Change to #undef to remove function
#if defined(MDL_START)
/* Function: mdlStart =====================================================
 * Abstract:
 *    This function is called once at start of model execution. If you
 *    have states that should be initialized once, this is the place
 *    to do it.
 */
static void mdlStart(SimStruct *S)
{
    SIM_Initialize_Simulation();
}
#endif // MDL_START



static void mdlInitializeSampleTimes(SimStruct *S)
{	

	// Шаг дискретизации
	SIM_Sample_Time = mxGetPr(ssGetSFcnParam(S,NPARAMS-1))[0];

	// Register one pair for each sample time
	ssSetSampleTime(S, 0, SIM_Sample_Time);
	ssSetOffsetTime(S, 0, 0.0);
}
extern HANDLE hThread;
static void mdlTerminate(SimStruct *S)
{
	//TerminateThread(hThread, NULL);
	//CloseHandle(hThread);
	flag_to_end = 1;
	ResumeThread(hThread);
	WaitForSingleObject(hThread, 1000);
	SIM_deInitialize_Simulation();
	mexUnlock();
}
#ifdef MATLAB_MEX_FILE    /* Is this file being compiled as a 
                             MEX-file? */
#include "simulink.c"     /* MEX-file interface mechanism */
#else
#include "cg_sfun.h"      /* Code generation registration 
                             function */
#endif
