/*
 *  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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "chain.h"

struct chain *new_chain(const void *value, chain_memory_manager _2be3) {
/* -----------------------------------------------------------------------------
 * Create a new chain 
 *
 * @param  const void *
 * @param  chain_memory_manager
 * @return struct chain *
 * -------------------------------------------------------------------------- */
  struct chain *new_pts;
  new_pts = (struct chain *)malloc(sizeof(struct chain));
  if (new_pts == NULL) {
    logmsg(LOG_ERR, "new_chain() : malloc error");
    return NULL;
  }
  new_pts->value = (void *)value;
  new_pts->_2be3 = _2be3;
  new_pts->previous = NULL;
  new_pts->next = NULL;
  
  return new_pts;
}

struct chain *new_chains_struct(struct chain * first, ...) {
/* -----------------------------------------------------------------------------
 * Create new root chain from several chains 
 *
 * @param  struct chain *
 * @return struct chain *
 * -------------------------------------------------------------------------- */
  va_list args;
  struct chain *pts;
  struct chain *result = first;
  va_start(args, first);
    
  while ((pts = va_arg(args, struct chain *)) != NULL) {
    append_chain(pts, result);
  }
  va_end(args);
  return result;
}

struct chain *new_chains(const void * first_value, ...) {
/* -----------------------------------------------------------------------------
 * Create new root chain from several values 
 * if values are allocated dynamically, this is the caller who must manage
 * their destruction
 *
 * @param  const void *
 * @return struct chain *
 * -------------------------------------------------------------------------- */
  va_list args;
  struct chain *pts;
  struct chain *result = new_chain(first_value, NO_FREE);
  va_start(args, first_value);
    
  while ((pts = va_arg(args, void *)) != NULL) {
    append_chain(new_chain(pts, NO_FREE), result);
  }
  va_end(args);
  return result;
}
  
void drop_all_chain(struct chain *root_chain) {
/* -----------------------------------------------------------------------------
 * Drop entirely a chain 
 *
 * @param  struct chain *
 * -------------------------------------------------------------------------- */
  struct chain *pts;
  struct chain *pts_tmp;
  if (root_chain == NULL) return;
  pts = root_chain;
  while(pts != NULL) {
    if (pts->_2be3 == FREE)
      free(pts->value);
    pts_tmp = pts->next;
    free(pts);
    pts = pts_tmp;
  }
}

struct chain * add_chain(struct chain *new_chain,
                         struct chain *chain_dest,
                         unsigned int position) {
/* -----------------------------------------------------------------------------
 * Add a new chain at the given position
 * if the given position is greater thant the size,
 * the new chain will be add to the end 
 *
 * @param  struct chain, the new chain
 * @param  struct chain, the chain where to add
 * @param  int
 * @return struct chain *, the new root chain
 * A <-> B <-> C <-> D   .... insert T, position 1
 * A <-> T <-> B <-> C <-> D
 * -------------------------------------------------------------------------- */
  struct chain *pts, *pts_last;
  pts = get_chain(chain_dest, position);
  pts_last = get_last_chain(new_chain);
  
  new_chain->previous = pts->previous;
  pts->previous = pts_last;
  pts_last->next = pts;
  if (new_chain->previous != NULL)
    new_chain->previous->next = new_chain;
  
  return (position == 0) ? new_chain : chain_dest;
}

struct chain * delete_chain(struct chain *chain_root, unsigned int position) {
/* -----------------------------------------------------------------------------
 * Delete chain at the given position
 * if the given position is greater thant the size,
 * the last chain will be deleted 
 *
 * @param  struct chain, the chain where to delete
 * @param  int
 * @return struct chain *, the new root chain
 * A <-> B <-> C <-> D   .... delete at position 1
 * A <-> C <-> D
 * -------------------------------------------------------------------------- */
  struct chain *pts, *new_root;
  pts = get_chain(chain_root, position);
  if (pts == NULL) return chain_root;
  
  if (pts->previous != NULL)
    pts->previous->next = pts->next;
  if (pts->next != NULL)
    pts->next->previous = pts->previous;
  if (pts == chain_root) {
    if (pts->next != NULL)
      new_root = pts->next;
    else
      new_root = NULL;
  }
  else
    new_root = chain_root;
  
  if (pts->_2be3 == FREE)
    free(pts->value);
  free(pts);
  return new_root;
}

struct chain *get_chain(struct chain *chain_root, unsigned int position) {
/* -----------------------------------------------------------------------------
 * Get chain at the given position
 * if the given position is greater thant the size,
 * the last chain will be got 
 *
 * @param  struct chain, the chain where to search
 * @param  int
 * @return struct chain * or NULL
 * -------------------------------------------------------------------------- */
  struct chain *pts;
  unsigned int count = 0;
  for (pts = chain_root; pts != NULL && count < position; count++, pts = pts->next);
  return pts;
}

