/***************************************************************************
    begin       : Thu Jul 02 2009
    copyright   : (C) 2018 by Martin Preuss
    email       : martin@libchipcard.de

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

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


#include "tm2c_list1.h"
#include "tm2c_misc.h"

#include <gwenhywfar/debug.h>

#include <ctype.h>


static void _addGetByMemberProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm);
static int _addGetByMemberImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm);
static void _addGetByMemberDeclaration(TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm, GWEN_BUFFER *tbuf);

static void _addListDupProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty);
static int _addListDupImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty);
static void _addListDupDeclaration(TYPEMAKER2_TYPE *ty, GWEN_BUFFER *tbuf);

static void _addSortByMemberProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm);
static void _addSortByMemberImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm);
static void _addSortByMemberDeclaration(TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm, GWEN_BUFFER *tbuf);

static void _addCompareMemberProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm);
static int _addCompareMemberImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm);
static void _addCompareMemberDeclaration(TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm, GWEN_BUFFER *tbuf);





int TM2C_BuildList1GetByMember(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  int rv;

  _addGetByMemberProtoType(tb, ty, tm);
  rv=_addGetByMemberImplementation(tb, ty, tm);
  if (rv<0) {
    DBG_INFO(NULL, "here (%d)", rv);
    return rv;
  }

  return 0;
}



int TM2C_BuildList1Dup(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty)
{
  int rv;

  _addListDupProtoType(tb, ty);
  rv=_addListDupImplementation(tb, ty);
  if (rv<0) {
    DBG_INFO(NULL, "here (%d)", rv);
    return rv;
  }

  return 0;
}



int TM2C_BuildList1SortByMember(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  int rv;

  _addSortByMemberProtoType(tb, ty, tm);
  _addCompareMemberProtoType(tb, ty, tm);

  _addSortByMemberImplementation(tb, ty, tm);
  rv=_addCompareMemberImplementation(tb, ty, tm);
  if (rv<0) {
    DBG_INFO(NULL, "here (%d)", rv);
    return rv;
  }

  return 0;
}






void _addGetByMemberProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  GWEN_BUFFER *tbuf;
  const char *s;
  TYPEMAKER2_TYPEMANAGER *tym;
  TYPEMAKER2_TYPE *mty;

  tym=Typemaker2_Builder_GetTypeManager(tb);
  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  s=Typemaker2_TypeManager_GetApiDeclaration(tym);
  if (s)
    GWEN_Buffer_AppendArgs(tbuf, "%s ", s);
  _addGetByMemberDeclaration(ty, tm, tbuf);
  GWEN_Buffer_AppendString(tbuf, ";\n");

  Typemaker2_Builder_AddPublicDeclaration(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);
}



int _addGetByMemberImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  GWEN_BUFFER *tbuf;
  TYPEMAKER2_TYPE *mty;
  const char *sTypeId;
  const char *sTypePrefix;
  const char *sMemberName;

  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  sTypeId=Typemaker2_Type_GetIdentifier(ty);
  sTypePrefix=Typemaker2_Type_GetPrefix(ty);
  sMemberName=Typemaker2_Member_GetName(tm);

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  _addGetByMemberDeclaration(ty, tm, tbuf);
  GWEN_Buffer_AppendString(tbuf, "{\n");

  GWEN_Buffer_AppendArgs(tbuf,   "  %s *p_struct;\n\n", sTypeId);
  GWEN_Buffer_AppendString(tbuf, "  assert(p_list);\n");
  GWEN_Buffer_AppendArgs(tbuf,   "  p_struct = %s_List_First(p_list);\n", sTypePrefix);
  GWEN_Buffer_AppendString(tbuf, "  while(p_struct) {\n");
  GWEN_Buffer_AppendString(tbuf, "    int p_rv;\n");
  GWEN_Buffer_AppendString(tbuf, "\n");

  GWEN_Buffer_AppendString(tbuf, "    ");
  if (1) {
    GWEN_BUFFER *dstbuf;
    GWEN_BUFFER *srcbuf;
    int rv;

    srcbuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendString(srcbuf, "p_cmp");

    dstbuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendArgs(dstbuf, "p_struct->%s", sMemberName);

    rv=Typemaker2_Builder_Invoke_CompareFn(tb, ty, tm,
                                           GWEN_Buffer_GetStart(srcbuf),
                                           GWEN_Buffer_GetStart(dstbuf),
                                           tbuf);
    GWEN_Buffer_free(srcbuf);
    GWEN_Buffer_free(dstbuf);
    if (rv<0) {
      DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
      GWEN_Buffer_free(tbuf);
      return rv;
    }
    GWEN_Buffer_AppendString(tbuf, "\n");
  }


  GWEN_Buffer_AppendString(tbuf, "    if (p_rv == 0)\n");
  GWEN_Buffer_AppendString(tbuf, "      return p_struct;\n");
  GWEN_Buffer_AppendArgs(tbuf,   "    p_struct = %s_List_Next(p_struct);\n", sTypePrefix);
  GWEN_Buffer_AppendString(tbuf, "  }\n");

  GWEN_Buffer_AppendString(tbuf, "  return NULL;\n");
  GWEN_Buffer_AppendString(tbuf, "}\n");

  Typemaker2_Builder_AddCode(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);

  return 0;
}



void _addGetByMemberDeclaration(TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm, GWEN_BUFFER *tbuf)
{
  TYPEMAKER2_TYPE *mty;
  const char *sTypeId;
  const char *sTypePrefix;
  const char *sMemberName;
  const char *sMemberTypeId;

  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  sTypeId=Typemaker2_Type_GetIdentifier(ty);
  sTypePrefix=Typemaker2_Type_GetPrefix(ty);
  sMemberName=Typemaker2_Member_GetName(tm);
  sMemberTypeId=Typemaker2_Type_GetIdentifier(mty);

  GWEN_Buffer_AppendArgs(tbuf,
                         "%s *%s_List_GetBy%c%s(const %s_LIST *p_list, ",
                         sTypeId,
                         sTypePrefix,
                         toupper(*sMemberName),
                         sMemberName+1,
                         sTypeId);
  if (Typemaker2_Type_GetType(mty)==TypeMaker2_Type_Pointer ||
      Typemaker2_Type_GetType(mty)==TypeMaker2_Type_Array)
    GWEN_Buffer_AppendArgs(tbuf, " const %s *", sMemberTypeId);
  else
    GWEN_Buffer_AppendArgs(tbuf, "%s ", sMemberTypeId);
  GWEN_Buffer_AppendString(tbuf, "p_cmp)");
}



void _addListDupDeclaration(TYPEMAKER2_TYPE *ty, GWEN_BUFFER *tbuf)
{
  const char *sTypeId;
  const char *sTypePrefix;

  sTypeId=Typemaker2_Type_GetIdentifier(ty);
  sTypePrefix=Typemaker2_Type_GetPrefix(ty);

  GWEN_Buffer_AppendArgs(tbuf,
                         "%s_LIST *%s_List_dup(const %s_LIST *p_src)",
                         sTypeId,
                         sTypePrefix,
                         sTypeId);
}






void _addListDupProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty)
{
  GWEN_BUFFER *tbuf;
  const char *s;
  TYPEMAKER2_TYPEMANAGER *tym;

  tym=Typemaker2_Builder_GetTypeManager(tb);

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  s=Typemaker2_TypeManager_GetApiDeclaration(tym);
  if (s)
    GWEN_Buffer_AppendArgs(tbuf, "%s ", s);
  _addListDupDeclaration(ty, tbuf);
  GWEN_Buffer_AppendString(tbuf, ";\n");

  Typemaker2_Builder_AddPublicDeclaration(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);
}



int _addListDupImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty)
{
  GWEN_BUFFER *tbuf;
  const char *sTypeId;
  const char *sTypePrefix;

  sTypeId=Typemaker2_Type_GetIdentifier(ty);
  sTypePrefix=Typemaker2_Type_GetPrefix(ty);

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  _addListDupDeclaration(ty, tbuf);
  GWEN_Buffer_AppendString(tbuf, " {\n");

  GWEN_Buffer_AppendArgs(tbuf,   "  %s_LIST *p_dest;\n", sTypeId);
  GWEN_Buffer_AppendArgs(tbuf,   "  %s *p_elem;\n", sTypeId);
  GWEN_Buffer_AppendString(tbuf, "\n");
  GWEN_Buffer_AppendString(tbuf, "  assert(p_src);\n");
  GWEN_Buffer_AppendArgs(tbuf,   "  p_dest=%s_List_new();\n", sTypePrefix);
  GWEN_Buffer_AppendArgs(tbuf,   "  p_elem=%s_List_First(p_src);\n", sTypePrefix);
  GWEN_Buffer_AppendString(tbuf, "  while(p_elem) {\n");
  GWEN_Buffer_AppendArgs(tbuf,   "    %s *p_cpy;\n\n", sTypeId);
  GWEN_Buffer_AppendArgs(tbuf,   "    p_cpy=%s_dup(p_elem);\n", sTypePrefix);
  GWEN_Buffer_AppendArgs(tbuf,   "    %s_List_Add(p_cpy, p_dest);\n", sTypePrefix);
  GWEN_Buffer_AppendArgs(tbuf,   "    p_elem=%s_List_Next(p_elem);\n", sTypePrefix);
  GWEN_Buffer_AppendString(tbuf, "  }\n");
  GWEN_Buffer_AppendString(tbuf, "\n");

  GWEN_Buffer_AppendString(tbuf, "  return p_dest;\n");
  GWEN_Buffer_AppendString(tbuf, "}\n");

  Typemaker2_Builder_AddCode(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);

  return 0;
}







void _addSortByMemberProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  GWEN_BUFFER *tbuf;
  const char *s;
  TYPEMAKER2_TYPEMANAGER *tym;
  TYPEMAKER2_TYPE *mty;

  tym=Typemaker2_Builder_GetTypeManager(tb);
  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  s=Typemaker2_TypeManager_GetApiDeclaration(tym);
  if (s)
    GWEN_Buffer_AppendArgs(tbuf, "%s ", s);
  _addSortByMemberDeclaration(ty, tm, tbuf);
  GWEN_Buffer_AppendString(tbuf, ";\n");

  Typemaker2_Builder_AddPublicDeclaration(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);
}



void _addSortByMemberImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  GWEN_BUFFER *tbuf;
  TYPEMAKER2_TYPE *mty;
  const char *sTypeId;
  const char *sTypePrefix;
  const char *sMemberName;

  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  sTypeId=Typemaker2_Type_GetIdentifier(ty);
  sTypePrefix=Typemaker2_Type_GetPrefix(ty);
  sMemberName=Typemaker2_Member_GetName(tm);

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  _addSortByMemberDeclaration(ty, tm, tbuf);
  GWEN_Buffer_AppendString(tbuf, "{\n");

  GWEN_Buffer_AppendArgs(tbuf,   "  %s_LIST_SORT_FN oldSortFn;\n", sTypeId);
  GWEN_Buffer_AppendString(tbuf, "\n");
  GWEN_Buffer_AppendArgs(tbuf,   "  oldSortFn=%s_List_SetSortFn(p_list, %s_List_Compare_%c%s);\n",
                         sTypePrefix, sTypePrefix, toupper(*sMemberName), sMemberName+1);
  GWEN_Buffer_AppendArgs(tbuf,   "  %s_List_Sort(p_list, p_ascending);\n", sTypePrefix);
  GWEN_Buffer_AppendArgs(tbuf,   "  %s_List_SetSortFn(p_list, oldSortFn);\n", sTypePrefix);
  GWEN_Buffer_AppendString(tbuf, "}\n");

  Typemaker2_Builder_AddCode(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);
}



void _addSortByMemberDeclaration(TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm, GWEN_BUFFER *tbuf)
{
  TYPEMAKER2_TYPE *mty;
  const char *sTypeId;
  const char *sTypePrefix;
  const char *sMemberName;

  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  sTypeId=Typemaker2_Type_GetIdentifier(ty);
  sTypePrefix=Typemaker2_Type_GetPrefix(ty);
  sMemberName=Typemaker2_Member_GetName(tm);

  GWEN_Buffer_AppendArgs(tbuf,
                         "void %s_List_SortBy%c%s(%s_LIST *p_list, int p_ascending)",
                         sTypePrefix,
                         toupper(*sMemberName),
                         sMemberName+1,
                         sTypeId);
}



void _addCompareMemberProtoType(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  GWEN_BUFFER *tbuf;

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  GWEN_Buffer_AppendString(tbuf, "static ");
  _addCompareMemberDeclaration(ty, tm, tbuf);
  GWEN_Buffer_AppendString(tbuf, ";\n");

  Typemaker2_Builder_AddPrivateDeclaration(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);
}



int _addCompareMemberImplementation(TYPEMAKER2_BUILDER *tb, TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm)
{
  GWEN_BUFFER *tbuf;
  TYPEMAKER2_TYPE *mty;
  const char *sMemberName;

  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  sMemberName=Typemaker2_Member_GetName(tm);

  tbuf=GWEN_Buffer_new(0, 256, 0, 1);

  _addCompareMemberDeclaration(ty, tm, tbuf);
  GWEN_Buffer_AppendString(tbuf, "{\n");
  GWEN_Buffer_AppendString(tbuf, "  int p_rv;\n");
  GWEN_Buffer_AppendString(tbuf, "\n");

  if (1) {
    GWEN_BUFFER *dstbuf;
    GWEN_BUFFER *srcbuf;
    int rv;

    srcbuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendArgs(srcbuf, "p_a->%s", sMemberName);

    dstbuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_Buffer_AppendArgs(dstbuf, "p_b->%s", sMemberName);

    rv=Typemaker2_Builder_Invoke_CompareFn(tb, ty, tm,
                                           GWEN_Buffer_GetStart(srcbuf),
                                           GWEN_Buffer_GetStart(dstbuf),
                                           tbuf);
    GWEN_Buffer_free(srcbuf);
    GWEN_Buffer_free(dstbuf);
    if (rv<0) {
      DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
      GWEN_Buffer_free(tbuf);
      return rv;
    }
    GWEN_Buffer_AppendString(tbuf, "\n");
  }

  GWEN_Buffer_AppendString(tbuf, "  if (p_ascending)\n");
  GWEN_Buffer_AppendString(tbuf, "    return p_rv;\n");
  GWEN_Buffer_AppendString(tbuf, "  else\n");
  GWEN_Buffer_AppendString(tbuf, "    return -p_rv;\n");
  GWEN_Buffer_AppendString(tbuf, "}\n");

  Typemaker2_Builder_AddCode(tb, GWEN_Buffer_GetStart(tbuf));
  GWEN_Buffer_free(tbuf);

  return 0;
}



void _addCompareMemberDeclaration(TYPEMAKER2_TYPE *ty, TYPEMAKER2_MEMBER *tm, GWEN_BUFFER *tbuf)
{
  TYPEMAKER2_TYPE *mty;
  const char *sTypeId;
  const char *sTypePrefix;
  const char *sMemberName;

  mty=Typemaker2_Member_GetTypePtr(tm);
  assert(mty);

  sTypeId=Typemaker2_Type_GetIdentifier(ty);
  sTypePrefix=Typemaker2_Type_GetPrefix(ty);
  sMemberName=Typemaker2_Member_GetName(tm);

  GWEN_Buffer_AppendArgs(tbuf,
                         "int GWENHYWFAR_CB %s_List_Compare_%c%s(const %s *p_a, const %s *p_b, int p_ascending)",
                         sTypePrefix,
                         toupper(*sMemberName),
                         sMemberName+1,
                         sTypeId,
                         sTypeId);
}


