/*
 * IWRITE.C - write record.
 *
 *                      Copyright (c) 1987, Jim Mischel
 * Modifications:
 *
 * 08/13/87 - jim - original coding
 * 08/21/87 - jim - copy source to data buffer before writing
 */

#include "inxdefs.h"

/*
 * iwrite() - add the data from '*source' to the data file.  Returns 0 if
 * successful, status on error.  If duplicate keys are not permitted and
 * adding the record would casue a duplicate key, the global variable 'ierrno'
 * will be set to I_INVKEY.
 */
int iwrite(void *d, void *src)
{
  df_rec *db_control = (df_rec *)d;
  char *source = (char *)src;

  inx_rec irec;                         /* new index record */
  inx_rec save_inx_rec;
  long save_inx_ptr,
       write_pos;

  irec.if_flags = 0;

  switch (isearch(db_control,(source+db_control->df_key_offset))) {
    case 0      :
        if (!(db_control->df_flags & DF_DUP))   /* check duplicates */
          return(ierror(I_INVKEY));     /* error: duplicate key */
      /*
       * duplicate record key.
       * if the right node is a thread pointer, add the new record at right
       */
        if (db_control->df_inx_buff.if_flags & RTHRD)
          goto set_right;
      /*
       * otherwise get the inorder successor and add the new record as the
       * left son.
       */
        if (iget_next(db_control,&db_control->df_inx_buff)) {
          return(ierrno);
        }
    case -1     :                       /* insert key at left node */
        /* left node of new node is parent's left node */
        irec.if_left_node = db_control->df_inx_buff.if_left_node;

        /* right node of new node is parent */
        irec.if_right_node = db_control->df_inx_ptr;

        /* both right and left pointers of new node are thread pointers */
        irec.if_flags = (db_control->df_inx_buff.if_flags & BTHRD) +
                        RTHRD + LTHRD;

        /* if parent is the header node, this node is end of thread */
        if (db_control->df_inx_ptr == 0L)
          irec.if_flags |= ETHRD;

        /* parent's left node points to new node */
        db_control->df_inx_buff.if_left_node = fsize(db_control->df_inx_file);

        /* parents left node is NOT a thread pointer */
        db_control->df_inx_buff.if_flags &= (ETHRD+RTHRD);
        break;
    case 1      :                       /* insert key at right node */
set_right:
        /* right node of new node is parent's right node */
        irec.if_right_node = db_control->df_inx_buff.if_right_node;

        /* left node of new node is parent */
        irec.if_left_node = db_control->df_inx_ptr;

        /* both right and left pointers of new node are thread pointers */
        irec.if_flags = (db_control->df_inx_buff.if_flags & ETHRD) +
                         RTHRD + LTHRD;

        /* parent's right node points to new node */
        db_control->df_inx_buff.if_right_node = fsize(db_control->df_inx_file);

        /* parent's right node is NOT a thread pointer */
        db_control->df_inx_buff.if_flags &= (LTHRD+BTHRD);
        break;
    default     : return(ierrno);       /* read error */
  } /* switch */

  /* data record will go to end of data file */
  irec.if_dat_ptr = fsize(db_control->df_dat_file);

  /* new record's parent is current node */
  irec.if_parent = db_control->df_inx_ptr;      

  /* save current index record */
  memcpy(&save_inx_rec,&db_control->df_inx_buff,sizeof(inx_rec));
  save_inx_ptr = db_control->df_inx_ptr;        /* save current index pointer */
  write_pos = fsize(db_control->df_inx_file);   /* save new index location */

  /*
   * Now we update the index and data files.  This is done as non-destructively
   * as possible.  The new index node is added to the index file first.  Then
   * the new data record is added to the end of the data file.  The last
   * operation performed is to update the old index record.  This ensures
   * the integrity of the binary tree structure.  If an error occurs while
   * writing any of the records, the binary tree remains as it was before
   * adding the new node was attempted.
   */

  /* add new index node */
  if (iwrite_inx(db_control,&irec,write_pos))
    return(ierrno);

  /* add new data record to end of data file */
  memcpy(db_control->df_dat_buff,source,db_control->df_rec_size);
  if (iwrite_dat(db_control,source,fsize(db_control->df_dat_file)))
    return(ierrno);

  /* update the old index record */
  if (iwrite_inx(db_control,&save_inx_rec,save_inx_ptr))
    return(ierrno);

  /* make index file point to new record */
  if (iread_inx(db_control,write_pos))
    return(ierrno);

  db_control->df_flags &= ~DF_DELETE;           /* clear deleted record flag */
  return(ierror(0));
} /* iwrite */
