/*---------------------------------------------------------------------------
pardef.h

my way to define and handle objects and its parameters

Markus Zolliker, March 2005
----------------------------------------------------------------------------
*/

#ifndef PARDEF_H
#define PARDEF_H

#include "sics.h"
#include "logger.h"

int ParPrintf(void *object, int iOut, const char *fmt, ...);

typedef enum { PAR_ALWAYS_READY, PAR_NOT_READY, PAR_NOW_READY } ReadyState;

typedef struct ParInfo {
  struct ParInfo *next;
  char *name;
  Logger *log;
  int saveIt;                   /* bool */
  int saveLog;                  /* bool */
  ReadyState state;             /* enum ReadyState */
  int sugarStatus;              /* =1: sugar object made */
} ParInfo;

#define PAR_LEVELS 8
#define PAR_NAN -999999.
#define PAR_LNAN -999999

typedef void (*ParDef) (void *);

typedef struct ParClass {
  char *name;
  size_t size;
  int level;
  struct ParClass *base[PAR_LEVELS];
} ParClass;

typedef struct ParData {
  pObjectDescriptor desc;       /* SICS object descriptor */
  ParClass *class;              /* class descriptor */
  ParDef pardef;
  char *creationCmd;            /* the command to create this object */
  char *name;                   /* object name */
  time_t logTime;               /* last logging time */
  int logPending;
  int period;                   /* logging interval */
  ParInfo *infoList;            /* list for additional info on parameters */
  pICallBack pCall;             /* sics callback function */
  SConnection *conn;            /* last connection with user or manager priv. */
  int verbose;                  /* verbosity, mainly for tests */
} ParData;

/*
  Implementation of an object:
  
  object data:
  
    The first member of the objects data struct must be a plain ParData (not a pointer)
    The ParData member may also be the first member of an nested struct, the only
    condition is, that it is at offset 0, i.e. a cast to ParData is always valid.

    typedef struct {
      ParData p;
      // object members:
      float value;
      char *text;
      FooSpecialObject *obj;
    } FooData;
    
    a static class object of type ParClass has to be declared and initialized
    with its name and the object data size:
    
      static ParClass fooClass {"Foo", sizeof(FooData) }
    
    at initialisation, or at least before it is used first, it has
    to be initialized with
    
      ParMakeClass(&fooClass, NULL);
      
    (NULL as the second argument makes ParData to be the base class,
    if more levels of inheritance are used, use the base class as argument)
    
  parameter definition:
  
    The parameters definition is within the pardef function, for example

      void FooPardef(void *object) {
        FooData *data = ParCast(&fooClass, object);
        
        ParName("value"); ParFmt("%.3f"); ParFloat(&data->value, 0.0);
        ParName("text"); ParList(NULL); ParStr(&data->text, "");
        if (ParAction() == PAR_KILL) {
          if (data->obj) {
            FreeSpecialObject(data->obj);
            data->obj = NULL;
          }
        }
      }
    
    The first line in FooPardef is a cast converting the anonymous object to
    your data type. ParCast does some validity checks, which are strongly recommended.
    
    The next two lines are parameters definitions. A parameter definition starts always
    with ParName, followed by some options, and ends with the parameter type definition.
    
    FooPardef is called for all operations on data, so do not insert long calculations
    inside it. For special actions, code may be inserted for something like killing
    members.
    
    All char arguments in the following functions might be referred even after
    the function returned. If they are not a constant, it is the callers responsibility
    to make its contents valid whenever they are used.
    
*/
void ParName(char *name);       /* name must not change during lifetime of the object,
                                   normally it is a constant */
/*
  Options (to be called before parameter type definitions):
*/
void ParFmt(char *fmt);         /* format (float only) */
void ParAccess(int access);     /* access rights (default usInternal = read only) */
void ParSave(int save);         /* save on status file (0: do not svae, 1: save, 2: saved by driver
                                   (default: 0 for ParAccess(usInternal), 1 for others) */
void ParEnum(char *list[]);     /* automatic keyword to number conversion */
void ParList(char *group);      /* ParList may be repeated with several group arguments,
                                   to appear on a list <group> subcommand. Give NULL
                                   for std list, "" for no list */
void ParTail(char *tail);       /* comment to appear after list entry (e.g. units).
                                   If ParList is not called, ParTail will force to appear
                                   in standard list. */
void ParLogAs(char *name);      /*  switch log on and give sugar name (NULL for no logging) */

