/****************************************************************************************************************
 *
 *  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
 */

#include "sg.h"

static int size        = 19;

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

static int read_int (i, file)
int   *i;
FILE  *file;
{
  int result = SG_OK;
  int ch     = ' ';
  int j      = 0;

  while (ch != '[' && ch != EOF) ch = getc (file);
  if (ch == EOF) return SG_EOFERR;

  ch = getc (file);
  if (! isdigit (ch)) goto error;

  while (isdigit (ch)) {
    j = 10 * j + ch - '0';
    ch = getc (file);
  }
  *i = j;
  goto ok;

 error :
  result = SG_NDGERR;

 ok :
  while (ch != ']' && ch != EOF) ch = getc (file);
  return result;
}

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

static int read_color (color, file)
unsigned char  *color;
FILE           *file;
{
  int result = SG_OK;
  int ch     = ' ';

  while (ch != '[' && ch != EOF) ch = getc (file);
  if (ch == EOF) return SG_EOFERR;

  ch = getc (file);
  switch (ch) {
  case 'B' : case 'b' : *color = SG_BlackStone; goto ok;
  case 'W' : case 'w' : *color = SG_WhiteStone; goto ok;
  }

  result = SG_NCLERR;

 ok :
  while (ch != ']' && ch != EOF) ch = getc (file);
  return result;
}

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

static int read_stone (x, y, file)
unsigned char  *x;
unsigned char  *y;
FILE           *file;
{
  int result = SG_OK;
  int ch     = ' ';

  while (ch != '[' && ch != EOF) ch = getc (file);
  if (ch == EOF) return SG_EOFERR;

  ch = getc (file);
  if (! islower (ch)) goto error;
  *x = ch - 'a' + 1;

  ch = getc (file);
  if (! islower (ch)) goto error;
  *y = size + 1 - (ch - 'a' + 1);

  if (*y == 20 || *x == 20)
    *x = *y = 0;

  goto ok;

 error :
  result = SG_NSTERR;

 ok :
  while (ch != ']' && ch != EOF) ch = getc (file);
  return result;
}

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

static int read_stones (color, dst, file)
unsigned char   color;     
unsigned char **dst;
FILE           *file;
{
  unsigned char  stones[361 * 2];
  unsigned char *pos = stones;
  int            ch  = ' ';

  do {
    if (read_stone (pos, pos + 1, file) == SG_OK) {
      *pos++ |= color;
      *pos++ |= color;
    }

    do ch = getc (file);
    while (isspace (ch) && ch != EOF);

    if (ch != EOF)
      (void) ungetc (ch, file);

  } while (ch == '[');

  if (pos != stones) {
    *pos++ = SG_EOS;
    SG_strcat ((char **) dst, (char *) stones, SG_EOS);
    return SG_OK;
  }

  else 
    return SG_NSTERR;
}

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

static int read_text (dst, file, add)
unsigned char **dst;
FILE           *file;
int             add;
{
  char  text[5010];
  char *ptr = text;
  int   ch  = ' ';

  while (ch != '[' && ch != EOF) ch = getc (file);
  if (ch == EOF) return SG_NCTERR;

  while (ch != ']' && ch != EOF) {
    ch = getc (file);

    if ((unsigned) (ptr - text) < 5000) {
      if (ch == '\\') {
	*ptr = getc (file);
	
	switch (*ptr) {
	case 'n' : *ptr = '\n'; break;
	case 't' : *ptr = '\t'; break;
	}
	
	ptr++;
      }
      
      else
	*ptr++ = ch;
    }

    else {
      *ptr++ = '.';
      *ptr++ = '.';
      *ptr++ = '.';
    }
  }
  
  ptr--;

  if (ptr != text) {
    *ptr++ = '\0';

    if (add != 0)
      SG_strcat ((char **) dst, (char *) text, '\0');
    else 
      SG_strcpy ((char **) dst, (char *) text, '\0');
  }

  return SG_OK;
}

/*******************************************************************************************************
 */
   
