/*
 *  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_string.h"

struct chain_string *new_chain_string(const char *value) {
/* -----------------------------------------------------------------------------
 * Create a new chain string 
 *
 * @param  const char *
 * @return struct chain_string *
 * -------------------------------------------------------------------------- */
  struct chain_string *new_pts;
  new_pts = (struct chain_string *)malloc(sizeof(struct chain_string));
  if (new_pts == NULL) {
    logmsg(LOG_ERR, "new_chain_string() : malloc error");
    return NULL;
  }
  new_pts->value = strdup(value);
  if (new_pts->value == NULL) {
    logmsg(LOG_ERR, "new_chain_string() : strdup error");
    new_pts->length = 0;
  }
  else
    new_pts->length = strlen(value);
  new_pts->previous = NULL;
  new_pts->next = NULL;
  
  return new_pts;
}

struct chain_string *new_chains_string(struct chain_string * first, ...) {
/* -----------------------------------------------------------------------------
 * Create new chain string with several string
 *
 * @param  const char *
 * @return struct chain_string *
 * -------------------------------------------------------------------------- */
  va_list args;
  struct chain_string *pts;
  struct chain_string *result = first;
  va_start(args, first);
    
  while ((pts = va_arg(args, struct chain_string *)) != NULL) {
    append_chain_string(pts, result);
  }
  va_end(args);
  return result;
}

void drop_all_chain_string(struct chain_string *root_chain_string) {
/* -----------------------------------------------------------------------------
 * Destroy a chain string from the specified root 
 *
 * @param  struct chain_string *
 * -------------------------------------------------------------------------- */
  struct chain_string *pts;
  struct chain_string *pts_tmp;
  if (root_chain_string == NULL) return;
  pts = root_chain_string;
  while(pts != NULL) {
    free(pts->value);
    pts_tmp = pts->next;
    free(pts);
    pts = pts_tmp;
  }
}

struct chain_string * add_chain_string(struct chain_string *new_chain_string,
                                       struct chain_string *chain_string_dest,
                                       unsigned int position) {
/* -----------------------------------------------------------------------------
 * Insert a string in a chain string at the given position
 * if the given position is greater thant the chain_string size,
 * the new chain_string will be add to the end 
 *
 * @param  struct chain_string *, the chain string to insert
 * @param  struct chain_string *, where to insert
 * @param  int
 * @return struct chain_string *, the new root chain string
 * A <-> B <-> C <-> D   .... insert T, position 1 
 * A <-> T <-> B <-> C <-> D
 * -------------------------------------------------------------------------- */
  struct chain_string *pts, *pts_last;
  pts = get_chain_string(chain_string_dest, position);
  pts_last = get_last_chain_string(new_chain_string);
  
  new_chain_string->previous = pts->previous;
  pts->previous = pts_last;
  pts_last->next = pts;
  if (new_chain_string->previous != NULL)
    new_chain_string->previous->next = new_chain_string;
  
  return (position == 0) ? new_chain_string : chain_string_dest;
  
}

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

struct chain_string * delete_chain_string_by_value(struct chain_string *chain_string_root,
                                                   const char *value) {
/* -----------------------------------------------------------------------------
 * Delete a chain string by value
 * The first chain string with the given value will be deleted 
 *
 * @param  struct chain_string *, the chain string where to delete
 * @param  const char *
 * @return struct chain_string *, the new root chain string
 * A <-> B <-> C <-> D   .... delete in position 1
 * A <-> C <-> D
 * -------------------------------------------------------------------------- */
  struct chain_string *pts, *new_root;
  pts = get_chain_string_by_value(chain_string_root, value);
  if (pts == NULL) return chain_string_root;
  
  if (pts->previous != NULL)
    pts->previous->next = pts->next;
  if (pts->next != NULL)
    pts->next->previous = pts->previous;
  if (pts == chain_string_root) {
    if (pts->next != NULL)
      new_root = pts->next;
    else
      new_root = NULL;
  }
  else
    new_root = chain_string_root;   
  
  free(pts->value);
  free(pts);
  return new_root;
}

