/*
 * SRESHAPE C-MEX S-function for reshaping the input signal.
 *
 *  Input Signal: a vector or matrix.  Can be real or complex with 
 *                any Simulink built-in or user defined data type.
 *  Parameter: 1- Output orientation: (1) Unoriendted vector, (2) row vector, 
 *                (3) column vector, or (4) Matrix or vector with specified
 *                dimensions
 *             2- Dimensions: This parameter is ignored when Output 
 *                orientation is (1) Unoriendted vector, (2) row vector, 
 *                (3) column vector.  If the output orientation is "Matrix or
 *                vector", this parameter specifies output port width or 
 *                dimensions.
 *
 *  Output Signal: a vector or matrix based on the parameter. Output has the 
 *                 same data type and signal type (real/complex) as the input.
 *
 *  Authors: M. Shakeri
 *
 *  Copyright 1990-2000 The MathWorks, Inc.
 *
 *  $Revision: 1.11 $  $Date: 2000/09/09 02:18:51 $
 */
#define S_FUNCTION_NAME sreshape
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

enum {UNORIENTED = 1, COL_VECTOR, ROW_VECTOR, MATRIX_OR_VECTOR};
enum {ORIENTATION_IDX,DIMENSIONS_IDX,NUM_PARAMS};
enum {INPORT=0};
enum {OUTPORT=0};

#define ORIENTATION_ARG ssGetSFcnParam(S,ORIENTATION_IDX)
#define MATRIX_ARG      ssGetSFcnParam(S,DIMENSIONS_IDX)


#define EDIT_OK(S, ARG) \
       (!((ssGetSimMode(S) == SS_SIMMODE_SIZES_CALL_ONLY) && mxIsEmpty(ARG)))


#ifdef MATLAB_MEX_FILE 
#define MDL_CHECK_PARAMETERS 
static void mdlCheckParameters(SimStruct *S) 
{
    int_T   orientation  = (int_T)(mxGetPr(ORIENTATION_ARG)[0]);
    
    if(EDIT_OK(S, MATRIX_ARG) && (orientation == MATRIX_OR_VECTOR)){
        int       numElms     = mxGetNumberOfElements(MATRIX_ARG);
        boolean_T reportError = (numElms != 1) && (numElms != 2); 
        
        if(!reportError){
            int_T m = (int_T)(mxGetPr(MATRIX_ARG)[0]);
            int_T n = (numElms == 2) ? (int_T)(mxGetPr(MATRIX_ARG)[1]) : 1;
            reportError = m < 1 || n < 1 || 
                (real_T)m != (mxGetPr(MATRIX_ARG)[0]) ||
                ((numElms == 2) && (real_T)n != (int_T)(mxGetPr(MATRIX_ARG)[1]));
        }
        
        if(reportError){
            ssSetErrorStatus(S,
               "Invalid value specified for 'Output dimensions' parameter. "
               "For a one-dimensional array, the 'Output dimensions' parameter "
               "must be a positive, integer scalar."
               "For a matrix signal, the parameter must be a positive, integer "
               "vector of length 2");
            return;
        }      
    }
}
#endif 

static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, NUM_PARAMS);

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

    /* Input: */
    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortDirectFeedThrough(S, INPORT, 1);
    ssSetInputPortFrameData(        S, INPORT, FRAME_INHERITED);
    ssSetInputPortDataType(         S, INPORT, DYNAMICALLY_TYPED);
    ssSetInputPortComplexSignal(    S, INPORT, COMPLEX_INHERITED);
    ssSetInputPortReusable(         S, INPORT, 1);
    ssSetInputPortOverWritable(     S, INPORT, 1);
    
     /* Output: */
    if (!ssSetNumOutputPorts(S,1)) return;
    ssSetOutputPortDataType(     S, OUTPORT, DYNAMICALLY_TYPED);
    ssSetOutputPortComplexSignal(S, OUTPORT, COMPLEX_INHERITED);
    ssSetOutputPortReusable(     S, OUTPORT, 1);

    /* Input and output dimensions. Output frame Data */
    {                
        int_T     orientation   = (int_T)(mxGetPr(ORIENTATION_ARG)[0]);
        boolean_T outFrmDynamic = true; /* assume */
        
        switch(orientation){
          case MATRIX_OR_VECTOR:
          {   
              int_T      m = 1;
              int_T      n = 1;
              DimsInfo_T input   = {DYNAMICALLY_SIZED, DYNAMICALLY_SIZED, NULL};
              int        numElms = mxGetNumberOfElements(MATRIX_ARG);

              if(EDIT_OK(S, MATRIX_ARG)){
                  m  = (int_T)(mxGetPr(MATRIX_ARG)[0]);
                  n  = (numElms == 2) ? (int_T)(mxGetPr(MATRIX_ARG)[1]) : 1;
              }

              if(numElms == 2) {
                  /* Set the output port dimensions */
                  if(!ssSetOutputPortMatrixDimensions(S, OUTPORT, m, n)) return;
              }else{
                  /* Set the output port width */
                  if(!ssSetOutputPortVectorDimension(S, OUTPORT, m)) return;
                  outFrmDynamic = false; /* Because 1-D vector */
              }
              
              /* Set the input port dimensions */
              input.numDims = DYNAMICALLY_SIZED;
              input.width   = m * n;
              if(!ssSetInputPortDimensionInfo(S, INPORT, &input)) return;
              break;
          }
          case ROW_VECTOR:
          case COL_VECTOR:
          {
              int_T      m = 1;
              int_T      n = 1;

              if(orientation == COL_VECTOR){
                  m = DYNAMICALLY_SIZED;
              }else{
                  n = DYNAMICALLY_SIZED;
              }
              /* Set the input and output port dimensions */
              if(!ssSetInputPortDimensionInfo(S,INPORT,DYNAMIC_DIMENSION)) 
                  return;
              if(!ssSetOutputPortMatrixDimensions(S, OUTPORT, m, n)) return;
              break;
          }
          case UNORIENTED:
          {
              /* Set the input and output port dimensions */
              if(!ssSetInputPortDimensionInfo(S,INPORT,DYNAMIC_DIMENSION)) 
                  return;
              if(!ssSetOutputPortVectorDimension(S,OUTPORT,DYNAMICALLY_SIZED)) 
                  return;
              outFrmDynamic = false;
          }
          break;
        }

        if (outFrmDynamic) {
            ssSetOutputPortFrameData(S, OUTPORT, FRAME_INHERITED);
        } else {
            ssSetOutputPortFrameData(S, OUTPORT, FRAME_NO);
        }
    }
    /*
     * This block can accept partial dimension call.  Simulink may
     * call mdlSetInputPortDimensionInfo or mdlSetOutputPortDimensionInfo
     * with partial information. 
     */
    ssSetNumSampleTimes(S, 1);
    ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE |
                 SS_OPTION_ALLOW_PARTIAL_DIMENSIONS_CALL |
                 SS_OPTION_USE_TLC_WITH_ACCELERATOR);
}


