/*
 * Part of Very Secure FTPd
 * Licence: GPL v2
 * Author: Chris Evans
 * strlist.c
 */

/* Anti-lamer measures deployed, sir! */
#define PRIVATE_HANDS_OFF_alloc_len alloc_len
#define PRIVATE_HANDS_OFF_list_len list_len
#define PRIVATE_HANDS_OFF_p_nodes p_nodes
#include "strlist.h"

#include "str.h"
#include "utility.h"
#include "sysutil.h"

struct mystr_list_node
{
  struct mystr str;
  struct mystr sort_key_str;
};

/* File locals */
static struct mystr s_null_str;

static int sort_compare_func(const void* p1, const void* p2);
static int sort_compare_func_reverse(const void* p1, const void* p2);
static int sort_compare_common(const void* p1, const void* p2, int reverse);

void
str_list_free(struct mystr_list* p_list)
{
  unsigned int i;
  for (i=0; i < p_list->list_len; ++i)
  {
    str_free(&p_list->p_nodes[i].str);
    str_free(&p_list->p_nodes[i].sort_key_str);
  }
  p_list->list_len = 0;
  p_list->alloc_len = 0;
  if (p_list->p_nodes)
  {
    vsf_sysutil_free(p_list->p_nodes);
    p_list->p_nodes = 0;
  }
}

int
str_list_get_length(const struct mystr_list* p_list)
{
  return p_list->list_len;
}

int
str_list_contains_str(const struct mystr_list* p_list,
                      const struct mystr* p_str)
{
  unsigned int i;
  for (i=0; i < p_list->list_len; ++i)
  {
    if (str_equal(p_str, &p_list->p_nodes[i].str))
    {
      return 1;
    }
  }
  return 0;
}

void
str_list_add(struct mystr_list* p_list, const struct mystr* p_str,
             const struct mystr* p_sort_key_str)
{
  struct mystr_list_node* p_node;
  /* Expand the node allocation if we have to */
  if (p_list->list_len == p_list->alloc_len)
  {
    if (p_list->alloc_len == 0)
    {
      p_list->alloc_len = 32;
      p_list->p_nodes = vsf_sysutil_malloc(p_list->alloc_len *
                                           sizeof(struct mystr_list_node));
    }
    else
    {
      p_list->alloc_len *= 2;
      p_list->p_nodes = vsf_sysutil_realloc(p_list->p_nodes,
                                            p_list->alloc_len *
                                            sizeof(struct mystr_list_node));
    }
  }
  p_node = &p_list->p_nodes[p_list->list_len];
  p_node->str = s_null_str;
  p_node->sort_key_str = s_null_str;
  str_copy(&p_node->str, p_str);
  if (p_sort_key_str)
  {
    str_copy(&p_node->sort_key_str, p_sort_key_str);
  }
  p_list->list_len++;
}

void
str_list_sort(struct mystr_list* p_list, int reverse)
{
  if (!reverse)
  {
    vsf_sysutil_qsort(p_list->p_nodes, p_list->list_len,
                      sizeof(struct mystr_list_node), sort_compare_func);
  }
  else
  {
    vsf_sysutil_qsort(p_list->p_nodes, p_list->list_len,
                      sizeof(struct mystr_list_node),
                      sort_compare_func_reverse);
  }
}

static int
sort_compare_func(const void* p1, const void* p2)
{
  return sort_compare_common(p1, p2, 0);
}

static int
sort_compare_func_reverse(const void* p1, const void* p2)
{
  return sort_compare_common(p1, p2, 1);
}

static int
sort_compare_common(const void* p1, const void* p2, int reverse)
{
  const struct mystr* p_cmp1;
  const struct mystr* p_cmp2;
  const struct mystr_list_node* p_node1 = (const struct mystr_list_node*) p1;
  const struct mystr_list_node* p_node2 = (const struct mystr_list_node*) p2;
  if (!str_isempty(&p_node1->sort_key_str))
  {
    p_cmp1 = &p_node1->sort_key_str;
  }
  else
  {
    p_cmp1 = &p_node1->str;
  }
  if (!str_isempty(&p_node2->sort_key_str))
  {
    p_cmp2 = &p_node2->sort_key_str;
  }
  else
  {
    p_cmp2 = &p_node2->str;
  }

  if (reverse)
  {
    return str_strcmp(p_cmp2, p_cmp1);
  }
  else
  {
    return str_strcmp(p_cmp1, p_cmp2);
  }
}

const struct mystr*
str_list_get_pstr(const struct mystr_list* p_list, unsigned int indexx)
{
  if (indexx >= p_list->list_len)
  {
    bug("indexx out of range in str_list_get_str");
  }
  return &p_list->p_nodes[indexx].str;
}