unsigned int replace_chain_string_value(struct chain_string *chain_string_root,
                                        const char *oldvalue,
                                        const char *newvalue) {
/* -----------------------------------------------------------------------------
 * Replace a value by another 
 *
 * @param  struct chain_string *, the chain string where to do the replace
 * @param  const char *, the string to replace
 * @param  const char *, the string replacment
 * @return int
 * -------------------------------------------------------------------------- */
  struct chain_string *pts = NULL;
  pts = get_chain_string_by_value(chain_string_root, oldvalue);
  if (pts == NULL)
    return 0;
  free(pts->value);
  pts->value = strdup(newvalue);
  if (pts->value == NULL)
    return 0;
  return 1;
}

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

struct chain_string *get_chain_string_by_value(struct chain_string *chain_string_root,
                                               const char *value) {
/* -----------------------------------------------------------------------------
 * Get a chain string by value
 *
 * @param  struct chain_string *, the chain string where to search
 * @param  const char *
 * @return struct chain_string * or NULL if not found
 * -------------------------------------------------------------------------- */
  struct chain_string *pts;
  for (pts = chain_string_root; pts != NULL && strcmp(pts->value, value) != 0; pts = pts->next);
  return pts;
}

struct chain_string *get_last_chain_string(struct chain_string *chain_string_root) {
/* -----------------------------------------------------------------------------
 * Get the last chain string
 *
 * @param  struct chain_string *, the chain string where to search
 * @return struct chain_string *
 * -------------------------------------------------------------------------- */
  struct chain_string *pts;
  for (pts = chain_string_root; pts->next != NULL; pts = pts->next);
  return pts;
}

unsigned int get_nb_chain_string(struct chain_string *chain_string_root) {
/* -----------------------------------------------------------------------------
 * Get the number of elements
 *
 * @param  struct chain_string *, the chain string where to search
 * @return int
 * -------------------------------------------------------------------------- */
  struct chain_string *pts;
  unsigned int count = 0;
  for (pts = chain_string_root; pts != NULL; count++, pts = pts->next);
  return count;
}

struct chain_string *explode_to_chain_string(const char * strin, const char * delimiters) {
/* -----------------------------------------------------------------------------
 * Parse a string and explode it to several chain string by delimiters
 *
 * @param  const char *, source string
 * @param  const char *, delimiters
 * @return struct chain_string *
 * -------------------------------------------------------------------------- */
  struct chain_string *pts = NULL;
  char *strin_cp = NULL;
  char *strin_cp_tmp = NULL;
  char *word = NULL;
  char *pts_tok = NULL;

  strin_cp = strdup(strin);
  if (strin_cp == NULL) {
    return NULL;
  }
  strin_cp_tmp = strin_cp;
  word = strtok_r(strin_cp_tmp, delimiters, &pts_tok);
  if (word != NULL) {
    pts = new_chain_string(word);
    while ((word = strtok_r(NULL, delimiters, &pts_tok)) != NULL) {
      append_chain_string(new_chain_string(word), pts);
    }
  }
  else {
    free(strin_cp);
    return NULL;
  }
  free(strin_cp);
	
  return pts;
}

struct chain_string *copy_chain_string(struct chain_string *chain_string_root) {
/* -----------------------------------------------------------------------------
 * Copy chain string
 *
 * @param  struct chain_string *, the chain string where to copy
 * @return struct chain_string *, thz new copy
 * -------------------------------------------------------------------------- */
  struct chain_string *chain_string_copy;
  struct chain_string *pts;
  if (chain_string_root == NULL) return NULL;  
  
