/*
 * readtext.c,v 1.1 1994/01/28 17:06:48 franktor Exp
 */

#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "marctext.h"

/*
 * Debug function...
 */

void dump_record(Marcrecord *rec) {
  Marcdata *entry;
  for(entry = rec->entries; entry; entry = entry->next)
    fprintf(stderr, "%3lu %s\n",entry->tag, entry->data);
}

/*
 * ReadMarcText: Parse input in text-format and return it in Marcrecord
 * format.
 */

Marcrecord *
ReadMarcText (octetString *os)
{
  unsigned long pos = 0; /* Which position we are working at in the text */
  Marcrecord *rec = (Marcrecord *) malloc(sizeof(Marcrecord));

  rec->entries = (Marcdata *) NULL;
  Init_Marclabel (&rec->label);

  while (ReadMarcTextEntry (rec, os, &pos) == True);
  
#ifdef DEBUG
  dump_record(rec); /* Debug */
#endif

  /*
   * Must add something here that calculates correct values for the label.
   * (In case the text-format lacks the 000 field)
   */
  if(ConvertMarcTextEntryToLabel(rec) == False)
  {
#ifdef DEBUG
    fprintf(stderr, "Warning: Failed to convert 000 to label.\n");
#endif
    /*
     * Which means I must calculate it myself
     */
  }
  UpdateMarcLabelDirectoryFormat(rec);
  return rec;
}

/*
 * UpdateMarcLabelDirectoryFormat calculates which format to use
 * for the length and start field in the directory.
 * The sum of the siffers to use on those two numbers is DATAFIELD_LENGTH
 * (usually 9), and that length must be divided between the
 * number specifying where a record starts and the number specifying
 * how long a record is.
 */

void
UpdateMarcLabelDirectoryFormat (Marcrecord *rec)
{
  long max_length = 0;
  long max_start = 0;
  int i,j;
  Marcdata *entry;
  char buf[MAX_BUF];

  rec->label.record_length = Len_label + 1; /* 1 for the last RECORD_SEPARATOR*/

  for(entry = rec->entries; entry != (Marcdata *) NULL; entry = entry->next)
  {
    if (entry->length_of_datafield > max_length)
      max_length = entry->length_of_datafield;
    if (entry->starting_character_position > max_start)
      max_start = entry->starting_character_position;

    rec->label.record_length += entry->length_of_datafield +
                                TAG_LENGTH + DATAFIELD_LENGTH + 1;
  }
  (void) sprintf(buf, "%ld", max_length);
  max_length = strlen(buf);
  (void) sprintf(buf, "%ld", max_start);
  max_start = strlen(buf);

  for (i = 0; i < DATAFIELD_LENGTH/2; i++)
    for (j = (-1); j <= 1; j+=2)
    {
      int start = DATAFIELD_LENGTH/2 + i*j;
      int length = DATAFIELD_LENGTH - start;
      if (max_start <= start && max_length <= length)
      {
        rec->label.directory_map_start = start;
        rec->label.directory_map_length = length;
#ifdef DEBUG
        fprintf(stderr, "Debug: directory map format is %1d%1d\n", length, start);
#endif
        return;
      }
    }
  fprintf(stderr, "Warning: Failed to find suitable format for directory!\n");
}

void log_start(char *cp, int max) {
  int i;
  for (i=0; *cp != '\n' && *cp != '\0' && i < max; i++, cp++)
    putc(*cp, stderr);
  for (;i<max;i++)
    putc(' ', stderr);
}

