/****************************************************************************************************************
 *
 *  Copyright (c) 1992 by Antoine Dumesnil de Maricourt. All rights reserved.
 *
 *  This program is distributed in the hope that it will be useful.
 *  Use and copying of this software and preparation of derivative works
 *  based upon this software are permitted, so long as the following
 *  conditions are met:
 *       o credit to the authors is acknowledged following current
 *         academic behaviour
 *       o no fees or compensation are charged for use, copies, or
 *         access to this software
 *       o this copyright notice is included intact.
 *  This software is made available AS IS, and no warranty is made about 
 *  the software or its performance. 
 * 
 *  Bug descriptions, use reports, comments or suggestions are welcome.
 *  Send them to    dumesnil@etca.fr   or to:
 *       
 *       Antoine de Maricourt
 *       ETCA CREA-SP
 *       16 bis, avenue Prieur de la Cote d'Or
 *       94114 Arcueil Cedex
 *       France
 */
/* Modified by Randy Hootman (rph@netcom.com) to run under Linux */

#include "sg.h"

#ifndef _GNU_SOURCE
static void bcopy (b1, b2, length)
char *b1;
char *b2;
int length;
{
  if (length > 0)
    if ((long) (b2 - b1) < 0)
      while (length-- > 0)
	*b2++ = *b1++;

    else {
      b2 += length;
      b1 += length;

      while (length-- > 0) 
	*--b2 = *--b1;
    }
}

static void bzero (b, length)
char *b;
int length;
{
  if (length > 0)
    while (length-- > 0)
      *b++ = '\0';
}
#endif

/*******************************************************************************************************
 *
 *    Recopie la chaine <src> a la suite de la chaine <*dst>. La memoire est allouee par malloc et les
 *    deux chaines sont supposees etre terminees par le caractere <end>.
 */

#ifdef _GNU_SOURCE
void SG_strcat (char **dst, char *src, char end)
#else
void SG_strcat (dst, src, end)
char **dst;
char *src;
char end;
#endif
{
  char *ptr;
  int   src_size = 0;
  int   dst_size = 0;

  if ( src != NULL) { ptr =  src; while (*ptr++ != end) src_size++; }
  if (*dst != NULL) { ptr = *dst; while (*ptr++ != end) dst_size++; }

  if (src_size > 0) {
    ptr = (char*) malloc ((unsigned) src_size + dst_size + 1);

    (void) bcopy (*dst, ptr           , dst_size    );
    (void) bcopy ( src, ptr + dst_size, src_size + 1);

    (void) free (*dst);
    *dst = ptr;
  }
}

/*******************************************************************************************************
 *
 *    Recopie la chaine <src> a la place de la chaine *<dst>. Les deux chaines sont terminees par
 *    le caractere <end>.
 */

#ifdef _GNU_SOURCE
void SG_strcpy (char **dst, char *src, char end)
#else
void SG_strcpy (dst, src, end)
char **dst;
char *src;
char end;
#endif
{
  char *ptr;
  int   src_size = 0;
  int   dst_size = 0;

  if ( src != NULL) { ptr =  src; while (*ptr++ != end) src_size++; }
  if (*dst != NULL) { ptr = *dst; while (*ptr++ != end) dst_size++; }

  if (src_size == dst_size)
    (void) bcopy (src, *dst, src_size);

  else {
    (void) free (*dst);
    *dst = (char*) malloc ((unsigned) src_size + 1);
    (void) bcopy (src, *dst, src_size + 1);
  }
}

/*******************************************************************************************************
 *
 *   Supprime le noeud <node> de l'arbre. Tous les pointeurs (up, down, right et left) sont remis
 *   a NULL pour ce noeud. La valeur retournee est le noeud lui-meme : il est necessaire d'appeler
 *   SG_FreeNode pour recuperer la place memoire associee et liberer les proprietes.
 */

#ifdef _GNU_SOURCE
SG_NodePtr SG_RemoveNode (SG_NodePtr node)
#else
SG_NodePrt SG_RemoveNode (node)
SG_NodePtr node;
#endif
{
  if (node->up != NULL) {
    node->up->down  = node->right;
    if (node->right != NULL)
      node->right->up = node->up;
    node->up        = NULL;
  }

  if (node->left != NULL && node->right != NULL) {
    node->left->right = node->right;
    node->right->left = node->left;
  }

  else if (node->right != NULL)
    node->right->left = NULL;

  else if (node->left != NULL)
    node->left->right = NULL;

  node->left        = NULL;
  node->right       = NULL;

  return node;
}

/*******************************************************************************************************
 *
 *   Libere le noeud <node>. Cette fonction est appelee recursivement sur les fils et les freres
 *   de ce noeud. La liste des proprietes est liberee par la meme occasion
 */