/*
  Parameter type definitions:
                                select one out of these per parameter, with 
                                a pointer to the corresponding object member as argument
*/
void ParFloat(float *value, float defValue);
void ParInt(int *value, int defValue);
void ParFixedStr(char *value, int maxsize, char *defValue);     /* fixed size string */
void ParStr(char **value, char *defValue);      /* dynamic string */

typedef int (*ParCommand) (void *object, void *userarg, int argc,
                           char *argv[]);
    /* command function to be executed */
int ParCmd(ParCommand cmd, void *userarg);      /* command, returns 0 when not executed
                                                   else return value from cmd, default access
                                                   is usUser */
/*
  If code has to be executed for a specific to a parameter and and action,
  ParActionIs (see below) may be used. Typical usage:
  
  if (ParActionIs(PAR_SET) > 0) {
    things to do when parameter has changed
  }
  
  A parameter definition finishes after these calls.
  
  There are predefined actions which are all handled by this module:
*/
typedef enum {
  PAR_NOOP,                     /* no operation */
  PAR_INIT,                     /* initialize object (called during initialisation) */
  PAR_SHOW,                     /* show single parameter (1) */
  PAR_SET,                      /* set a single parameter (1) */
  PAR_GET,                      /* get a single float parameter (1) */
  PAR_LIST,                     /* list all or a group of parameters (2) */
  PAR_LOG,                      /* log all parameters activated for logging (2) */
  PAR_LOGSWITCH,                /* (de)activate single parameter for logging (1) */
  PAR_SAVE,                     /* save all parameters activated for saving (2) */
  PAR_SAVESWITCH,               /* (de)activate single parameter for saving (1) */
  PAR_KILL                      /* kill content (called before remove) */
} ParAct;

int ParActionIs(ParAct action);
         /* to be called inside the pardef function.
            returns 0, when the action is not active.
            returns 1 or -1 when the action is active.
            the return value -1 is used under following conditions:
            - for actions marked with (1), when the name does not match
            (for this case ParActionIs may be called between the call to ParName
            for the parameter referred and before the next call to ParName)
            - for actions marked with (2), when the parameter was not used
            (for this case ParActionIs must be called after the parameter definition
            for the parameter referred and before the next call to ParName)
          */

void ParStdDef(void);           /* handle the standard parameters "verbose" and "driver" */
void *ParObject(void);          /* get the current object (to be called by functions
                                   called inside pardef, when the object is not in the argument
                                   list */
void ParLogReady(ReadyState state);     /* set ready state for log */
FILE *ParSaveFile(void);        /* get the save-file name when action is PAR_SAVE */
char *ParGetValueArg();         /* get the set argument for floats and ints. If this function
                                   is called, the caller is reponsible for assigning the value
                                   to the object parameter */

/*
   functions to be used outside pardef:
*/
int ParLog(void *object);       /* log all parameters of this object, but only if not
                                   already logged in the interval, returns >= 0 when done,
                                   or < 0, when not done (execute PAR_LOG action) */
void ParKill(void *object);     /* free the object (execute PAR_KILL action) */
void ParGetFloat(SConnection * con, void *object, char *name,
                 float *value);
                               /* get a float parameter (execute PAR_GET action) */
void *ParCast(ParClass * class, void *object);
                               /* returns object, if the object has type "class" or
                                  inherits type "class", returns NULL otherwise */
void *ParCheck(ParClass * class, void *object);
                               /* dumps core when object has not or does not inherit "class" */
void *ParMakeClass(ParClass * class, ParClass * base);
                               /* initialize "class". base defines its base class or NULL
                                  when ParData is its direct base class */

void *ParMake(SConnection * con, char *name, ParClass * class,
              ParDef pardef, char *creationCmd);
                              /* make an object of class "class", and declare its
                                 pardef function. Add it as command "name" to SICS,
                                 and execute pardef with PAR_INIT action.
                                 When creationCmd is not NULL, it is saved in order to
                                 recreate the object. 

                                 The SICS system will execute:
                                 - PAR_SHOW, PAR_SET, PAR_LIST, PAR_LOGSWITCH
                                 from the corresponding command
                                 - PAR_LOG after a PAR_SET action
                                 - PAR_SAVE after a parameter change on any SICS command
                                 - PAR_KILL on RemoveCommand */

char *ParArg2Str(int argc, char *argv[], char *res, int maxsize);
                              /* convert args to text. if res==NULL, the result is
                                 allocated and maxsize is ignored */

void ParSaveConn(void *object, SConnection * con);
                              /* save connection for further use */

void ParInitPar(void *object, char *name);
                              /* inititalize dynamic parameter */

#endif
