/* sui_dinfo.c
 *
 * DINFO (data info structures) common functions
 *  
 * Version 0.1
 * Copyright holders and project originators
 *   (C) 2002 by Roman Bartosinski, bartosr@centrum.cz
 *   (C) 2002 by PiKRON Ltd. http://www.pikron.com
 *   (C) 2001 by Pavel Pisa pisa@cmp.felk.cvut.cz
 *
 * The SUITK project can be used and copied under next licenses
 *   - MPL - Mozilla Public License
 *   - GPL - GNU Public License
 *   - LGPL - Lesser GNU Public License
 *   - and other licenses added by project originators
 * Code can be modified and re-distributed under any combination
 * of the above listed licenses. If contributor does not agree with
 * some of the licenses, he can delete appropriate line.
 * Warning, if you delete all lines, you are not allowed to
 * distribute code or build project.
 *
 */

#include <string.h>

#include "ul_utmalloc.h"
#include "sui_dinfo.h"
#include "sui_event.h"

#define SUI_DINFO_WITH_DBUFF_DINFO

#ifdef SUI_DINFO_WITH_DBUFF_DINFO
    #include "sui_dinfo_dbuff.h"
#endif /*SUI_DINFO_WITH_DBUFF_DINFO*/

#ifdef SUI_DINFO_WITH_EVC
    #include "ul_evcbase.h"
    #include "sui_dievc.h"
#endif /*SUI_DINFO_WITH_EVC*/

/**
 * sui_dinfo_inc_refcnt - Increase reference count of DINFO
 * @datai: Pointer to dinfo structure.
 *
 * File: sui_dinfo.c
 */
void sui_dinfo_inc_refcnt( sui_dinfo_t *datai) {
  if ( !datai) return;
  if( datai->refcnt >= 0) datai->refcnt++;
}

/**
 * sui_dinfo_dec_refcnt - Decrease reference count of DINFO
 * @datai: Pointer to dinfo structure.
 *
 * If the reference count reaches zero, DINFO starts to be destroyed.
 * The event %SUEV_COMMAND with command %SUCM_DONE is sent to dinfo,
 * next event %SUEV_FREE is emmited or direct free() is called
 * the %SUEV_FREE is disabled.
 * File: sui_dinfo.c
 */
void sui_dinfo_dec_refcnt( sui_dinfo_t *datai) {
  if ( !datai) return;
  if(datai->refcnt>0) datai->refcnt--;
  if(!datai->refcnt) {
    if(datai->hevent) {
      sui_event_t event;
      if( datai->evmask & SUEV_COMMAND) {
        event.what = SUEV_COMMAND;
        event.u_ud()message.command = SUCM_DONE;
        datai->hevent(datai,&event);
      }
    }
   #ifdef SUI_DINFO_WITH_EVC
    if(datai->tx_hub != NULL){
      evc_tx_hub_done(datai->tx_hub);
      free(datai->tx_hub);
      datai->tx_hub=NULL;
    }
   #endif /*SUI_DINFO_WITH_EVC*/
    if(datai->hevent) {
      sui_event_t event;
      if( datai->evmask & SUEV_FREE) {
        event.what = SUEV_FREE;
        datai->hevent(datai,&event);
        return;
      }
    }
    free(datai);
  }
}

/********************************************************************/
/* Dynamic struct */


/**
 * sui_create_dinfo - Creates new dynamic DINFO
 * @adata: DINFO type specific pointer to the data
 * @afdig: Number of fractional digits if the fixed decimal 
 *	   point format is used
 * @amin:  The minimal allowed value
 * @amax:  The maximal allowed value
 * @ainfo: DINFO type specific pointer
 * @rd:    Pointer to the read processing function
 * @wr:    Pointer to the write processing function
 *
 * Return Value: Pointer to newly created DINFO.
 * File: sui_dinfo.c
 */
