/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,       */
/*               1997-2001, All Rights Reserved                         */
/*                                                                      */
/* MODULE NAME : mvlu_rt.c                                              */
/* PRODUCT(S)  :                                                        */
/*                                                                      */
/* MODULE DESCRIPTION :                                                 */
/*                                                                      */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :                            */
/*              main                                                    */
/*                                                                      */
/* MODIFICATION LOG :                                                   */
/*  Date     Who   Rev                  Comments                        */
/* --------  ---  ------   -------------------------------------------  */
/* 01/18/08  JRB     11    We allow up to 15 char LNName (sclparse.c),	*/
/*			   so max flatname must be MAX_IDENT_LEN-15.	*/
/* 07/20/07  JRB     10    Flatnames must be shorter to add LNName later*/
/* 11/22/06  JRB     09    Free bufs if _mvlu_build_uca_name_tbl fails.	*/
/* 01/30/06  GLB     09    Integrated porting changes for VMS           */
/* 03/11/04  GLB     08    Added "#ifdef DEBUG_SISCO" for "thisFileName"                    	*/
/* 09/18/03  JRB     07    Allow array of structures.			*/
/*			   DON'T allow array of arrays (return error).	*/
/* 05/02/03  JRB     06    switch(rt->el_tag): Use default for most cases*/
/* 04/29/03  JRB     05    Chg several functions to return ST_RET.	*/
/* 04/24/03  JRB     04    Use MAX_IDENT_LEN define.			*/
/* 03/13/03  JRB     03    mvlu_proc_rt_type: Chg to use RUNTIME_CTRL.	*/
/*			   MVLU_UCA_NAME_CTRL: Chg ucaName member from	*/
/*			   ptr to array (saves allocs).			*/
/* 12/11/02  JRB     02    Use new mvl_uca.h				*/
/* 11/14/02  MDE     01    New module, extracted fo_uca.c		*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include "gen_list.h"

#include "mmsdefs.h"
#include "mms_pvar.h"
#include "mms_vvar.h"
#include "mem_chk.h"
#include "mvl_uca.h"

/************************************************************************/
/************************************************************************/

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

/************************************************************************/
/************************************************************************/
/* STATIC VARIABLES							*/

#define MAX_PREFIX_LEN  200
#define MAX_NEST_LEVEL  50
static ST_CHAR *namePrefix[MAX_NEST_LEVEL];
static ST_INT nestLevel;


/************************************************************************/
/* STATIC FUNCTIONS							*/

static ST_RET _mvlu_set_rt_sort_num (ST_CHAR *typeName, 
				      RUNTIME_TYPE *rt, ST_INT numRt, 
				      MVLU_UCA_NAME_CTRL **ucaNamesOut);
static ST_VOID _mvlu_set_rt_size (RUNTIME_TYPE *rt) ;
static ST_VOID _mvlu_set_rt_offset (RUNTIME_TYPE *rt, ST_INT numRt);
static ST_RET _mvlu_build_uca_name_tbl (RUNTIME_TYPE *rt, ST_INT rt_num, 
 		       MVLU_UCA_NAME_CTRL *mvluFoundryInfo);
static ST_RET _mvlu_save_rt_uca_name (ST_INT nestLevel, RUNTIME_TYPE *rt,
 			 MVLU_UCA_NAME_CTRL *mvluFoundryInfo);
static ST_VOID _mvlu_sort_uca_names (MVLU_UCA_NAME_CTRL *ucaNames, 
				RUNTIME_TYPE *rt, ST_INT numRt);

/************************************************************************/
/************************************************************************/
/*			mvlu_proc_rt_type				*/
/************************************************************************/

ST_RET mvlu_proc_rt_type (ST_CHAR *typeName, RUNTIME_CTRL *rt_ctrl, 
			  MVLU_UCA_NAME_CTRL **ucaNamesOut)
  {
RUNTIME_TYPE *rt;
ST_INT i;
ST_RET ret;

/* Set the sortedNum for each RT element */
  ret = _mvlu_set_rt_sort_num (typeName, rt_ctrl->rt_first, rt_ctrl->rt_num, ucaNamesOut);
  if (ret)
    return(ret);	/* don't bother continuing	*/

/* Find the offset of the data for each RT within the type		*/
  _mvlu_set_rt_offset (rt_ctrl->rt_first, rt_ctrl->rt_num);

/* Find the size of each RT within the type				*/
  rt = rt_ctrl->rt_first;
  for (i = 0; i < rt_ctrl->rt_num; ++i, ++rt)
    _mvlu_set_rt_size (rt);

  return (ret);
  }

