/*+++++++++++++++++
  mset.c - implements some functions for bibliography output
  markus@mhoenicka.de 2005-12-15
  $Id: mset.c,v 1.1.2.2 2005/12/17 01:36:33 mhoenicka Exp $

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, see <http://www.gnu.org/licenses/>

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include <stdio.h> /* for a definition of NULL */
#include <stdlib.h> /* for malloc */
#include <string.h>

#include "strfncs.h"
#include "xmlhelper.h"
#include "mset.h"
#include "outformats.h" /* for the REFXX definitions */

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  set_mset(): starts and closes bibliomsets as requested

  static char* set_mset returns ptr to the buffer if successful, NULL if failed

  struct mset* ptr_first pointer to the sentinel of the mset stack

  char* new_mset ptr to a string containing the requested new
              bibliomset relation attribute. The string may be empty
              indicating that we shall only close the current bibliomset
              if any

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least one byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* set_mset(struct mset* ptr_first, char* new_mset, char** ptr_ref, size_t* ptr_ref_len, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  int no_close = 0;
  char* new_ref;
  char current_mset[MSET_RELATION_LEN];
  char bibliomset[] = "bibliomset";
  char relation[] = "relation";

  /* if there is no stack, assume that no msets should be handled */
  if (!ptr_first) {
    return *ptr_ref;
  }

  if (!ptr_first->ptr_next) {
    *current_mset = '\0';
    no_close = 1;
  }
  else {
    strncpy(current_mset, ptr_first->ptr_next->relation, MSET_RELATION_LEN);
    current_mset[MSET_RELATION_LEN-1] = '\0';
  }

  if (n_ref_format == REFTEIX5) {
    strcpy(bibliomset, "seg");
    strcpy(relation, "type");
  }

  if (/*(!(*current_mset == *new_mset && *current_mset == '\0')) || strcmp(current_mset, new_mset) != 0*/ 1) {
    if (!*new_mset/*  && *current_mset */ && !no_close) { /* dont add new bibliomset, but old one exists */
      if (print_elend_x(ptr_ref, ptr_ref_len, bibliomset, ptr_indent, ns) == NULL) {
	return NULL;
      }
      pop_mset(ptr_first, current_mset);
    }
    else if (!no_close && *new_mset && strcmp(current_mset, new_mset)){ /* close old one, add new one */
      pop_mset(ptr_first, current_mset);
      if (print_elend_x(ptr_ref, ptr_ref_len, bibliomset, ptr_indent, ns) == NULL) {
	return NULL;
      }

      if (print_elstart_x(ptr_ref, ptr_ref_len, bibliomset, relation, new_mset, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	return NULL;
      }
      push_mset(ptr_first, new_mset);
    }
    else if (*new_mset && strcmp(current_mset, new_mset)) { /* add new one, no old one to close */
      if (print_elstart_x(ptr_ref, ptr_ref_len, bibliomset, relation, new_mset, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	return NULL;
      }
      push_mset(ptr_first, new_mset);
    }
  }

  return *ptr_ref;
}

struct mset* new_mset_stack(void) {
  struct mset* ptr_new;

  if ((ptr_new = malloc(sizeof(struct mset))) == NULL) {
    return NULL;
  }

  *(ptr_new->relation) = '\0';
  ptr_new->ptr_next = NULL;
  ptr_new->stacksize = 0;

  return ptr_new;
}

void clear_mset_stack(struct mset* ptr_first) {
  struct mset* ptr_delete;

  if (ptr_first) {
    while((ptr_delete = ptr_first->ptr_next) != NULL) {
      ptr_first->ptr_next = ptr_delete->ptr_next;
      free(ptr_delete);
    }
    free(ptr_first);
  }
}

char* push_mset(struct mset* ptr_first, char* new_mset) {
  struct mset* ptr_new;

  if (!new_mset
      || (ptr_new = malloc(sizeof(struct mset))) == NULL) {
    return NULL;
  }

  strncpy(ptr_new->relation, new_mset, MSET_RELATION_LEN);
  ptr_new->relation[MSET_RELATION_LEN-1] = '\0';

  ptr_new->ptr_next = ptr_first->ptr_next;
  ptr_first->ptr_next = ptr_new;
  ptr_first->stacksize++;

  return new_mset;
}

char* pop_mset(struct mset* ptr_first, char* prev_mset) {
  struct mset* ptr_pop;

  if (!prev_mset
      || !ptr_first->ptr_next) {
    return NULL;
  }

  strncpy(prev_mset, ptr_first->ptr_next->relation, MSET_RELATION_LEN);
  prev_mset[MSET_RELATION_LEN-1] = '\0';
  
  ptr_pop = ptr_first->ptr_next;

  ptr_first->ptr_next = ptr_pop->ptr_next;
  free(ptr_pop);
  ptr_first->stacksize--;

  return prev_mset;
}
