/**
 * @file   SCFCBELM.c
 * @brief  Sample program in C to read and dump FLAM 5 elements from an arbitrary input
 * @author limes datentechnik gmbh
 **********************************************************************/

/* defines code page for literals for IBM mainframes to German*/
#ifdef __ZOS__
#  pragma convert(1141)
#endif

#include<stdio.h>
#include<stdlib.h>

#include"FLCBYT.h"

typedef struct ReadHdl_ {
   void*        fileHdl;
   unsigned int elementsRead;
   size_t       bufferSize;
   char*        buffer;
} ReadHdl;

static ReadHdl*      openInputFile(const char* filename);
static void          closeInputFile(ReadHdl* handle);
static FlmElmRec0*   readNextElement(ReadHdl* handle);
static char*         xmlTypeString(unsigned int type);
static char*         matrixTypeString(unsigned int type);

/**
 * This test program takes a FLUC CONV input string as parameter and dumps
 * the resulting FLAM5 elements to stdout.
 *
 * @param argc must be 2
 * @param argv contains a FLUC CONV input string as the only parameter
 * @return 8=parameter error, 6=open file error, 4=read write error, 2=close error, 0=Success
 */
int main(int argc, char * argv[])
{
   FlmElmRec0*             element;
   ReadHdl*                handle;

/* defines environment variable LANG to German EBCDIC for IBM mainframes*/
#ifdef __ZOS__
   __setenv("LANG","de_DE.IBM-1141",1);
#endif

   if (argc!=2) {
      fprintf(stderr,"Please specify a FLUC input string as the single parameter.\n");
      return 8;
   }

   handle = openInputFile(argv[1]);
   for (element = readNextElement(handle); element != NULL; element = readNextElement(handle)) {
      switch (element->matTyp) {
         case FLMMAT_TYP_XMLELM:
            fprintf(stderr, "Element #%u (MatTyp: %s, ElmTyp: %s, AtrLen:%d DatLen: %d)\n", handle->elementsRead, matrixTypeString(element->matTyp), xmlTypeString(element->elmTyp), element->atrLen, element->datLen);
            break;
         default:
            fprintf(stderr, "Element #%u (MatTyp: %s, ElmTyp: %d, AtrLen:%d DatLen: %d)\n", handle->elementsRead, matrixTypeString(element->matTyp), element->elmTyp, element->atrLen, element->datLen);
      }

   }
   closeInputFile(handle);

   return(0);
}

static ReadHdl* openInputFile(const char* input) {
   ReadHdl* hdl;

   hdl = (ReadHdl*)malloc(sizeof(ReadHdl));
   if (hdl==NULL) {
      fprintf(stderr,"Opening the input file for read failed.\n");
      fprintf(stderr,"Could not allocate handle.\n");
      exit(6);
   }
   hdl->elementsRead = 0;

   hdl->bufferSize = 4096;
   hdl->buffer = (char*)malloc(hdl->bufferSize);
   if (hdl->buffer==NULL) {
      fprintf(stderr,"Opening the input file for read failed.\n");
      fprintf(stderr,"Could not allocate read buffer.\n");
      free(hdl);
      exit(6);
   }

   hdl->fileHdl=fcbopen(input,"format.element()");
   if (hdl->fileHdl==NULL) {
      fprintf(stderr,"Opening the input file for read failed\n");
      fprintf(stderr,"pvRed=fcbopen(\"%s\",\"%s\");\n\n",input,"format.element()");
      fprintf(stderr,"%s\n\n",fcberrms(fcberrno));
      fprintf(stderr,"%s\n\n",fcberrtr());
      free(hdl->buffer);
      free(hdl);
      exit(6);
   }

   return hdl;
}

static void closeInputFile(ReadHdl* handle) {
   int fail = 0;

   if (handle!=NULL) {
      if (handle->fileHdl) {
         if (fcbclose(handle->fileHdl)) {
            fprintf(stderr,"Closing the input file failed\n");
            fprintf(stderr,"fcbclose(handle->fileHdl);\n\n");
            fprintf(stderr,"%s\n\n",fcberrms(fcberrno));
            fprintf(stderr,"%s\n\n",fcberrtr());
            fail = 1;
         }
      }
      if (handle->buffer) {
         free(handle->buffer);
      }
      free(handle);

      if (fail)
         exit(2);
   }
}