/************************************************************************/
/************************************************************************/
/*				_mvlu_set_rt_size			*/
/************************************************************************/
/* This function would not be needed except that the UCA structure 	*/
/* handling needs to know the size of each subelement too ...		*/

static ST_VOID _mvlu_set_rt_size (RUNTIME_TYPE *rt) 
  {
ST_INT numUcaRt;
ST_INT blockedLen;

  if (rt->el_tag == RT_ARR_END ||  rt->el_tag == RT_STR_END)
    {
    return;
    }

  if (rt->el_tag == RT_STR_START)
    numUcaRt = rt->u.str.num_rt_blks+2;
  else if (rt->el_tag == RT_ARR_START)
    numUcaRt = rt->u.arr.num_rt_blks+2;
  else
    numUcaRt = 1;

/* Now we can find the size of this element and it's associates */
  blockedLen = ms_get_blocked_length (rt, numUcaRt);
  rt->offset_to_last = blockedLen;
  }

/************************************************************************/
/************************************************************************/
/*                       _mvlu_set_rt_offset                            */
/************************************************************************/

static ST_VOID _mvlu_set_rt_offset (RUNTIME_TYPE *rt, ST_INT numRt)
  {
ST_INT data_offset;
SD_CONST RUNTIME_TYPE *rt_end;
ST_INT arr_loops[ASN1_MAX_LEVEL];
ST_BOOLEAN arr_el_set[ASN1_MAX_LEVEL];
ST_INT arr_loop_level;

  arr_loop_level = 0;
  rt_end = rt + numRt;  	        /* end block			*/
  data_offset = 0;

/* Traverse the RT type as though we were doing a ASN.1 to LOCAL	*/
  while (rt < rt_end)
    {
    if (rt->el_tag == RT_ARR_END)	/* treat case of array ending	*/
      {
      if (--arr_loops[arr_loop_level] > 0)/* if need to do next ar elmnt*/
        {
        rt -= rt->u.arr.num_rt_blks;    /* mv rt to start of arr	*/
        continue; 
        }
      else
        --arr_loop_level;
      }
    if (arr_loop_level == 0 || arr_el_set[arr_loop_level] == SD_FALSE)
      {
      rt->mvluTypeInfo.offSet = data_offset; /* Set the offset		*/
	  /* renxiaobao Êý×é*/
      /*arr_el_set[arr_loop_level] = SD_TRUE;*/
      }

    data_offset += rt->el_size;		/* Offset for next		*/

    if (rt->el_tag == RT_ARR_START)	/* Set up array looping		*/
      {
      ++arr_loop_level;
      arr_loops[arr_loop_level] = rt->u.arr.num_elmnts;
      arr_el_set[arr_loop_level] = SD_FALSE;
      }
    rt++;			/* point to next runtime element	*/
    }
  }


/************************************************************************/
/************************************************************************/
/*                   _mvlu_set_rt_sort_num                              */
/************************************************************************/

