/*
 * SZEROPAD  S-function to increase the input length to the value specified
 *           by appending zeros.  If the vector is longer than the length
 *           parameter, the output is truncated if the truncation flag is set.
 *
 *  Copyright 1995-2000 The MathWorks, Inc.
 *  $Revision: 1.14 $  $Date: 2000/06/14 14:28:02 $
 */

#define S_FUNCTION_NAME  szeropad
#include "dsp_sim.h"

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif

 /*===========================================================================*
 *  Number of S-function Parameters and macros to access from the SimStruct  *
 *===========================================================================*/
#define NUM_ARGS   (1)
#define PAD_ARG    (ssGetArg(S,0))

#define NUM_IWORKS (1)
#define PAD_IDX    (0)


/*====================*
 * S-function methods *
 *====================*/


/* Function: mdlCheckParameters ===============================================
 * Abstract:
 *      Check if the specified parameters are ok. If they are, then check if
 *      this block's sub-class record is set, if it is not, then cache away
 *      the parameter values in this record.
 */
#ifdef MATLAB_MEX_FILE
#define MDL_CHECK_PARAMETERS
static void mdlCheckParameters(SimStruct *S) {
    const char *msg      = NULL;
    const char *perr     = "Zero pad length must be an integer > 0 or -1 (inherit "
					       "input width).";
	const int_T len_pad  = mxGetNumberOfElements(PAD_ARG);
	real_T dp;
	int_T  ip;

	/* Check PAD_ARG: */
    if ( !mxIsDouble(PAD_ARG) || (len_pad < 1) || (len_pad > 2)) {
        msg = perr;
        goto FCN_EXIT;
    }
	dp = mxGetPr(PAD_ARG)[0];
    ip = dp;
	if ( (ip != dp) || ((ip < 1) && (ip != -1)) ) {
		msg = perr;
		goto FCN_EXIT;
	}

FCN_EXIT:
    ssSetErrorStatus(S,msg);

} /* end mdlCheckParameters */
#endif


/* Function: mdlInitializeSizes ===============================================
 * Abstract:
 *      Allocate memory for the sub-class record, and save the pointer in this
 *      s-function's user data. Then call mdlCheckParams() to check and cache
 *      the block parameters. Set all the sizes to be  dynamically sized, this
 *      block can scalar expand.
 */
static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, NUM_ARGS);

#if defined(MATLAB_MEX_FILE)
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) return;
    mdlCheckParameters(S);
    if (ssGetErrorStatus(S) != NULL) return;
#endif

	ssSetNumInputs(        S, DYNAMICALLY_SIZED);
    ssSetNumOutputs(       S, DYNAMICALLY_SIZED);
    ssSetDirectFeedThrough(S, 1);
	ssSetNumSampleTimes(   S, 1);
    ssSetNumIWork(         S, NUM_IWORKS);
    ssSetOptions(          S, SS_OPTION_EXCEPTION_FREE_CODE |
						      SS_OPTION_USING_ssGetUPtrs);
}


static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTimeEvent(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTimeEvent(S, 0, 0.0);
}


#if defined(MATLAB_MEX_FILE)

#define MDL_GET_INPUT_PORT_WIDTH
static int_T mdlGetInputPortWidth(SimStruct *S, int_T outputPortWidth)
{
	/*  If we are backpropagating, verify that the output width is
	 *  correct given the pad length argument.
	 *
	 *  Note that if pad length is -1 (pass-thru), Simulink must be able
	 *  to determine the block input width using forward-propagation.
	 *  If this fcn gets called, it indicates that forward-propagation
	 *  failed, in which case we immediately know that pass-thru cannot
	 *  be performed and this is an error.
	 */
    int_T pad = (int_T)(mxGetPr(PAD_ARG)[0]);
	if (pad == -1) {
		ssSetErrorStatus(S, "Failed to determine block input width "
							"when pass-thru mode (-1) selected.");
	}
    return(outputPortWidth);
}

#define MDL_GET_OUTPUT_PORT_WIDTH
static int_T mdlGetOutputPortWidth(SimStruct *S, int_T inputPortWidth)
{
	/* Simulink is using forward-propagation for block width info: */
	const int_T len_pad = mxGetNumberOfElements(PAD_ARG);
    int_T       pad     = (int_T)(mxGetPr(PAD_ARG)[0]);
	int_T       trunc   = (len_pad > 1)
						  ? (mxGetPr(PAD_ARG)[1] != 0.0)
						  : 1;  /* True by default */
	/*
	 * Pad==-1 indicates pass-thru: no padding or truncation.
	 *    -> Set pad length equal to input length.
	 *
	 * If truncation disallowed, minimum pad is set to the input width.
	 */
	if ((pad == -1) || (!trunc && (pad < inputPortWidth))) {
		pad = inputPortWidth;
	}
    return(pad);
}

#endif


static void mdlInitializeConditions(real_T *x0, SimStruct *S)
{
	const int_T len_pad = mxGetNumberOfElements(PAD_ARG);
	const int_T N       = ssGetNumInputs(S);
	int_T       pad     = (int_T)mxGetPr(PAD_ARG)[0];
	int_T       trunc   = (len_pad > 1)
						  ? (mxGetPr(PAD_ARG)[1] != 0.0)
						  : 1;  /* True by default */
	/*
	 * Pad==-1 indicates pass-thru: no padding or truncation.
	 *    -> Set pad length equal to input length.
	 *
	 * If truncation disallowed, minimum pad is set to the input width.
	 */
	if ((pad == -1) || (!trunc && (pad < N))) {
		pad = N;
	}

    ssSetIWorkValue(S, PAD_IDX, pad);
}


static void mdlOutputs(real_T *y, const real_T *x, const real_T *u, 
                       SimStruct *S, int_T tid)
{
    int_T     pad  = ssGetIWorkValue(S, PAD_IDX);
    int_T     N    = ssGetNumInputs(S);
    int_T     Nmin = MIN(N, pad);    /* pad could be < width */

	UPtrsType uPtrs = ssGetUPtrs(S);

    while(Nmin-- > 0) {
        *y++ = **(uPtrs++);  /* Copy input data   */
    }
    pad -= N;               /* Any zeros to add? */
    while(pad-- > 0) {
        *y++ = (real_T)0.0; /* Pad with zeros    */
    }
}


static void mdlUpdate(real_T *x, const real_T *u, SimStruct *S, int_T tid)
{
}


static void mdlDerivatives(real_T *dx, const real_T *x, const real_T *u, 
                           SimStruct *S, int_T tid)
{
}


static void mdlTerminate(SimStruct *S)
{
}


#include "dsp_trailer.c"
 
/* [EOF]: szeropad.c */
