/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  backendn-notex.c defines the notes xnote output backend of refdbd
  markus@mhoenicka.de 2003-10-15

   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 <string.h>
#include <syslog.h> /* for definitions of log message priorities */
#include <time.h>
#include <stdio.h>
#include <dbi/dbi.h>

#include "refdb.h"
#include "linklist.h"
#include "refdbd.h"
#include "backend.h"
#include "backendn-notex.h"
#include "strfncs.h"
#include "dbfncs.h"
#include "xmlhelper.h"
#include "connect.h"


/* some globals */
extern int n_log_level; /* numeric version of log_level */

/* forward declaration of local functions */
static int indent_notbelow_xnote(const char* name);
static int is_entry_xnote(const char* name);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  prepare_render_note_xnote(): writes a header for the xnote output of a
                         notes query

  int prepare_render_note_xnote returns 0 if successful or >0 if failed

  struct renderinfo* ptr_rendinfo ptr to a structure with the info
                             how the note should be rendered

 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int prepare_render_note_xnote(struct renderinfo* ptr_rendinfo) {
  char* new_ref;
  char* header;
  char ns[PREFS_BUF_LEN+128];

  if ((header = assemble_header(ptr_rendinfo)) == NULL) {
    return 801;
  }

  new_ref = mstrcpy(*(ptr_rendinfo->ptr_ref), header, ptr_rendinfo->ptr_ref_len);
  free(header);

  if (new_ref == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }
  else {
    *(ptr_rendinfo->ptr_ref) = new_ref;
  }

  if (*(ptr_rendinfo->ptr_clrequest->namespace)) {
    sprintf(ns, "<%s:xnoteset xmlns:%s=\"http://refdb.sourceforge.net/ns/xnote\">\n", ptr_rendinfo->ptr_clrequest->namespace, ptr_rendinfo->ptr_clrequest->namespace);
  }
  else {
    strcpy(ns, "<xnoteset>\n");
  }

  new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), ns, ptr_rendinfo->ptr_ref_len, 0);

  if (new_ref == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }
  else {
    *(ptr_rendinfo->ptr_ref) = new_ref;
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  finish_render_note_xnote(): writes a footer for the xnote output of
                             a notes query

  int finish_render_note_xnote returns 0 if successful, >0 if failed

  struct renderinfo* ptr_rendinfo ptr to a structure with the info
                             how the reference should be rendered

 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int finish_render_note_xnote(struct renderinfo* ptr_rendinfo) {
  char* new_ref;
  char ns[PREFS_BUF_LEN+32];

  if (*(ptr_rendinfo->ptr_clrequest->namespace)) {
    sprintf(ns, "\n</%s:xnoteset>\n", ptr_rendinfo->ptr_clrequest->namespace);
  }
  else {
    strcpy(ns, "\n</xnoteset>\n");
  }

  new_ref = mstrcpy(*(ptr_rendinfo->ptr_ref), ns, ptr_rendinfo->ptr_ref_len);

  if (new_ref == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }
  else {
    *(ptr_rendinfo->ptr_ref) = new_ref;
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  render_note_xnote() renders a note for xnote output

  int render_note_xnote returns 0 if successful, >0 if failed

  struct renderinfo* ptr_rendinfo ptr to a structure with the info
                             how the reference should be rendered

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int render_note_xnote(struct renderinfo* ptr_rendinfo) {
  int i;
  char id[32] = "";
  char linktype[4][11] = {"reference", "keyword", "author", "periodical"};
  char date_buffer[256];
  const char* citem;
  const char* owner;
  short int share;
  char* item;
  char* new_ref;
  dbi_conn conn;
  dbi_result dbires;
  struct xmlindent xindent;

  /*initialize xindent */
  initialize_xmlindent(&xindent, 2, indent_notbelow_xnote, is_entry_xnote);

  conn = dbi_result_get_conn(ptr_rendinfo->dbires);

  /*----------------------------------------------------------------*/
  /* ID */
  get_refdb_note_id(ptr_rendinfo->dbires, id);

  citem = get_refdb_note_key(ptr_rendinfo->dbires);
  if (citem && !*citem) {
    citem = NULL;
  }
  
  get_refdb_note_date(ptr_rendinfo->dbires, date_buffer, 1);

  owner = get_refdb_note_user_name(ptr_rendinfo->dbires);

  if (!owner && !*owner) {
    owner = ptr_rendinfo->username;
  }

  share = get_refdb_note_share(ptr_rendinfo->dbires);

  if (*id) {
    if (share != -1) {
      if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "xnote", "id", id, "citekey", citem, "user", owner, "date", date_buffer, "share", (share == 1) ? "public":"private", 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      return 801;
      }
    }
    else {
      if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "xnote", "id", id, "citekey", citem, "user", owner, "date", date_buffer, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      return 801;
      }
    }
  }
  else {
    LOG_PRINT(LOG_WARNING, "note has no ID");
    return 234; /* this is bad and will hopefully never happen */
  }

  /*----------------------------------------------------------------*/
  /* note title */
  item = get_refdb_note_title_copy(ptr_rendinfo->dbires);
  if (item != NULL) {
    if (print_element_x(item, ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "title", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free((char*)item);
      return 801;
    }
    free((char*)item);

    if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), "\n", ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      return 801;
    }
    else {
      *(ptr_rendinfo->ptr_ref) = new_ref;
    }
  }

  /*----------------------------------------------------------------*/
  /* the content of the note */
  item = get_refdb_note_content_copy(ptr_rendinfo->dbires);
  if (item != NULL) {
    if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "content", "type", get_refdb_note_content_type(ptr_rendinfo->dbires), "xml:lang", get_refdb_note_content_xmllang(ptr_rendinfo->dbires), NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(item);
      return 801;
    }

    if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), (char*)item, ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
      free(item);
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      return 801;
    }
    else {
      *(ptr_rendinfo->ptr_ref) = new_ref;
    }

    free(item);

    if (print_elend_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "content", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      return 801;
    }
  }

  /*----------------------------------------------------------------*/
  /* keywords */
  dbires = request_keywords(conn, my_dbi_result_get_idval(ptr_rendinfo->dbires, "note_id"), 1, 0);
  if (dbires == NULL) {
    return 801;
  }

  /* fetch all keywords of this article */
  while ((citem = get_keyword(dbires)) != NULL) {

    if (print_element_x(citem, ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "keyword", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      clean_request(dbires);
      return 801;
    }
  }
  clean_request(dbires);

  /*----------------------------------------------------------------*/
  /* links */

  /* loop over reference, keyword, author, periodical links */
  for (i = 0; i < 4; i++) {
    int mode = i;
    dbires = request_links(conn, my_dbi_result_get_idval(ptr_rendinfo->dbires, "note_id"), i);
    if (dbires == NULL) {
      return 234;
    }

    /* fetch all links of this type and note */
    while ((citem = get_link(dbires, &mode)) != NULL) {
      if (i == 3) { /* periodical */
	if (!mode) { /* full name */
	  if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "link", "type", "journalfull", "target", citem, NULL, NULL, NULL, NULL, NULL, NULL, 1, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	    return 801;
	  }
	}
	else if (mode == 1) { /* abbrev */
	  if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "link", "type", "journalabbrev", "target", citem, NULL, NULL, NULL, NULL, NULL, NULL, 1, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	    return 801;
	  }
	}
	else if (mode == 2) { /* custabbrev 1 */
	  if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "link", "type", "journalcustabbrev1", "target", citem, NULL, NULL, NULL, NULL, NULL, NULL, 1, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	    return 801;
	  }
	}
	else if (mode == 3) { /* custabbrev 2 */
	  if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "link", "type", "journalcustabbrev2", "target", citem, NULL, NULL, NULL, NULL, NULL, NULL, 1, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	    return 801;
	  }
	}
      }
      else {
	if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "link", "type", linktype[i], "target", citem, NULL, NULL, NULL, NULL, NULL, NULL, 1, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  return 801;
	}
      }
    }
    clean_request(dbires);
  } /* end for */


  /*----------------------------------------------------------------*/
  /* ulink */

  /* loop over all link types */
  for (i=0; i<5;i++) {
    char ulink_type[10];
    dbi_result dbires;

    dbires = request_ulinks(conn, my_dbi_result_get_idval(ptr_rendinfo->dbires, "note_id"), 1 /* notes entry */, i /* link type */, 0 /* is_temp */, ptr_rendinfo->username);
    if (dbires == NULL) {
      return 234;
    }

    while ((citem = get_ulink(dbires)) != NULL) {
      if (i == 0) {
	strcpy(ulink_type, "url");
      }
      else if (i == 1) {
	strcpy(ulink_type, "pdf");
      }
      else if (i == 2) {
	strcpy(ulink_type, "fulltext");
      }
      else if (i == 3) {
	strcpy(ulink_type, "related");
      }
      else if (i == 4) {
	strcpy(ulink_type, "image");
      }
      if (print_element_x(citem, ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "ulink", "type", ulink_type, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	clean_request(dbires);
	return 801;
      }
    }

    clean_request(dbires);
  } /* end for */

  if (print_elend_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "xnote", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    return 801;
  }

  /* finish with an empty line */
  if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), "\n\n", ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 801;
  }
  else {
    *(ptr_rendinfo->ptr_ref) = new_ref;
  }
/*      printf("%s\n", ref); */
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  indent_notbelow_xnote(): checks whether or not to indent below the current
                     element

  static int indent_notbelow returns 1 if not to indent, 0 if to indent

  const char* name ptr to element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int indent_notbelow_xnote(const char* name)
{
  if (!strcmp(name, "date")) {
    return 1;
  }
  else {
    return 0;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  is_entry_xnote(): checks whether an element starts a new entry

  static int is_entry_risx returns 1 if entry starts, 0 if not

  const char* name ptr to element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int is_entry_xnote(const char* name) {
  if (!strcmp(name, "refdbnote")) {
    return 1;
  }
  else {
    return 0;
  }
}