static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);
}



static void mdlOutputs(SimStruct *S, int_T tid)
{

    int_T     width       = ssGetInputPortWidth(S,0);
    DTypeId   dType       = ssGetInputPortDataType(S, 0);
    boolean_T isComplex   = ssGetInputPortComplexSignal(S, 0) == COMPLEX_YES;

    int_T     dTypeSize   = ssGetDataTypeSize(S, dType); 
    int_T     elmSize     = ((isComplex)? 2 : 1) * dTypeSize;

    InputPtrsType u0Ptr   = ssGetInputPortSignalPtrs(S,0);  
    void          *y      = ssGetOutputPortSignal(S,0);
    char_T        *yCPtr  = (char_T *)y;
    int i;

    UNUSED_ARG(tid); /* not used in single tasking mode */

    for (i = 0; i < width; i++) {
        (void)memcpy(yCPtr, u0Ptr[i], elmSize);
        yCPtr += elmSize;
    } 
}

#if defined(MATLAB_MEX_FILE)
#define MDL_SET_INPUT_PORT_DIMENSION_INFO
static void mdlSetInputPortDimensionInfo(SimStruct *S, 
                                      int_T port,
                                      const DimsInfo_T *dimsInfo)
{
   
    if(!ssSetInputPortDimensionInfo(S, port, dimsInfo)) return;
    
    if(ssGetOutputPortWidth(S,port) == DYNAMICALLY_SIZED && 
       dimsInfo->width != DYNAMICALLY_SIZED){

        int_T orientation  = (int_T)(mxGetPr(ORIENTATION_ARG)[0]);
        int_T width        = dimsInfo->width;

        if(orientation == COL_VECTOR){
            if(!ssSetOutputPortMatrixDimensions(S, port, width, 1)) return;
        }else if(orientation == ROW_VECTOR){
            if(!ssSetOutputPortMatrixDimensions(S, port, 1, width)) return;
        }else{ /* it must be UNORIENTED */
            if(!ssSetOutputPortVectorDimension(S, port, width)) return;
        }

    }
}

# define MDL_SET_OUTPUT_PORT_DIMENSION_INFO
static void mdlSetOutputPortDimensionInfo(SimStruct        *S, 
                                          int_T            port,
                                          const DimsInfo_T *dimsInfo)
{
    if(!ssSetOutputPortDimensionInfo (S, port, dimsInfo)) return;
    
    if(ssGetInputPortWidth(S,port) == DYNAMICALLY_SIZED &&
       dimsInfo->width != DYNAMICALLY_SIZED){

        DECL_AND_INIT_DIMSINFO(input);

        input.width   = dimsInfo->width;
        input.dims    = ssGetInputPortDimensions(S, 0);
        input.numDims = ssGetInputPortNumDimensions(S, 0);

        if(!ssSetInputPortDimensionInfo(S, port, &input)) return;
    } 
}

#define MDL_SET_INPUT_PORT_FRAME_DATA
static void mdlSetInputPortFrameData(SimStruct *S, 
                                     int_T     port,
                                     Frame_T   frameData)
{
    ssSetInputPortFrameData(S, port, frameData);

    /* If output is dynamic, apply same frame data to it */
    if (ssGetOutputPortFrameData(S, 0) == FRAME_INHERITED) {
        ssSetOutputPortFrameData(S, 0, frameData);
    }
}
#endif


static void mdlTerminate(SimStruct *S)
{
    UNUSED_ARG(S); /* unused input argument */
}


#ifdef	MATLAB_MEX_FILE    
#include "simulink.c"      
#else
#include "cg_sfun.h"       
#endif

/* [EOF] sreshape.c */
