/***************************************************************************
    begin       : Mon Mar 01 2004
    copyright   : (C) 2004-2010 by Martin Preuss
    email       : martin@libchipcard.de

 ***************************************************************************
 *          Please see toplevel file COPYING for license details           *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif



#include "args.h"
#include "typemaker_p.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/logger.h>
#include <gwenhywfar/xml.h>
#include <gwenhywfar/syncio_file.h>

#include <stdlib.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>




int write_ha_file_c(ARGUMENTS *args, GWEN_XMLNODE *node)
{
  int rv;
  const char *f;
  GWEN_BUFFER *fname;
  GWEN_BUFFER *hbuf;
  const char *s;
  const char *nacc;
  const char *constAcc;
  const char *constName;
  const char *fromDbAcc;
  const char *fromDbName;
  const char *dupAcc;
  const char *dupName;
  GWEN_SYNCIO *sio;
  int err;
  GWEN_XMLNODE *n;
  const char *id;
  const char *prefix;

  id=get_struct_property(node, "id", 0);
  if (!id) {
    DBG_ERROR(0, "No id for struct");
    return -1;
  }

  prefix=get_struct_property(node, "prefix", 0);
  if (!prefix) {
    DBG_ERROR(0, "No prefix for struct");
    return -1;
  }

  f=get_struct_property(node, "filename", 0);
  if (!f) {
    DBG_ERROR(0, "No filename given");
    return -1;
  }

  nacc=get_struct_property(node, "access", "public");
  constAcc=get_struct_property(node, "constructor-access", nacc);
  constName=get_struct_property(node, "constructor-name", 0);
  fromDbAcc=get_struct_property(node, "fromdb-access", nacc);
  fromDbName=get_struct_property(node, "fromdb-name", 0);
  dupAcc=get_struct_property(node, "dup-access", nacc);
  dupName=get_struct_property(node, "fromdb-name", 0);

  fname=GWEN_Buffer_new(0, 256, 0, 1);
  GWEN_Buffer_AppendString(fname, f);
  GWEN_Buffer_AppendString(fname, ".h");

  sio=GWEN_SyncIo_File_new(GWEN_Buffer_GetStart(fname),
                           GWEN_SyncIo_File_CreationMode_CreateAlways);
  GWEN_SyncIo_AddFlags(sio,
                       GWEN_SYNCIO_FILE_FLAGS_READ |
                       GWEN_SYNCIO_FILE_FLAGS_WRITE |
                       GWEN_SYNCIO_FILE_FLAGS_UREAD |
                       GWEN_SYNCIO_FILE_FLAGS_UWRITE |
                       GWEN_SYNCIO_FILE_FLAGS_GREAD |
                       GWEN_SYNCIO_FILE_FLAGS_GWRITE);
  rv=GWEN_SyncIo_Connect(sio);
  if (rv<0) {
    DBG_ERROR(0, "open(%s): %s",
              GWEN_Buffer_GetStart(fname),
              strerror(errno));
    GWEN_Buffer_free(fname);
    GWEN_SyncIo_free(sio);
    return -1;
  }
  GWEN_Buffer_free(fname);

  /* Insert the auto-generation warning */
  GWEN_SyncIo_WriteString(sio, "/* This file is auto-generated from \"");
  GWEN_SyncIo_WriteString(sio, f);
  GWEN_SyncIo_WriteLine(sio, ".xml\" by the typemaker");
  GWEN_SyncIo_WriteLine(sio, "   tool of Gwenhywfar. ");
  GWEN_SyncIo_WriteLine(sio, "   Do not edit this file -- all changes will be lost! */");

  hbuf=GWEN_Buffer_new(0, 256, 0, 1);
  s=f;
  while (*s) {
    GWEN_Buffer_AppendByte(hbuf, toupper(*s));
    s++;
  }
  GWEN_Buffer_AppendString(hbuf, "_H");

  GWEN_SyncIo_WriteString(sio, "#ifndef ");
  GWEN_SyncIo_WriteLine(sio, GWEN_Buffer_GetStart(hbuf));
  GWEN_SyncIo_WriteString(sio, "#define ");
  GWEN_SyncIo_WriteLine(sio, GWEN_Buffer_GetStart(hbuf));
  GWEN_SyncIo_WriteLine(sio, "");

  rv=write_apidoc_c(args, node, sio, "public");
  if (rv) {
    DBG_INFO(0, "here (%d)", rv);
    return rv;
  }

  if (strcasecmp(nacc, "public")==0) {
    GWEN_SyncIo_WriteLine(sio, "#ifdef __cplusplus");
    GWEN_SyncIo_WriteLine(sio, "extern \"C\" {");
    GWEN_SyncIo_WriteLine(sio, "#endif");
    GWEN_SyncIo_WriteLine(sio, "");
    GWEN_SyncIo_WriteString(sio, "typedef struct ");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteString(sio, " ");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, ";");
    GWEN_SyncIo_WriteLine(sio, "");
    GWEN_SyncIo_WriteLine(sio, "#ifdef __cplusplus");
    GWEN_SyncIo_WriteLine(sio, "} /* __cplusplus */");
    GWEN_SyncIo_WriteLine(sio, "#endif");
    GWEN_SyncIo_WriteLine(sio, "");
  }

  GWEN_SyncIo_WriteLine(sio, "#include <gwenhywfar/db.h>");

  if (strcasecmp(get_struct_property(node, "inherit", ""),
                 "public")==0) {
    GWEN_SyncIo_WriteLine(sio, "#include <gwenhywfar/inherit.h>");
  }

  if (strcasecmp(get_struct_property(node, "list", ""),
                 "public")==0) {
    GWEN_SyncIo_WriteLine(sio, "#include <gwenhywfar/misc.h>");
  }

  if (strcasecmp(get_struct_property(node, "list2", ""),
                 "public")==0) {
    GWEN_SyncIo_WriteLine(sio, "#include <gwenhywfar/list2.h>");
  }

  n=GWEN_XMLNode_FindFirstTag(node, "pre-headers", 0, 0);
  if (n) {
    GWEN_XMLNODE *nn;

    GWEN_SyncIo_WriteLine(sio, "/* pre-headers */");
    nn=GWEN_XMLNode_GetFirstTag(n);
    while (nn) {
      rv=write_hp_group_c(args, nn, sio);
      if (rv) {
        GWEN_Buffer_free(hbuf);
        return -1;
      }
      nn=GWEN_XMLNode_GetNextTag(nn);
    } /* while */
    GWEN_SyncIo_WriteLine(sio, "");
  }

  n=GWEN_XMLNode_FindFirstTag(node, "headers", 0, 0);
  if (n) {
    GWEN_XMLNODE *nn;

    GWEN_SyncIo_WriteLine(sio, "/* headers */");
    nn=GWEN_XMLNode_GetFirstTag(n);
    while (nn) {
      rv=write_hp_group_c(args, nn, sio);
      if (rv) {
        GWEN_Buffer_free(hbuf);
        DBG_INFO(0, "here (%d)", rv);
        return -1;
      }
      nn=GWEN_XMLNode_GetNextTag(nn);
    } /* while */
    GWEN_SyncIo_WriteLine(sio, "");
  }

  GWEN_SyncIo_WriteLine(sio, "#ifdef __cplusplus");
  GWEN_SyncIo_WriteLine(sio, "extern \"C\" {");
  GWEN_SyncIo_WriteLine(sio, "#endif");
  GWEN_SyncIo_WriteLine(sio, "");

  if (write_h_enums(args, node, sio, "public")) {
    DBG_ERROR(0, "Error writing enum types");
    return -1;
  }

  if (write_h_funcs(args, node, sio, "public")) {
    DBG_ERROR(0, "Error writing function types");
    return -1;
  }

  if (strcasecmp(get_struct_property(node, "inherit", ""),
                 "public")==0) {
    if (args->domain)
      GWEN_SyncIo_WriteString(sio, "GWEN_INHERIT_FUNCTION_LIB_DEFS(");
    else
      GWEN_SyncIo_WriteString(sio, "GWEN_INHERIT_FUNCTION_DEFS(");
    GWEN_SyncIo_WriteString(sio, id);
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, ", ");
      GWEN_SyncIo_WriteString(sio, args->domain);
    }
    GWEN_SyncIo_WriteLine(sio, ")");
  }

  if (strcasecmp(get_struct_property(node, "list", ""),
                 "public")==0) {
    if (args->domain)
      GWEN_SyncIo_WriteString(sio, "GWEN_LIST_FUNCTION_LIB_DEFS(");
    else
      GWEN_SyncIo_WriteString(sio, "GWEN_LIST_FUNCTION_DEFS(");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteString(sio, ", ");
    GWEN_SyncIo_WriteString(sio, prefix);
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, ", ");
      GWEN_SyncIo_WriteString(sio, args->domain);
    }
    GWEN_SyncIo_WriteLine(sio, ")");

    if (dupAcc && strcasecmp(dupAcc, "none")!=0) {
      if (args->domain) {
        GWEN_SyncIo_WriteString(sio, args->domain);
        GWEN_SyncIo_WriteString(sio, " ");
      }
      GWEN_SyncIo_WriteString(sio, id);
      GWEN_SyncIo_WriteString(sio, "_LIST *");
      GWEN_SyncIo_WriteString(sio, prefix);
      GWEN_SyncIo_WriteString(sio, "_List_dup(const ");
      GWEN_SyncIo_WriteString(sio, id);
      GWEN_SyncIo_WriteLine(sio, "_LIST *stl);");
      GWEN_SyncIo_WriteLine(sio, "");
    }
  }

  if (strcasecmp(get_struct_property(node, "list2", ""),
                 "public")==0) {
    if (args->domain)
      GWEN_SyncIo_WriteString(sio, "GWEN_LIST2_FUNCTION_LIB_DEFS(");
    else
      GWEN_SyncIo_WriteString(sio, "GWEN_LIST2_FUNCTION_DEFS(");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteString(sio, ", ");
    GWEN_SyncIo_WriteString(sio, prefix);
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, ", ");
      GWEN_SyncIo_WriteString(sio, args->domain);
    }
    GWEN_SyncIo_WriteLine(sio, ")");
    GWEN_SyncIo_WriteLine(sio, "");

    GWEN_SyncIo_WriteLine(sio,
                          "/** Destroys all objects stored in the given "
                          "LIST2 and the list itself");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, "void ");
    GWEN_SyncIo_WriteString(sio, prefix);
    GWEN_SyncIo_WriteString(sio, "_List2_freeAll(");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, "_LIST2 *stl);");

    GWEN_SyncIo_WriteLine(sio, "");
  }

  n=GWEN_XMLNode_FindFirstTag(node, "post-headers", 0, 0);
  if (n) {
    GWEN_XMLNODE *nn;

    GWEN_SyncIo_WriteLine(sio, "/* post-headers */");
    nn=GWEN_XMLNode_GetFirstTag(n);
    while (nn) {
      rv=write_hp_group_c(args, nn, sio);
      if (rv) {
        GWEN_Buffer_free(hbuf);
        DBG_INFO(0, "here (%d)", rv);
        return -1;
      }
      nn=GWEN_XMLNode_GetNextTag(nn);
    } /* while */
    GWEN_SyncIo_WriteLine(sio, "");
  }


  if (strcasecmp(constAcc, "public")==0) {
    GWEN_SyncIo_WriteLine(sio,
                          "/** Creates a new object.");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteString(sio, " *");
    GWEN_SyncIo_WriteString(sio, prefix);
    if (constName)
      GWEN_SyncIo_WriteString(sio, constName);
    else
      GWEN_SyncIo_WriteString(sio, "_new");
    GWEN_SyncIo_WriteLine(sio, "(void);");
  }

  /* FromDb */
  if (strcasecmp(fromDbAcc, "public")==0) {
    GWEN_SyncIo_WriteLine(sio,
                          "/** Creates an object from the data in the"
                          " given GWEN_DB_NODE");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteString(sio, " *");
    GWEN_SyncIo_WriteString(sio, prefix);
    if (fromDbName)
      GWEN_SyncIo_WriteString(sio, fromDbName);
    else
      GWEN_SyncIo_WriteString(sio, "_fromDb");
    GWEN_SyncIo_WriteLine(sio, "(GWEN_DB_NODE *db);");
  }

  /* dup */
  if (strcasecmp(dupAcc, "public")==0) {
    GWEN_SyncIo_WriteLine(sio,
                          "/** Creates and returns a deep copy of the"
                          "given object.");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteString(sio, " *");
    GWEN_SyncIo_WriteString(sio, prefix);
    if (dupName)
      GWEN_SyncIo_WriteString(sio, dupName);
    else
      GWEN_SyncIo_WriteString(sio, "_dup");
    GWEN_SyncIo_WriteString(sio, "(const ");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, "*st);");
  }

  if (strcasecmp(nacc, "public")==0) {
    GWEN_SyncIo_WriteLine(sio,
                          "/** Destroys the given object.");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, "void ");
    GWEN_SyncIo_WriteString(sio, prefix);
    GWEN_SyncIo_WriteString(sio, "_free(");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, " *st);");

    GWEN_SyncIo_WriteLine(sio,
                          "/** Increments the usage counter of the "
                          "given object, so an additional free() is"
                          " needed to destroy the object.");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, "void ");
    GWEN_SyncIo_WriteString(sio, prefix);
    GWEN_SyncIo_WriteString(sio, "_Attach(");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, " *st);");

    /* ReadDb */
    GWEN_SyncIo_WriteLine(sio, "/** Reads data from a GWEN_DB.");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, "int ");
    GWEN_SyncIo_WriteString(sio, prefix);
    GWEN_SyncIo_WriteString(sio, "_ReadDb(");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, " *st, GWEN_DB_NODE *db);");

    /* ToDb */
    GWEN_SyncIo_WriteLine(sio,
                          "/** Stores an object in the"
                          " given GWEN_DB_NODE");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, "int ");
    GWEN_SyncIo_WriteString(sio, prefix);
    GWEN_SyncIo_WriteString(sio, "_toDb(const ");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, "*st, GWEN_DB_NODE *db);");

    GWEN_SyncIo_WriteLine(sio,
                          "/** Returns 0 if this object has not been"
                          " modified, !=0 otherwise");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, "int ");
    GWEN_SyncIo_WriteString(sio, prefix);
    GWEN_SyncIo_WriteString(sio, "_IsModified(const ");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, " *st);");

    GWEN_SyncIo_WriteLine(sio,
                          "/** Sets the modified state of the given "
                          "object");
    GWEN_SyncIo_WriteLine(sio, "*/");
    if (args->domain) {
      GWEN_SyncIo_WriteString(sio, args->domain);
      GWEN_SyncIo_WriteString(sio, " ");
    }
    GWEN_SyncIo_WriteString(sio, "void ");
    GWEN_SyncIo_WriteString(sio, prefix);
    GWEN_SyncIo_WriteString(sio, "_SetModified(");
    GWEN_SyncIo_WriteString(sio, id);
    GWEN_SyncIo_WriteLine(sio, " *st, int i);");
  }

  rv=write_h_setget_c(args, node, sio, "public");
  if (rv) {
    GWEN_Buffer_free(hbuf);
    GWEN_SyncIo_Disconnect(sio);
    GWEN_SyncIo_free(sio);
    DBG_INFO(0, "here (%d)", rv);
    return rv;
  }

  err=GWEN_SyncIo_WriteLine(sio, "");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }

  err=GWEN_SyncIo_WriteLine(sio, "#ifdef __cplusplus");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }
  err=GWEN_SyncIo_WriteLine(sio, "} /* __cplusplus */");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }
  err=GWEN_SyncIo_WriteLine(sio, "#endif");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }
  err=GWEN_SyncIo_WriteLine(sio, "");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }

  /* write trailing endif */
  err=GWEN_SyncIo_WriteLine(sio, "");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }

  err=GWEN_SyncIo_WriteString(sio, "#endif /* ");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }
  err=GWEN_SyncIo_WriteString(sio, GWEN_Buffer_GetStart(hbuf));
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }
  err=GWEN_SyncIo_WriteLine(sio, " */");
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_Buffer_free(hbuf);
    return -1;
  }

  err=GWEN_SyncIo_Disconnect(sio);
  if (err) {
    DBG_ERROR_ERR(0, err);
    GWEN_SyncIo_free(sio);
    GWEN_Buffer_free(hbuf);
    return -1;
  }

  GWEN_Buffer_free(hbuf);
  return 0;
}



int write_ha_files_c(ARGUMENTS *args, GWEN_XMLNODE *node)
{
  GWEN_XMLNODE *n;
  int rv;

  n=GWEN_XMLNode_FindFirstTag(node, "type", 0, 0);
  while (n) {
    const char *nacc;

    nacc=get_struct_property(n, "access", "public");
    if (strcasecmp(nacc, "public")==0) {
      rv=write_ha_file_c(args, n);
      if (rv) {
        DBG_INFO(0, "here (%d)", rv);
        return rv;
      }
    }
    n=GWEN_XMLNode_FindNextTag(n, "type", 0, 0);
  }
  return 0;
}