  pts = chain_string_root;
  chain_string_copy = new_chain_string((const char *)pts->value);
  pts = pts->next;
  while(pts != NULL) {
    append_chain_string(new_chain_string((const char *)pts->value), chain_string_copy);
    pts = pts->next;
  }
  return chain_string_copy;
}

void append_chain_string(struct chain_string *new_chain_string,
                         struct chain_string *chain_string_dest) {
/* -----------------------------------------------------------------------------
 * Insert a chain string at the end
 *
 * @param  struct chain_string *, the chain string to add
 * @param  struct chain_string *, the root chain string
 * A <-> B <-> C <-> D   .... insert T at the end
 * A <-> B <-> C <-> D <-> T
 * -------------------------------------------------------------------------- */
  struct chain_string *pts;
  
  for (pts = chain_string_dest; pts->next != NULL; pts = pts->next);
  new_chain_string->previous = pts;
  pts->next = new_chain_string;
}

char * chain_string_join(struct chain_string *chain_string_root,
                         const char *glue, unsigned int position) {
/* -----------------------------------------------------------------------------
 * Join all chain string element in one string with the given glue
 *
 * @param  struct chain_string *, the root chain string
 * @param  const char *, glue,
 * @param  int, the offset
 * @return char *, a new string dynamically allocated or NULL
 * -------------------------------------------------------------------------- */
  char *result = NULL, *result_realloc = NULL;
  struct chain_string *pts = NULL;
  unsigned int count = 0;
  int len = 0, total_size = 0, len_glue = 0;
  for (pts = chain_string_root; pts != NULL && count < position; count++, pts = pts->next);
  if (pts == NULL) return NULL;
  
  len_glue = strlen(glue);
  while (pts != NULL) {
    len = strlen(pts->value);
    total_size += len;
    if (pts->next != NULL)
      total_size += len_glue;
    result_realloc = (char *)realloc(result, (total_size + 1) * sizeof(char));
    if (result_realloc == NULL)
      return NULL;
    result = result_realloc; 
    if (pts->next != NULL) {
      memcpy(&result[total_size - len - len_glue], pts->value, len * sizeof(char));
      memcpy(&result[total_size - len_glue], glue, len_glue * sizeof(char));
    }
    else {
      memcpy(&result[total_size - len], pts->value, len * sizeof(char));
      result[total_size] = '\0';
    }
    pts = pts->next;
  }
  return result;
}

void print_chain_string(struct chain_string *chain_string_root) {
/* -----------------------------------------------------------------------------
 * Print a chain string to stdout
 *
 * @param  struct chain_string *, the root chain string
 * -------------------------------------------------------------------------- */
  struct chain_string *pts;
  for (pts = chain_string_root; pts != NULL; pts = pts->next) {
    printf("%s -> ", pts->value);
  }
  printf("\n");
}

void print_reverse_chain_string(struct chain_string *chain_string_root_reverse) {
/* -----------------------------------------------------------------------------
 * Print a chain string to stdout from the end 
 *
 * @param  struct chain_string *, the root chain string
 * -------------------------------------------------------------------------- */
  struct chain_string *pts;
  for (pts = chain_string_root_reverse; pts != NULL; pts = pts->previous) {
    printf("%s -> ", pts->value);
  }
  printf("\n");
}

void print_all_chain_string(struct chain_string *chain_string_root) {
/* -----------------------------------------------------------------------------
 * Print a chain string to stdout from the beginning to the end and from the end
 * to the beginning 
 *
 * @param  struct chain_string *, the root chain string
 * -------------------------------------------------------------------------- */
  struct chain_string *pts = NULL;
  struct chain_string *last_pts = NULL;
  for (pts = chain_string_root; pts != NULL; last_pts = pts, pts = pts->next) {
    printf("%s -> ", pts->value);
  }
  printf("\n");
  
  for (pts = last_pts; pts != NULL; pts = pts->previous) {
    printf("%s -> ", pts->value);
  }
  printf("\n");
}


syntax highlighted by Code2HTML, v. 0.9.1