static void read_property (node, cmd, file)
SG_NodePtr  node;
char       *cmd;
FILE       *file;
{
  int            id;
  SG_PropertyPtr property;
  char           top;
  char           left;
  char           bottom;
  char           right;
  unsigned char  x;
  unsigned char  y;
  int            i;
  long           value;

  for (id = 0; id < SG_MaxID; id++)
    if (strcmp (cmd, sg_keywords[id].keyword) == 0)
      break;

  id = sg_keywords[id].id;

  if ((id == SG_Black || id == SG_White) && node->type == SG_DiagramType) {
    if (node->setup == NULL)
      node->type = SG_MoveType;

    else
      id = (id == SG_Black) ? SG_AddBlack : SG_AddWhite;
  }

  if (node->type == SG_DiagramType || node->type == SG_EventType) {
    if (id == SG_Black) {
      id = SG_AddBlack;
      if (node->player == SG_EmptyPoint) node->player = SG_WhiteStone;
    }

    if (id == SG_White) {
      id = SG_AddWhite;
      if (node->player == SG_EmptyPoint) node->player = SG_BlackStone;
    }
  }

  if ((node->type & sg_keywords[id].node_id) != 0) 
    switch (id) {
      
    case SG_SiZe :
      if (read_int (&i, file) == SG_OK) {
	size = i;
	SG_MakeProperty (node, id, size);
      }
      break;
  
    case SG_Black :
      if (read_stone (&x, &y, file) == SG_OK) {
	node->x     = x;
	node->y     = y;
	node->color = SG_BlackStone;
      }
      break;
      
    case SG_White :
      if (read_stone (&x, &y, file) == SG_OK) {
	node->x     = x;
	node->y     = y;
	node->color = SG_WhiteStone;
      }
      break;
      
    case SG_AddBlack :
      (void) read_stones (SG_SETB, &node->setup, file);
      break;
      
    case SG_AddWhite :
      (void) read_stones (SG_SETW, &node->setup, file);
      break;
      
    case SG_AddEmpty :
      (void) read_stones (SG_SETE, &node->setup, file);
      break;

    case SG_VieW :
      if (read_stone (&x, &y, file) == SG_OK) {
	left = x; 
	top  = y;

	if (read_stone (&x, &y, file) == SG_OK) {
	  right  = x; 
	  bottom = y;
	  value  = ((long) top << 24) + ((long) left << 16) + ((long) bottom << 8) + (long) right;

	  SG_MakeProperty (node, id, value);
	}
      }
      break;

    case SG_Letters :
    case SG_Marked  :
      SG_MakeProperty (node, id, NULL);
      property = SG_GetProperty (node, id);
      (void) read_stones (SG_SETE, &property->data.pvalue, file);
      break;

    case SG_Comment :
    case SG_GameComment :
      SG_MakeProperty (node, id, NULL);
      property = SG_GetProperty (node, id);
      (void) read_text (&property->data.pvalue, file, 1);
      break;

    case SG_PLayer :
      (void) read_color (&x, file);
      node->player = x;
      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_MakeProperty (node, id, NULL);
      property = SG_GetProperty (node, id);
      (void) read_text (&property->data.pvalue, file, 0);
      break;
    }
}

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

static SG_NodePtr root;

static SG_NodePtr read_node (node, var, file, level)
SG_NodePtr  node;
int         var;
FILE       *file;
int level;
{
  char cmd[3];
  int  ch;
  int  len     = 0;
  int  no_node = 1;
  int  nb_var  = 0;

  do {
    ch = getc (file);

    switch (ch) {

    case ';' :
      if (level != 0) {
	if (node == NULL) {
	  node = SG_MakeNode (root, SG_Event);
	  if (root == NULL)
	    root = node;
	}

	else if (var-- == 1)
	  node = SG_MakeNode (node, SG_VariationDiagram);

	else
	  node = SG_MakeNode (node, SG_Diagram);
	
	no_node = 0;
	len     = 0;
      }
      break;

    case '(' :	
      if (level == 0)
	node = read_node ((SG_NodePtr) NULL, 0, file, level + 1);
      else if (nb_var++ == 0 || node->down == NULL)
	(void) read_node (node, 0, file, level + 1);
      else 
	(void) read_node (node->down, 1, file, level + 1);

      len = 0;
      break;

    case EOF :
    case ')' :
      return node;

    case '[' :
      if (len > 0 && no_node == 0) {
	(void) ungetc (ch, file);
	cmd[len++] = '\0';

	read_property (node, cmd, file);
      }

      else
	while (ch != ']' && ch != EOF) ch = getc (file);

      len = 0;
      break;

    default :
      if (isupper (ch) && len < 2)
	cmd[len++] = ch;
    }

  } while (ch != EOF);

  return node;
}

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

SG_NodePtr SG_ReadTree (file)
FILE       *file;
{
  size = 19;
  root = NULL;

  (void) read_node ((SG_NodePtr) NULL, 0, file, 0);
  
  return root;
}

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