sui_dinfo_t *sui_create_dinfo( void *adata, int afdig, long amin, long amax,
                               long ainfo, sui_datai_rdfnc_t *rd, sui_datai_wrfnc_t *wr) {
  sui_dinfo_t *di;
  if (!(di=malloc(sizeof(sui_dinfo_t)))) return NULL;
  memset(di,0,sizeof(sui_dinfo_t));
  sui_dinfo_inc_refcnt(di);
  di->ptr = adata;
  di->fdigits = afdig;
  di->minval = amin;
  di->maxval = amax;
  di->info = ainfo;
  di->rdval = rd;
  di->wrval = wr;
  return di;
}

/**
 * sui_create_dinfo_int - Creates DINFO for signed integer or fixed point data
 * @adata:    Pointer to the signed char, short, int, long or fixed point data
 * @aidxsize: Allowed range of indexes form 0 to @aidxsize-1, if zero,
 *	      then no check
 * @asize:    The size of the integer type representation returned by sizeof()
 *
 * Return Value: Pointer to newly created DINFO.
 * File: sui_dinfo.c
 */
sui_dinfo_t *sui_create_dinfo_int( void *adata, long aidxsize, int asize) {  
  sui_datai_rdfnc_t *ard; 
  sui_datai_wrfnc_t *awr;
  int type = SUDI_TINFO_UNKNOWN;

  sui_dinfo_t *dinfo;
  
  if(asize==sizeof(long)) {
    ard=sui_long_rdval;
    awr=sui_long_wrval;
    type = SUDI_TINFO_LONG;
  } else if(asize==sizeof(int)) {
    ard=sui_int_rdval;
    awr=sui_int_wrval;
    type = SUDI_TINFO_INT;
  } else if(asize==sizeof(short)) {
    ard=sui_short_rdval;
    awr=sui_short_wrval;
    type = SUDI_TINFO_SHORT;
  } else if (asize==sizeof(char)) {
    ard=sui_char_rdval;
    awr=sui_char_wrval;
    type = SUDI_TINFO_CHAR;
  } else
    return NULL;

  dinfo=sui_create_dinfo( adata, 0, 0, 0, 0, ard, awr);
  if(dinfo) {
      dinfo->idxsize=aidxsize;
      dinfo->tinfo = type;
  }
  return dinfo;
}

/**
 * sui_create_dinfo_uint - Creates DINFO for unsigned integer or fixed point data
 * @adata:    Pointer to the unsigned char, short, int, long or fixed point data
 * @aidxsize: Allowed range of indexes form 0 to @aidxsize-1, if zero,
 *	      then no check
 * @asize:    The size of the integer type representation returned by sizeof()
 *
 * Return Value: Pointer to newly created DINFO.
 * File: sui_dinfo.c
 */
sui_dinfo_t *sui_create_dinfo_uint( void *adata, long aidxsize, int asize) {  
  sui_datai_rdfnc_t *ard; 
  sui_datai_wrfnc_t *awr;
  int type = SUDI_TINFO_UNKNOWN;
  
  sui_dinfo_t *dinfo;

  if(asize==sizeof(long)) {
    ard=sui_ulong_rdval;
    awr=sui_ulong_wrval;
    type = SUDI_TINFO_ULONG;
  } else if(asize==sizeof(int)) {
    ard=sui_uint_rdval;
    awr=sui_uint_wrval;
    type = SUDI_TINFO_UINT;
  } else if(asize==sizeof(short)) {
    ard=sui_ushort_rdval;
    awr=sui_ushort_wrval;
    type = SUDI_TINFO_USHORT;
  } else if (asize==sizeof(char)) {
    ard=sui_uchar_rdval;
    awr=sui_uchar_wrval;
    type = SUDI_TINFO_UCHAR;
  } else
    return NULL;

  dinfo=sui_create_dinfo( adata, 0, 0, 0, 0, ard, awr);
  if(dinfo) {
      dinfo->idxsize=aidxsize;
      dinfo->tinfo = type;
  }
  return dinfo;
}