static FlmElmRec0* readNextElement(ReadHdl* handle) {
   unsigned int uiLen, uiErr;
   char* tmpBuf;
   FlmElmRec0* element=(FlmElmRec0*)handle->buffer;

   /* read element */
   uiLen = fcbread(handle->buffer, 1, handle->bufferSize, handle->fileHdl);
   if (uiLen==0) {
      /* could not read element */
      uiErr = fcberror(handle->fileHdl);
      if (uiErr==FLMRTC_EOF) {
         /* there are no more elements */
         return NULL;
      } else if (uiErr==FLMRTC_LEN) {
         /* Buffer is too small, realloc and try again
            Note: Even though fcbread() returned 0, it has written the
            metadata fields of the FlmElmRec0 structure to the buffer,
            i.e. we know how much memory to reallocate! */
         tmpBuf = realloc(handle->buffer, element->datLen+sizeof(FlmElmRec0));
         if (tmpBuf==NULL) {
            fprintf(stderr, "Could not reallocate read buffer (old size: %lu, new size: %lu)\n", (unsigned long)handle->bufferSize, (unsigned long)(element->datLen+sizeof(FlmElmRec0)));
            exit(4);
         }
         handle->buffer = tmpBuf;
         element=(FlmElmRec0*)handle->buffer;
         handle->bufferSize = element->datLen+sizeof(FlmElmRec0);

         /* second try */
         uiLen = fcbread(handle->buffer, 1, handle->bufferSize, handle->fileHdl);
         if (uiLen==0) {
            /* Should never happen because FLMRTC_LEN signaled that there is an element */
            fprintf(stderr, "Unexpected error %u when reading an element\n", uiErr);
            fprintf(stderr, "%s\n", fcberrtr());
            exit(4);
         }
      } else {
         /* Some other error occurred */
         fprintf(stderr, "Unexpected error %u\n", uiErr);
         fprintf(stderr, "%s\n",fcberrtr());
         exit(4);
      }
   }

   /* Verify that the element structure is valid */
   if (element->version!=0) {
      fprintf(stderr, "Version (%d) of element structure not supported\n",element->version);
      exit(4);
   }

   /* Increment read elements counter */
   handle->elementsRead++;

   return (FlmElmRec0*)handle->buffer;
}

static char* xmlTypeString(unsigned int type) {
   switch (type) {
     case FLMELM_TYPXML_DATA:       return "DATA";
     case FLMELM_TYPXML_STARTELM:   return "STARTELM";
     case FLMELM_TYPXML_ENDSTRTELM: return "ENDSTRTELM";
     case FLMELM_TYPXML_ENDELM:     return "ENDELM";
     case FLMELM_TYPXML_ATTNAME:    return "ATTNAME";
     case FLMELM_TYPXML_ATTRVAL:    return "ATTRVAL";
     case FLMELM_TYPXML_XML:        return "XML";
     case FLMELM_TYPXML_SKIPENT:    return "SKIPENT";
     case FLMELM_TYPXML_STARTDTD:   return "STARTDTD";
     case FLMELM_TYPXML_ENDDTD:     return "ENDDTD";
     case FLMELM_TYPXML_ELMDECL:    return "ELMDECL";
     case FLMELM_TYPXML_ATTLDECL:   return "ATTLDECL";
     case FLMELM_TYPXML_INTENTDECL: return "INTENTDECL";
     case FLMELM_TYPXML_SYSENTDECL: return "SYSENTDECL";
     case FLMELM_TYPXML_PUBENTDECL: return "PUBENTDECL";
     case FLMELM_TYPXML_NOTDECLS:   return "NOTDECLS";
     case FLMELM_TYPXML_NOTDECLP:   return "NOTDECLP";
     case FLMELM_TYPXML_NOTDECLPS:  return "NOTDECLPS";
     case FLMELM_TYPXML_PROCINST:   return "PROCINST";
     case FLMELM_TYPXML_STARTCD:    return "STARTCD";
     case FLMELM_TYPXML_ENDCD:      return "ENDCD";
     case FLMELM_TYPXML_COMMENT:    return "COMMENT";
     case FLMELM_TYPXML_DEFAULT:    return "DEFAULT";
   }
   return "Unknown";
}

static char* matrixTypeString(unsigned int type) {
   switch(type) {
      case FLMMAT_TYP_DATBLK:    return "DATBLK";
      case FLMMAT_TYP_STDREC:    return "STDREC";
      case FLMMAT_TYP_STDASAREC: return "STDASAREC";
      case FLMMAT_TYP_STDMCCREC: return "STDMCCREC";
      case FLMMAT_TYP_RELREC:    return "RELREC";
      case FLMMAT_TYP_RELASAREC: return "RELASAREC";
      case FLMMAT_TYP_RELMCCREC: return "RELMCCREC";
      case FLMMAT_TYP_TXTREC:    return "TXTREC";
      case FLMMAT_TYP_TXTDLM:    return "TXTDLM";
      case FLMMAT_TYP_TXTRST:    return "TXTRST";
      case FLMMAT_TYP_XMLELM:    return "XMLELM";
   }
   return "Unknown";
}

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