Boolean
ReadMarcTextEntry (Marcrecord *rec, octetString *os, unsigned long *pos)
{
  Marcdata *entry, *last;
  unsigned long tag;
  unsigned long bp;	/* Buffer pointer */
  char buf[CHARS_IN_NORMARC_LINE];

  char indicator1, indicator2;
  Boolean indicators = False;

#ifdef DEBUG
  int tmp;
  fprintf(stderr, "Starting to read field (%4d): ", *pos);
  log_start(os->value+*pos, 20);
#endif

  if (*(os->value + *pos) == NORMARC_END_OF_RECORD || *pos == os->len)
  {
    /*
     * We have reached the end.
     */
#ifdef DEBUG
    fprintf(stderr, "\n");
#endif
    if (*pos == os->len)
      fprintf(stderr, "Warning: Record didn't end with ^.");
    return False;
  }

  if (*(os->value + *pos) != MARC_TEXT_LINE_START)
  {
    fprintf(stderr, "Error: Wrong start of normarc record: %c (%d)\n",
            *(os->value + *pos), *(os->value + *pos));
    return False;
  }
  (*pos)++; /* Skip over the '*' */

  tag = strntol(os->value + *pos, (char **) NULL, 10, TAG_LENGTH);
  if (tag < 000 || tag > 999)
    fprintf(stderr, "Warning: Range of tag value illegal: %lu\n",tag);
#ifdef DEBUG
  fprintf(stderr, "Tag: %3d\n",tag);
#endif

  *pos += TAG_LENGTH; /* Skip over the tag */

  /*
   * If the tag is less than 10, it is a control-field with constant length.
   * Otherwise the next two characters will be Starting Character Position.
   */
  if (!IS_CONTROL_FIELD(tag))
  {
    if (*pos + 2 > os->len)
      fprintf(stderr, "Warning: No room for indicators in datafield.\n");
    else {
      indicator1 = *(os->value + (*pos)++);
      indicator2 = *(os->value + (*pos)++);
      indicators = True;
    }
  }

  /*
   * This is a little simplified: Fix in the future.
   * If there are differences in USMARC Vs NORMARC in the use
   * of indicators/identifiers (quite likely), I will have to
   * parse them as well, instead of just storing them with the data.
   */

  bp = 0;
  while(bp < CHARS_IN_NORMARC_LINE)
  {
    register char ch;
    ch = *(os->value + *pos);
    switch(ch)
    {
      case CR:
      case LF:
      {
        /*
         * Skip over all lineshifts:
         */
        while((*(os->value + *pos)) == LF || (*(os->value + *pos)) == CR)
          (*pos)++;

        /*
         * Check if the field continues on the next line, and break if it does.
         */
        if (*(os->value + *pos) != MARC_TEXT_LINE_START &&
            *(os->value + *pos) != NORMARC_END_OF_RECORD)
        {
          /*
           * Yes, the field continues.  Strip leading whitespace on next line.
           */
#ifdef DEBUG
          tmp = *pos;
#endif
          while(isspace(*(os->value + *pos)))
            (*pos)++;
          buf[bp++]=' '; /* Add a space to separate words */
#ifdef DEBUG
          fprintf(stderr, "Record continues, skipped %d chars.\n", *pos - tmp);
#endif
          break;
        }

        entry = (Marcdata *) malloc(sizeof(Marcdata));
        entry->tag = tag;
        entry->data = (char *) malloc(bp * sizeof(char) + 1); /*in case 0-str*/
        (void) memcpy(entry->data, buf, bp);
        entry->length_of_datafield = bp;
        if ((entry->indicators = indicators) == True) {
          entry->indicator1 = indicator1;
          entry->indicator2 = indicator2;
        }
#ifdef DEBUG
        buf[bp]='\0';
        fprintf(stderr, "Entry: %s\n", buf);
#endif
        for (last = rec->entries;
             last != (Marcdata *) NULL && last->next != (Marcdata *) NULL;
             last = last->next);
        if (last)
          last->next = entry;
        else
          rec->entries = entry;
        return True;
      }
      default:
        buf[bp++] = ch;
        (*pos)++;
    }
  }
  fprintf(stderr, "Error: Line too long: %lu (of %d)\n",bp,CHARS_IN_NORMARC_LINE);
  return False;
}

/*
 * In text-format, the label with tag 000 is actually the label, so we
 * have to transfer the data to the label and free entry from the linked
 * list of entries.
 */

Boolean
ConvertMarcTextEntryToLabel(Marcrecord *rec)
{
  Marcdata *entry = rec->entries;
  Boolean result;

  if (entry == (Marcdata *) NULL || entry->tag != 0)
  {
#ifdef DEBUG
    fprintf(stderr, "Warning: First tag was not 000 in NORMARC text format.\n");
#endif
    return False;
  }
  if (entry->length_of_datafield < Len_label)
  {
#ifdef DEBUG
    fprintf(stderr, "Warning: Size of tag 000 is less than %d.\n",Len_label);
#endif
    /* return False; */
  }
  if (entry->length_of_datafield > Len_label)
  {
#ifdef DEBUG
    fprintf(stderr, "Warning: Size of tag 000 is more than %d.\n",Len_label);
#endif
    /* return False; */
  }
  /*
   * Since the data in the first entry has to be of the same format
   * as the first 24 characters of a marc entry in the ISO-2709 format,
   * I just call the same routine to parse that data into our private
   * data structure.
   */
  result = ReadMarcData_Label(&rec->label, entry->data, entry->length_of_datafield);
  rec->entries = entry->next;
  free(entry->data);
  free(entry);
  return result;
}

void
Init_Marclabel(Record_label *label)
{
  label->record_length = 0;
  label->record_status = ' ';
  label->implementation_codes[0] = '\0';
  label->indicator_length = 0;
  label->identifier_length = 0;
  label->base_address_of_data = 0;
  label->reserved_user_systems[0] = '\0';
  /*
   * Note: These values are illegal and must be computed later:
   */
  label->directory_map_length = 0;
  label->directory_map_start = 0;
  label->reserved_future[0] = '\0';
}