/********************************************************************/
/* Data connection processing */

int sui_long_rdval( sui_dinfo_t *datai, long idx, void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(long*)buf=*((long*)datai->ptr+idx);
  return SUDI_DATA_OK;
}

int sui_long_wrval( sui_dinfo_t *datai, long idx, const void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((long*)datai->ptr+idx)=*(long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

int sui_int_rdval( sui_dinfo_t *datai, long idx, void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(long*)buf=*((int*)datai->ptr+idx);
  return SUDI_DATA_OK;
}

int sui_int_wrval( sui_dinfo_t *datai, long idx, const void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((int*)datai->ptr+idx)=*(long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

int sui_short_rdval( sui_dinfo_t *datai, long idx, void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(long*)buf=*((short*)datai->ptr+idx);
  return SUDI_DATA_OK;
}

int sui_short_wrval( sui_dinfo_t *datai, long idx, const void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((short*)datai->ptr+idx)=(short)*(long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

int sui_char_rdval( sui_dinfo_t *datai, long idx, void *buf) {
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(long *)buf = *((char *)(datai->ptr)+idx);
  return SUDI_DATA_OK;
}

int sui_char_wrval( sui_dinfo_t *datai, long idx, const void *buf) {
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((char*)datai->ptr+idx)=(char)*(long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

/*----------------*/

int sui_ulong_rdval( sui_dinfo_t *datai, long idx, void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(unsigned long*)buf=*((unsigned long*)datai->ptr+idx);
  return SUDI_DATA_OK;
}

int sui_ulong_wrval( sui_dinfo_t *datai, long idx, const void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((unsigned long*)datai->ptr+idx)=*(unsigned long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

int sui_uint_rdval( sui_dinfo_t *datai, long idx, void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(unsigned long*)buf=*((unsigned int*)datai->ptr+idx);
  return SUDI_DATA_OK;
}

int sui_uint_wrval( sui_dinfo_t *datai, long idx, const void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((unsigned int*)datai->ptr+idx)=*(unsigned long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

int sui_ushort_rdval( sui_dinfo_t *datai, long idx, void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(unsigned long*)buf=*((unsigned short*)datai->ptr+idx);
  return SUDI_DATA_OK;
}

int sui_ushort_wrval( sui_dinfo_t *datai, long idx, const void *buf)
{
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((unsigned short*)datai->ptr+idx)=(short)*(unsigned long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

int sui_uchar_rdval( sui_dinfo_t *datai, long idx, void *buf) {
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *(long *)buf = *((unsigned char *)(datai->ptr)+idx);
  return SUDI_DATA_OK;
}

int sui_uchar_wrval( sui_dinfo_t *datai, long idx, const void *buf) {
  if(datai->idxsize&&(idx>=datai->idxsize)) return SUDI_DATA_EINDX;
  *((unsigned char*)datai->ptr+idx)=(char)*(unsigned long*)buf;
  sui_dinfo_changed(datai);
  return SUDI_DATA_OK;
}

/********************************************************************/
/* Data connection processing  with TINFO */


/**
 * sui_rd_long - Reads long integer data from specified DINFO
 * @datai: Pointer to the DIONFO
 * @idx:   Index of read data inside DINFO.
 * @buf:   Pointer to where the read value is stored 
 *
 * Return Value: Operation result code, %SUDI_DATA_OK in the case of success.
 * File: sui_dinfo.c
 */
int sui_rd_long( sui_dinfo_t *datai, long idx, long *buf) {
  if(!datai->rdval) return SUDI_DATA_EPERM;
  #ifdef SUI_DINFO_WITH_DBUFF_DINFO
  if(datai->tinfo == SUDI_TINFO_DBUFF) {
      return sui_dinfo_dbuff_rd_long(datai, idx, buf);
  }
  #endif
  return datai->rdval( datai, idx, buf);
}

/**
 * sui_wr_long - Writes long integer data to specifies DINFO
 * @datai: Pointer to the DIONFO
 * @idx:   Index of read data inside DINFO.
 * @buf:   Pointer to the new data value
 *
 * Return Value: Operation result code, %SUDI_DATA_OK in the case of success.
 * File: sui_dinfo.c
 */
int sui_wr_long( sui_dinfo_t *datai, long idx, const long *buf) {
  if(!datai->wrval) return SUDI_DATA_EPERM;
  #ifdef SUI_DINFO_WITH_DBUFF_DINFO
  if(datai->tinfo == SUDI_TINFO_DBUFF) {
      return sui_dinfo_dbuff_wr_long(datai, idx, buf);
  }
  #endif
  return datai->wrval( datai, idx, buf);
}

/********************************************************************/
// events and event connectors

#ifdef SUI_DINFO_WITH_EVC

typedef void sui_dinfo_propagate_event_fnc_t(void *context, sui_event_t *event);

int sui_dinfo_propagate_event(evc_link_t *link, va_list args)
{
  void *context;
  sui_dinfo_propagate_event_fnc_t *fnc;
  
  if(link->dead){
    if(link->standalone && link->dst.standalone.weakptr){
      *link->dst.standalone.weakptr=NULL;
      link->dst.standalone.weakptr=NULL;
    }
    return 0;
  }
  if(link->standalone){
    fnc=(sui_dinfo_propagate_event_fnc_t*) link->dst.standalone.rx_fnc;
    context=link->dst.standalone.context;
  }else{
    if(!link->dst.multi.hub) return 0;
    fnc=(sui_dinfo_propagate_event_fnc_t*) link->dst.multi.hub->rx_fnc;
    context=link->dst.multi.hub->context;
  }
  if(fnc)
    fnc(context, va_arg(args, sui_event_t *));
  return 0;
}


void sui_dinfo_do_changed( sui_dinfo_t *datai)
{
  sui_event_t event;
  if(!datai->tx_hub) return;
  event.what = SUEV_SIGNAL;
  event.u_ud()message.command = SUSIG_DINFO_CHANGED;
  event.u_ud()message.ptr = datai;
  evc_tx_hub_emit(datai->tx_hub, &event);
}

int sui_dinfo_evc_link_to(sui_dinfo_t *datai, evc_link_t *link, evc_rx_hub_t *dst)
{
  evc_prop_fnc_t *prop;

  if(!link)  return -1;
  if(!dst && !link->standalone) return -1;

  if(!datai->tx_hub){
    evc_tx_hub_t *hub;
    hub=malloc(sizeof(evc_tx_hub_t));
    if(!hub) return -1;
    datai->tx_hub=hub;
    evc_tx_hub_init(hub);
  }
  
  prop=link->propagate;
  if(!prop)
    prop=&sui_dinfo_propagate_event;
  
  if(link->standalone){
    return evc_link_connect_standalone(link, datai->tx_hub, prop);
  }else{
    return evc_link_connect(link, datai->tx_hub, dst, prop);
  }
}

void sui_dinfo_pass_hevent_link_to(sui_dinfo_t *datai, sui_event_t *event)
{
  if(datai->hevent) datai->hevent(datai, event);
  else {
    sui_dinfo_evc_link_to(datai, (evc_link_t*)event->u_ud()message.ptr, 
  	   (evc_rx_hub_t*)event->u_ud()message.info);
    event->what=SUEV_NOTHING;
    event->u_ud()message.ptr=datai;
  }
}

void sui_dinfo_connect_to_link(sui_dinfo_t *datai, evc_link_t *link,
			evc_rx_hub_t *dst, evc_prop_fnc_t *prop)
{
  link->propagate=prop;

  if(!datai->hevent){
    /* speedup code */
    sui_dinfo_evc_link_to(datai, link, dst);
  }else{
    sui_event_t event;
    event.what = SUEV_COMMAND;
    event.u_ud()message.command = SUCM_EVC_LINK_TO;
    event.u_ud()message.ptr = link;
    event.u_ud()message.info = (long)dst;
    sui_dinfo_pass_hevent_link_to(datai, &event);
  }
}

void sui_dinfo_connect_to_hub(sui_dinfo_t *datai, evc_rx_hub_t *dst,
                              evc_prop_fnc_t *prop)
{
  evc_link_t *link;
  if(!dst) return;

  link=evc_link_new();
  if(!link) return;
  
  sui_dinfo_connect_to_link(datai, link, dst, prop);

  evc_link_dec_refcnt(link);
}


int sui_dinfo_add_weakptr(sui_dinfo_t *datai, sui_dinfo_t **weakptr)
{
  evc_link_t *link;
  int ret;

  link=evc_link_new_standalone(NULL, NULL);
  if(!link) return -1;
   
  link->dst.standalone.weakptr=(void**)weakptr;
  link->propagate=NULL;
  
  ret=sui_dinfo_evc_link_to(datai, link, NULL);

  evc_link_dec_refcnt(link);
  
  return ret;
}

int sui_dinfo_del_weakptr(sui_dinfo_t *datai, sui_dinfo_t **weakptr)
{
  if(datai->tx_hub) return 0;
  return evc_tx_hub_eol_weakptr(datai->tx_hub, (void **)weakptr);
}

#else /*SUI_DINFO_WITH_EVC*/

void sui_dinfo_do_changed( sui_dinfo_t *datai){;}

#endif /*SUI_DINFO_WITH_EVC*/

/********************************************************************/
// scale_proxy and simple_proxy

int sui_scale_rdval(sui_dinfo_t *dinfo, long indx, void *buf) {
  long val;
  int ret;
  sui_dinfo_t *dfrom=(sui_dinfo_t*)(dinfo->ptr);
  if(!dfrom) return SUDI_DATA_NCON;
  if(!dfrom->rdval) return SUDI_DATA_ERR;
  if(dinfo->info) indx=dinfo->info;
  ret=sui_rd_long(dfrom,indx,&val);
  if(ret!=SUDI_DATA_OK) return ret;
  val*=((sui_dinfo_scale_t*)dinfo)->multiply;
  val/=((sui_dinfo_scale_t*)dinfo)->divide;
  *(long*)buf=val;
  return SUDI_DATA_OK;
}

int sui_scale_wrval(sui_dinfo_t *dinfo, long indx, const void *buf) {
  long val;
  int ret;
  sui_dinfo_t *dfrom=(sui_dinfo_t*)(dinfo->ptr);
  if(!dfrom) return SUDI_DATA_NCON;
  if(!dfrom->wrval) return SUDI_DATA_ERR;
  if(dinfo->info) indx=dinfo->info;
  val=*(long*)buf;
  val*=((sui_dinfo_scale_t*)dinfo)->divide;
  val/=((sui_dinfo_scale_t*)dinfo)->multiply;
  ret=sui_wr_long(dfrom,indx,&val);
  if(ret!=SUDI_DATA_OK) return ret;
  return SUDI_DATA_OK;
}

void sui_scale_minmax_update(sui_dinfo_t *dinfo) {
  long val;
  sui_dinfo_t *dfrom=(sui_dinfo_t*)(dinfo->ptr);
  if(!dfrom) return;
  dinfo->fdigits=dfrom->fdigits;
  val=dfrom->minval;
  val*=((sui_dinfo_scale_t*)dinfo)->multiply;
  val/=((sui_dinfo_scale_t*)dinfo)->divide;
  dinfo->minval=val;
  val=dfrom->maxval;
  val*=((sui_dinfo_scale_t*)dinfo)->multiply;
  val/=((sui_dinfo_scale_t*)dinfo)->divide;
  dinfo->maxval=val;
}


/**
 * dinfo_scale_proxy - Creates value scale proxy DINFO
 * @dfrom:    Pointer to the underlying DINFO
 * @ainfo:    The local DINFO specific parameter
 * @amultiply: Multiply factor
 * @adivide:  Divide factor
 *
 * Creates scaling proxy DINFO. Read value is multiplied
 * by @amultiply factor and then divided by @adivide factor.
 * The long integer overflow is not checked. If the full checking
 * is required use sui_lintrans_proxy() instead which works
 * with wider numbers representations and checks for all overflow cases.
 * Return Value: Pointer to newly created DINFO.
 * File: sui_dinfo.c
 */
sui_dinfo_t *dinfo_scale_proxy( sui_dinfo_t *dfrom, long ainfo, 
                               long amultiply, long adivide) {
  sui_dinfo_t *dinfo;

  if(!dfrom) return NULL;
  dinfo=(sui_dinfo_t*)malloc(sizeof(sui_dinfo_scale_t));
  if( dinfo==NULL) return NULL;
  memset( dinfo,0,sizeof(sui_dinfo_scale_t));
  
  sui_dinfo_inc_refcnt( dfrom);
  dinfo->ptr=dfrom;
  dinfo->info=ainfo;
  dinfo->idxsize=dfrom->idxsize;
  ((sui_dinfo_scale_t*)dinfo)->multiply=amultiply;
  ((sui_dinfo_scale_t*)dinfo)->divide=adivide;
  dinfo->rdval=sui_scale_rdval;
  dinfo->wrval=sui_scale_wrval;
  dinfo->hevent=dinfo_simple_proxy_hevent;
  dinfo->evmask=(short)(SUEV_COMMAND | SUEV_FREE);
  sui_dinfo_inc_refcnt( dinfo);
  sui_scale_minmax_update(dinfo);
  return dinfo;
}

void dinfo_simple_proxy_hevent(struct sui_dinfo *dinfo, struct sui_event *event) {
  switch(event->what){
    case SUEV_COMMAND:
      if(event->u_ud()message.command==SUCM_EVC_LINK_TO){
        if(dinfo->ptr)
          sui_dinfo_pass_hevent_link_to((sui_dinfo_t*)(dinfo->ptr),event);
	return;
      }
      if(event->u_ud()message.command==SUCM_DONE){
        if(dinfo->ptr)
          sui_dinfo_dec_refcnt((sui_dinfo_t*)(dinfo->ptr));
	dinfo->ptr=NULL;
        dinfo->evmask&=(short)SUEV_FREE;
      }
      break;
    case SUEV_FREE:
      if(dinfo->ptr)
        sui_dinfo_dec_refcnt((sui_dinfo_t*)(dinfo->ptr));
      free(dinfo);
      break;
  }

}

int sui_simple_proxy_rdval(sui_dinfo_t *dinfo, long indx, void *buf) {
  sui_dinfo_t *dfrom=(sui_dinfo_t*)(dinfo->ptr);
  if(!dfrom) return SUDI_DATA_NCON;
  if(!dfrom->rdval) return SUDI_DATA_ERR;
  if(dinfo->info) indx=dinfo->info;
  return dfrom->rdval(dfrom, indx, buf);
}

int sui_simple_proxy_wrval(sui_dinfo_t *dinfo, long indx, const void *buf) {
  sui_dinfo_t *dfrom=(sui_dinfo_t*)(dinfo->ptr);
  if(!dfrom) return SUDI_DATA_NCON;
  if(!dfrom->wrval) return SUDI_DATA_ERR;
  if(dinfo->info) indx=dinfo->info;
  return dfrom->wrval(dfrom, indx, buf);
}

/**
 * dinfo_simple_proxy - Creates simple proxy DINFO
 * @dfrom:    Pointer to the underlying DINFO
 * @ainfo:    The local DINFO specific parameter which specifies
 *            index value for calling of underlying DINFO
 *
 * Return Value: Pointer to newly created DINFO.
 * File: sui_dinfo.c
 */
sui_dinfo_t *dinfo_simple_proxy(sui_dinfo_t *dfrom, long ainfo) {
  sui_dinfo_t *dinfo;

  if(!dfrom) return NULL;
  dinfo=sui_create_dinfo(dfrom, dfrom->fdigits, dfrom->minval, dfrom->maxval,
           ainfo, sui_simple_proxy_rdval, sui_simple_proxy_wrval);

  if(dinfo==NULL) return NULL;
  sui_dinfo_inc_refcnt(dfrom);
  dinfo->idxsize=dfrom->idxsize;
  dinfo->hevent=dinfo_simple_proxy_hevent;
  dinfo->evmask=(short)(SUEV_COMMAND | SUEV_FREE);
  return dinfo;
}

int dinfo_dinfopar_proxy_rdval(sui_dinfo_t *dinfo, long indx, void *buf) {
  sui_dinfo_t *dfrom=(sui_dinfo_t*)(dinfo->ptr);
  if(!dfrom) return SUDI_DATA_NCON;
  if(dinfo->info) indx=dinfo->info;
  switch(indx){
    case SUDI_INFOPAR_INFO:
      *(long*)buf=dfrom->info; break;
    case SUDI_INFOPAR_MINVAL:
      *(long*)buf=dfrom->minval; break;
    case SUDI_INFOPAR_MAXVAL:
      *(long*)buf=dfrom->maxval; break;
    case SUDI_INFOPAR_FDIGITS:
      *(long*)buf=dfrom->fdigits; break;
    case SUDI_INFOPAR_TINFO:
      *(long*)buf=dfrom->tinfo; break;
    default: return SUDI_DATA_EINDX;
  }
  return SUDI_DATA_OK;
}

int dinfo_dinfopar_proxy_wrval(sui_dinfo_t *dinfo, long indx, const void *buf) {
  sui_dinfo_t *dfrom=(sui_dinfo_t*)(dinfo->ptr);
  if(!dfrom) return SUDI_DATA_NCON;
  if(dinfo->info) indx=dinfo->info;
  switch(indx){
    case SUDI_INFOPAR_INFO:
      dfrom->info=*(long*)buf; break;
    case SUDI_INFOPAR_MINVAL:
      dfrom->minval=*(long*)buf; break;
    case SUDI_INFOPAR_MAXVAL:
      dfrom->maxval=*(long*)buf; break;
    case SUDI_INFOPAR_FDIGITS:
      dfrom->fdigits=*(long*)buf; break;
    case SUDI_INFOPAR_TINFO:
      return SUDI_DATA_EPERM;
    default: return SUDI_DATA_EINDX;
  }
  sui_dinfo_changed(dinfo);
  return SUDI_DATA_OK;
}

sui_dinfo_t *dinfo_dinfopar_proxy(sui_dinfo_t *dfrom, long aparinfo) {
  sui_dinfo_t *dinfo;
  int fdig=0;
  if((aparinfo==SUDI_INFOPAR_MINVAL)||
     (aparinfo==SUDI_INFOPAR_MAXVAL)){
    fdig=dfrom->fdigits;
  }
  
  if(!dfrom) return NULL;
  dinfo=sui_create_dinfo(dfrom, fdig, 0, 0,
           aparinfo, dinfo_dinfopar_proxy_rdval, dinfo_dinfopar_proxy_wrval);

  if(dinfo==NULL) return NULL;
  sui_dinfo_inc_refcnt(dfrom);
  dinfo->hevent=dinfo_simple_proxy_hevent;
  dinfo->evmask=(short)(SUEV_COMMAND | SUEV_FREE);
  return dinfo;
}