#ifdef _GNU_SOURCE
void SG_FreeNode (SG_NodePtr node)
#else
void SG_FreeNode (node)
SG_NodePtr node;
#endif
{
  SG_PropertyPtr property;
  SG_PropertyPtr next_property;
  SG_NodePtr     next_node;

 free_node :

  if (node != NULL) {
    if (node->right != NULL)
      SG_FreeNode (node->right);

    next_property = node->property;
    
    while (next_property != NULL) {
      property      = next_property;
      next_property = property->next;
      
      switch (property->id) {
      case SG_Letters     :
      case SG_Comment     :
      case SG_GameComment :
      case SG_GameName    :
      case SG_nodeName    :
      case SG_EVent       :
      case SG_ROund       :
      case SG_DaTe        :
      case SG_PlaCe       :
      case SG_REsult      :
      case SG_USer        :
      case SG_TiMe        :
      case SG_SOurce      :
      case SG_BlackRank   :
      case SG_WhiteRank   :
      case SG_BlackSpec   :
      case SG_WhiteSpec   :
      case SG_HAndicap    :
      case SG_KoMi        :
      case SG_PlayerBlack :
      case SG_PlayerWhite :
      case SG_BlackLeft   :
      case SG_WhiteLeft   :
	free ((char *) property->data.pvalue);
	break;
      }
      
      (void) free ((char *) property);
    }

    (void) free ((char *) node->setup);

    next_node = node->down;
    (void) free ((char *) node);

    node = next_node;
    goto free_node;
  }
}

/*******************************************************************************************************
 *
 *   Retourne un nouveau noeud alloue par malloc. Tous les champs sont remis a zero.
 */

static SG_NodePtr make_node ()
{
  SG_NodePtr node;

  node = (SG_NodePtr) malloc ((unsigned) sizeof (SG_Node));

  node->num      = -1;
  node->color    = SG_EmptyPoint;
  node->x        = 0;
  node->y        = 0;
  node->player   = SG_EmptyPoint;
  node->setup    = NULL;
  node->property = NULL;
  node->up       = NULL;
  node->down     = NULL;
  node->left     = NULL;
  node->right    = NULL;

  return node;
}

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

#ifdef _GNU_SOURCE
SG_NodePtr SG_MakeNode (SG_NodePtr node, int type)
#else
SG_NodePtr SG_MakeNode (node, type)
SG_NodePtr node;
int type;
#endif
{
  SG_NodePtr new = NULL;

  switch (type) {

  case SG_Event :
    new = make_node ();

    if (node != NULL) {
      do {
	while (node->up != NULL)
	  node = node->up;
	while (node->left != NULL)
	  node = node->left;
      } while (node->up != NULL);
      
      while (node->right != NULL)
	node = node->right;
      
      node->right = new;
      new->left   = node;
    }
      
    new->type   = SG_EventType;
    break;

  case SG_Move    :
  case SG_Diagram :
    new = make_node ();

    node->down  = new;
    new->up     = node;

    if (type == SG_Move)
      new->type   = SG_MoveType;
    else
      new->type   = SG_DiagramType;
    break;

  case SG_VariationMove    :
  case SG_VariationDiagram :

    new = make_node ();

    while (node->right != NULL)
      node = node->right;

    node->right = new;
    new->left   = node;

    if (type == SG_VariationMove)
      new->type   = SG_MoveType;
    else
      new->type   = SG_DiagramType;

    break;
  }
  
  return new;
}

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

#ifdef _GNU_SOURCE
SG_PropertyPtr SG_GetProperty (SG_NodePtr node, int id)
#else
SG_PropertyPtr SG_GetProperty (node, id)
SG_NodePtr node;
int id;
#endif
{
  SG_PropertyPtr property = node->property;
  
  while (property != NULL) {
    if (property->id == id)
      break;
    property = property->next;
  }
  
  return property;
}

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

#ifdef _GNU_SOURCE
void SG_MakeProperty (SG_NodePtr node, int id, long value)
#else
void SG_MakeProperty (node, id, value)
SG_NodePtr node; 
int id; 
long value;
#endif
{
  SG_PropertyPtr property;

  property = SG_GetProperty (node, id);

  if (property == NULL) {
    if (node->property != NULL) {
      property = node->property;
  
      while (property->next != NULL)
	property = property->next;
 
      property->next = (SG_PropertyPtr) malloc ((unsigned) sizeof (SG_Property));
      property       = property->next;
    }

    else 
      property = node->property = (SG_PropertyPtr) malloc ((unsigned) sizeof (SG_Property));

    (void) bzero ((char *) property, sizeof (SG_Property));
  }

  property->id = id;
  
  switch (id) {
    
  case SG_unVieW :
  case SG_ko     :

  case SG_KO     :
    property->data.ivalue = value;
    break;
    
  case SG_SiZe :
  case SG_VieW :
    property->data.pvalue = (unsigned char *) value;
    break;
    
  case SG_AddBlack :
  case SG_AddWhite :
  case SG_AddEmpty :
  case SG_Letters  :
  case SG_Marked   :
    SG_strcat ((char **) &property->data.pvalue, (char *) value, SG_EOS);
    break;
    
  case SG_Comment :
  case SG_GameComment :
    SG_strcat ((char **) &property->data.pvalue, (char *) value, '\0');
    break;
    
  case SG_GameName    :
  case SG_nodeName    :
  case SG_EVent       :
  case SG_ROund       :
  case SG_DaTe        :
  case SG_PlaCe       :
  case SG_REsult      :
  case SG_USer        :
  case SG_TiMe        :
  case SG_SOurce      :
  case SG_BlackRank   :
  case SG_WhiteRank   :
  case SG_BlackSpec   :
  case SG_WhiteSpec   :
  case SG_HAndicap    :
  case SG_KoMi        :
  case SG_PlayerBlack :
  case SG_PlayerWhite :
  case SG_BlackLeft   :
  case SG_WhiteLeft   :
    SG_strcpy ((char **) &property->data.pvalue, (char *) value, '\0');
    break;
  }
}

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