static ST_RET _mvlu_set_rt_sort_num (ST_CHAR *typeName, 
				      RUNTIME_TYPE *rt, ST_INT numRt, 
				      MVLU_UCA_NAME_CTRL **ucaNamesOut)
  {
MVLU_UCA_NAME_CTRL *ucaNames;
ST_INT i;

  ucaNames = (MVLU_UCA_NAME_CTRL *) chk_calloc (numRt, sizeof (MVLU_UCA_NAME_CTRL));
  nestLevel = 0;

/* Derive the UCA names within this type */
  namePrefix[0] = (ST_CHAR *) chk_calloc (1, MAX_PREFIX_LEN);
  if (typeName != NULL)
    strcpy (namePrefix[0], typeName);

  if (_mvlu_build_uca_name_tbl (rt, numRt, ucaNames))
    {
    chk_free(ucaNames);
    chk_free(namePrefix[0]);
    return (SD_FAILURE);	/* don't bother continuing	*/
    }

/* Now sort the UCA names within the type	     			*/
  _mvlu_sort_uca_names (ucaNames, rt, numRt);    

  for (i = 0; i < MAX_NEST_LEVEL; ++i)
    {
    if (namePrefix[i] != NULL)
      {
      chk_free (namePrefix[i]);
      namePrefix[i] = NULL;
      }
    }
  if (ucaNamesOut == NULL)
    chk_free (ucaNames);
  else
    *ucaNamesOut = ucaNames;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       _mvlu_build_uca_name_tbl                       */
/* Start with retCode=SD_SUCCESS. If any call to _mvlu_save_rt_uca_name	*/
/* fails, set retCode=SD_FAILURE. Do not return immediately because we	*/
/* want to log as many problems as possible first.			*/
/************************************************************************/

static ST_RET _mvlu_build_uca_name_tbl (RUNTIME_TYPE *rt, ST_INT rt_num, 
			      MVLU_UCA_NAME_CTRL *ucaNames)
  {
ST_INT i;
ST_RET retCode=SD_SUCCESS;

  nestLevel = 0;
  for (i = 0; i < rt_num; ++i, ++rt)
    {
    ucaNames[i].rtIndex = i;
    ucaNames[i].rt = rt;
    switch (rt->el_tag)
      {
      case RT_STR_START :
        if (ms_comp_name_pres(rt))
          {
          if (_mvlu_save_rt_uca_name (nestLevel, rt, &ucaNames[i]))
            retCode = SD_FAILURE;	/*continue but eventually return this*/
      
          ++nestLevel;
          if (namePrefix[nestLevel] == NULL)
            namePrefix[nestLevel] = (ST_CHAR *) chk_calloc (1, MAX_PREFIX_LEN);
      
          strcpy (namePrefix[nestLevel], namePrefix[nestLevel-1]);
          strcat (namePrefix[nestLevel], "$");
          strcat (namePrefix[nestLevel], ms_comp_name_find(rt));
          }
      break;

      case RT_STR_END :
        --nestLevel;
      break;

      case RT_ARR_START :
        if (_mvlu_save_rt_uca_name (nestLevel, rt, &ucaNames[i]))
          retCode = SD_FAILURE;	/*continue but eventually return this*/

      /* We let the array element inherit our name so we will generate 	*/
      /* rd/wr ind handler functions and references for the contained 	*/
      /* primitive elements						*/
      /* This causes duplicate names (name of array same as name of	*/
      /* object IN array). That's OK because names of anything inside	*/
      /* array are dropped when sorted (see _mvlu_sort_uca_names).	*/
      /* The "sorted" array of names is used in GetNameList response.	*/

        if (ms_is_rt_prim (rt+1) || (rt+1)->el_tag==RT_STR_START)
	  {

#if defined(USE_RT_TYPE_2)
          (rt+1)->comp_name_ptr = rt->comp_name_ptr;
#elif defined(USE_RT_TYPE_3)
          (rt+1)->name_index = rt->name_index;
#else
          strcpy ((rt+1)->name, rt->name);
#endif
	  }
	else
	  {
	  SLOGALWAYS1 ("Warning: Arrays of arrays not supported for UCA (%s)", 
	  				ucaNames[i].ucaName);
          retCode = SD_FAILURE;	/*continue but eventually return this*/
	  }
      break;

      case RT_ARR_END :
      break;

      default :
        if (_mvlu_save_rt_uca_name (nestLevel, rt, &ucaNames[i]))
          retCode = SD_FAILURE;	/*continue but eventually return this*/
      break;
      }
    }
  return (retCode);
  }

/************************************************************************/
/*                      _mvlu_save_rt_uca_name                          */
/* Start with retCode=SD_SUCCESS. If anything fails, set retCode=SD_FAILURE.*/
/************************************************************************/
static ST_RET _mvlu_save_rt_uca_name (ST_INT nestLevel, 
				RUNTIME_TYPE *rt, 
				MVLU_UCA_NAME_CTRL *nameDest)
  {
ST_CHAR nameBuf[256];
ST_RET retCode = SD_SUCCESS;

  nameDest->ucaName [0] = '\0';		/* start with empty string	*/
  nameDest->rt = rt;
  if (ms_comp_name_pres(rt))
    {
    sprintf (nameBuf, "%s$%s", namePrefix[nestLevel], ms_comp_name_find(rt));
    /* Make sure there is room left for longest allowed Logical Node Name*/
    /* (11 characters according to IEC 61850-7-2 but we allow 15).	*/
    /* NOTE: this allows 5 or 6 more char than 61850-7-2 specifies, so */
    /*       it is more forgiving, but it prevents illegal MMS names.	*/
    if (strlen (nameBuf) > (MAX_IDENT_LEN-15))
      {
      SLOGALWAYS2 ("Error: Flattened IEC 61850 variable name '%s' > %d characters. Illegal.",
					nameBuf, (MAX_IDENT_LEN-15));
      SLOGCALWAYS0 ("Must allow space for LNName up to 15 characters");
      retCode = SD_FAILURE;
      }
    else
      strcpy (nameDest->ucaName, nameBuf);
    }
  return (retCode);
  }

/************************************************************************/
/************************************************************************/
/* SORTING UCA NAMES WITHIN A TYPE					*/
/************************************************************************/
/*                       elementCompare                                 */
/************************************************************************/
/* This function is sorting the namelist table                          */

static ST_INT elementCompare (const MVLU_UCA_NAME_CTRL *a, 
			      const MVLU_UCA_NAME_CTRL *b)
  {
  return (strcmp (a->ucaName, b->ucaName));
  }

/************************************************************************/
/*                       _mvlu_sort_uca_names				*/
/************************************************************************/

static ST_VOID _mvlu_sort_uca_names (MVLU_UCA_NAME_CTRL *ucaNames, 
				RUNTIME_TYPE *rt, ST_INT numRt)
  {
ST_INT i;
ST_INT numNames;
MVLU_UCA_NAME_CTRL *sortedNames;	/* compressed/sorted array of names*/

  sortedNames = (MVLU_UCA_NAME_CTRL *) M_MALLOC (MSMEM_GEN, numRt*sizeof(MVLU_UCA_NAME_CTRL));

/* Compress the name table */  
  numNames = 0;
  for (i = 0; i < numRt; ++i, ++rt)
    {
    if (ucaNames[i].ucaName[0] != '\0')
      {
      sortedNames[numNames] = ucaNames[i];
      ++numNames;
/* CRITICAL:
 * The "ucaNames" array contains names for objects inside arrays.
 * They must be kept in the original array, because they are needed
 * for generating leaf function names.
 * However, they are NOT legal variable names, so they are NOT
 * copied to the SORTED array (used for GetNameList response).
 * This is done by skipping over the runtime type for the array.
 */
      if (rt->el_tag==RT_ARR_START)
        {
        rt += (rt->u.arr.num_rt_blks + 1);
        i += (rt->u.arr.num_rt_blks + 1);
        }
      }
    }

/* OK, we now have a raw table of names, go ahead and sort them */
  qsort (sortedNames, numNames, sizeof (MVLU_UCA_NAME_CTRL), 
	 (int (*)(const void *,const void *)) elementCompare);

#if 0	/* DEBUG: enable this code to debug sorting algorithm	*/
  if (numNames)
    {
    ST_INT j;
    SLOGALWAYS0 ("Array of UCA variable names (Uncompressed/Unsorted)");
    for (j = 0; j<numRt; j++)
      SLOGCALWAYS1 ("%s", ucaNames[j].ucaName);

    SLOGALWAYS0 ("Array of UCA variable names (Compressed/Sorted)");
    for (j = 0; j<numNames; j++)
      SLOGCALWAYS1 ("%s", sortedNames[j].ucaName);
    }
#endif

/* Now assign sort numbers to the RT elements */
  for (i = 0; i < numNames; ++i)
    {
    sortedNames[i].rt->mvluTypeInfo.sortedNum = i+1;
    }
  M_FREE (MSMEM_GEN, sortedNames);
  }