struct chain * find_chain(struct chain * chain_root, const void *value) {
/* -----------------------------------------------------------------------------
 * Get chain by value 
 *
 * @param  struct chain, the chain where to search
 * @param  const void *
 * @return struct chain * or NULL
 * -------------------------------------------------------------------------- */
  struct chain *pts;
  for (pts = chain_root; pts != NULL && pts->value != value; pts = pts->next);
  return pts;
}

int get_chain_position(struct chain * chain_root, const void *value) {
/* -----------------------------------------------------------------------------
 * Get chain position by value 
 *
 * @param  struct chain, the chain where to search
 * @param  const void *
 * @return int or -1 if not found
 * -------------------------------------------------------------------------- */
  struct chain *pts;
  int i = 0;
  
  for (pts = chain_root; pts != NULL && pts->value != value; pts = pts->next, i++);
  if (pts != NULL)
    return -1;
  return i;
}

struct chain *get_last_chain(struct chain *chain_root) {
/* -----------------------------------------------------------------------------
 * Get last chain 
 *
 * @param  struct chain, the chain where to search
 * @return struct chain * or NULL
 * -------------------------------------------------------------------------- */
 
  struct chain *pts;
  for (pts = chain_root; pts->next != NULL; pts = pts->next);
  return pts;
}

unsigned int get_nb_chain(struct chain *chain_root) {
/* -----------------------------------------------------------------------------
 * Get chain number 
 *
 * @param  struct chain, the chain where to search
 * @return unsigned int
 * -------------------------------------------------------------------------- */

  struct chain *pts;
  unsigned int count = 0;
  for (pts = chain_root; pts != NULL; count++, pts = pts->next);
  return count;
}

struct chain *copy_chain(struct chain *chain_root) {
/* -----------------------------------------------------------------------------
 * Copy chain 
 *
 * @param  struct chain, the chain where to copy
 * @return struct chain *, the copy
 * -------------------------------------------------------------------------- */
  struct chain *chain_copy;
  struct chain *pts;
  if (chain_root == NULL) return NULL;
  pts = chain_root;
  chain_copy = new_chain(pts->value, pts->_2be3);
  pts = pts->next;
  while(pts != NULL) {
    append_chain(new_chain(pts->value, pts->_2be3), chain_copy);
    pts = pts->next;
  }
  return chain_copy;
}

void append_chain(struct chain *new_chain, struct chain *chain_dest) {
/* -----------------------------------------------------------------------------
 * Insert chain at the end 
 *
 * @param  struct chain, the chain to add
 * @param  struct chain, the chain where to add
 * A <-> B <-> C <-> D   .... insert T at the end
 * A <-> B <-> C <-> D <-> T
 * -------------------------------------------------------------------------- */
  struct chain *pts;
  
  for (pts = chain_dest; pts->next != NULL; pts = pts->next);
  new_chain->previous = pts;
  pts->next = new_chain;
}

void print_chain(struct chain *chain_root) {
/* -----------------------------------------------------------------------------
 * Print the chain to stdout
 * Warning ! this only work for string
 *
 * @param  struct chain, the root chain
 * -------------------------------------------------------------------------- */
  struct chain *pts;
  for (pts = chain_root; pts != NULL; pts = pts->next) {
    printf("%s -> ", (char *)pts->value);
  }
  printf("\n");
}

void print_reverse_chain(struct chain *chain_root_reverse) {
/* -----------------------------------------------------------------------------
 * Print the chain to stdout from the end
 * Warning ! this only work for string
 *
 * @param  struct chain, the root chain
 * -------------------------------------------------------------------------- */
  struct chain *pts;
  for (pts = chain_root_reverse; pts != NULL; pts = pts->previous) {
    printf("%s -> ", (char *)pts->value);
  }
  printf("\n");
}

void print_all_chain(struct chain *chain_root) {
/* -----------------------------------------------------------------------------
 * Print the chain to stdout from the beginning to the end and from the end
 * to the beginning 
 * Warning ! this only work for string
 *
 * @param  struct chain, the root chain
 * -------------------------------------------------------------------------- */
  struct chain *pts = NULL;
  struct chain *last_pts = NULL;
  for (pts = chain_root; pts != NULL; last_pts = pts, pts = pts->next) {
    printf("%s -> ", (char *)pts->value);
  }
  printf("\n");
  
  for (pts = last_pts; pts != NULL; pts = pts->previous) {
    printf("%s -> ", (char *)pts->value);
  }
  printf("\n");
}


syntax highlighted by Code2HTML, v. 0.9.1