/*
 * Copyright notice from original mutt:
 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
 *
 * This file is part of mutt-ng, see http://www.muttng.org/.
 * It's licensed under the GNU General Public License,
 * please see the file GPL in the top level source directory.
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "mutt.h"
#include "sort.h"
#include "thread.h"
#include "mutt_idna.h"

#include "lib/str.h"
#include "lib/intl.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

#define SORTCODE(x) (Sort & SORT_REVERSE) ? -(x) : x

/* function to use as discriminator when normal sort method is equal */
static sort_t *AuxSort = NULL;

#define AUXSORT(code,a,b) if (!code && AuxSort && !option(OPTAUXSORT)) { \
  set_option(OPTAUXSORT); \
  code = AuxSort(a,b); \
  unset_option(OPTAUXSORT); \
} \
if (!code) \
  code = (*((HEADER **)a))->index - (*((HEADER **)b))->index;

int compare_score (const void *a, const void *b)
{
  HEADER **pa = (HEADER **) a;
  HEADER **pb = (HEADER **) b;
  int result = (*pb)->score - (*pa)->score;     /* note that this is reverse */

  AUXSORT (result, a, b);
  return (SORTCODE (result));
}

int compare_size (const void *a, const void *b)
{
  HEADER **pa = (HEADER **) a;
  HEADER **pb = (HEADER **) b;
  int result = (*pa)->content->length - (*pb)->content->length;

  AUXSORT (result, a, b);
  return (SORTCODE (result));
}

int compare_date_sent (const void *a, const void *b)
{
  HEADER **pa = (HEADER **) a;
  HEADER **pb = (HEADER **) b;
  int result = (*pa)->date_sent - (*pb)->date_sent;

  AUXSORT (result, a, b);
  return (SORTCODE (result));
}

int compare_subject (const void *a, const void *b)
{
  HEADER **pa = (HEADER **) a;
  HEADER **pb = (HEADER **) b;
  int rc;

  if (!(*pa)->env->real_subj) {
    if (!(*pb)->env->real_subj)
      rc = compare_date_sent (pa, pb);
    else
      rc = -1;
  }
  else if (!(*pb)->env->real_subj)
    rc = 1;
  else
    rc = str_casecmp ((*pa)->env->real_subj, (*pb)->env->real_subj);
  AUXSORT (rc, a, b);
  return (SORTCODE (rc));
}

const char *mutt_get_name (ADDRESS * a)
{
  ADDRESS *ali;
  const char *name = "";

  if (a) {
    if (option (OPTREVALIAS) && (ali = alias_reverse_lookup (a))
        && ali->personal)
      name = ali->personal;
    else if (a->personal)
      name = a->personal;
    else if (a->mailbox)
      name = (mutt_addr_for_display (a));
  }
  /* don't return NULL to avoid segfault when printing/comparing */
  return name;
}

int compare_to (const void *a, const void *b)
{
  HEADER **ppa = (HEADER **) a;
  HEADER **ppb = (HEADER **) b;
  char fa[1024];
  char fb[1024];
  int result;

  /* mutt_get_name() will sometimes return a pointer to a static buffer.
   * On the next call that pointer may get smashed so we copy the return value
   * to our own memory space. */

  strncpy (fa, mutt_get_name ((*ppa)->env->to), sizeof (fa));
  fa[sizeof (fa) - 1] = '\0';

  strncpy (fb, mutt_get_name ((*ppb)->env->to), sizeof (fb));
  fb[sizeof (fb) - 1] = '\0';

  result = str_casecmp (fa, fb);
  AUXSORT (result, a, b);
  return (SORTCODE (result));
}

int compare_from (const void *a, const void *b)
{
  HEADER **ppa = (HEADER **) a;
  HEADER **ppb = (HEADER **) b;
  char fa[1024];
  char fb[1024];
  int result;

  /* mutt_get_name() will sometimes return a pointer to a static buffer.
   * On the next call that pointer may get smashed so we copy the return value
   * to our own memory space. */

  strncpy (fa, mutt_get_name ((*ppa)->env->from), sizeof (fa));
  fa[sizeof (fa) - 1] = '\0';

  strncpy (fb, mutt_get_name ((*ppb)->env->from), sizeof (fb));
  fb[sizeof (fb) - 1] = '\0';

  result = str_casecmp (fa, fb);
  AUXSORT (result, a, b);
  return (SORTCODE (result));
}

int compare_date_received (const void *a, const void *b)
{
  HEADER **pa = (HEADER **) a;
  HEADER **pb = (HEADER **) b;
  int result = (*pa)->received - (*pb)->received;

  AUXSORT (result, a, b);
  return (SORTCODE (result));
}

int compare_order (const void *a, const void *b)
{
  HEADER **ha = (HEADER **) a;
  HEADER **hb = (HEADER **) b;

#ifdef USE_NNTP
  if ((*ha)->article_num && (*hb)->article_num) {
    int result = (*ha)->article_num - (*hb)->article_num;

    AUXSORT (result, a, b);
    return (SORTCODE (result));
  }
  else
#endif
    /* no need to auxsort because you will never have equality here */
    return (SORTCODE ((*ha)->index - (*hb)->index));
}

int compare_spam (const void *a, const void *b)
{
  HEADER **ppa = (HEADER **) a;
  HEADER **ppb = (HEADER **) b;
  char *aptr, *bptr;
  int ahas, bhas;
  int result = 0;

  /* Firstly, require spam attributes for both msgs */
  /* to compare. Determine which msgs have one.     */
  ahas = (*ppa)->env && (*ppa)->env->spam;
  bhas = (*ppb)->env && (*ppb)->env->spam;

  /* If one msg has spam attr but other does not, sort the one with first. */
  if (ahas && !bhas)
    return (SORTCODE (1));
  if (!ahas && bhas)
    return (SORTCODE (-1));

  /* Else, if neither has a spam attr, presume equality. Fall back on aux. */
  if (!ahas && !bhas) {
    AUXSORT (result, a, b);
    return (SORTCODE (result));
  }


  /* Both have spam attrs. */

  /* preliminary numeric examination */
  result = (strtoul ((*ppa)->env->spam->data, &aptr, 10) -
            strtoul ((*ppb)->env->spam->data, &bptr, 10));

  /* If either aptr or bptr is equal to data, there is no numeric    */
  /* value for that spam attribute. In this case, compare lexically. */
  if ((aptr == (*ppa)->env->spam->data) || (bptr == (*ppb)->env->spam->data))
    return (SORTCODE (str_cmp (aptr, bptr)));

  /* Otherwise, we have numeric value for both attrs. If these values */
  /* are equal, then we first fall back upon string comparison, then  */
  /* upon auxiliary sort.                                             */
  if (result == 0) {
    result = str_cmp (aptr, bptr);
    if (result == 0)
      AUXSORT (result, a, b);
  }

  return (SORTCODE (result));
}

sort_t *mutt_get_sort_func (int method)
{
  switch (method & SORT_MASK) {
  case SORT_RECEIVED:
    return (compare_date_received);
  case SORT_ORDER:
    return (compare_order);
  case SORT_DATE:
    return (compare_date_sent);
  case SORT_SUBJECT:
    return (compare_subject);
  case SORT_FROM:
    return (compare_from);
  case SORT_SIZE:
    return (compare_size);
  case SORT_TO:
    return (compare_to);
  case SORT_SCORE:
    return (compare_score);
  case SORT_SPAM:
    return (compare_spam);
  default:
    return (NULL);
  }
  /* not reached */
}

void mutt_sort_headers (CONTEXT * ctx, int init)
{
  int i;
  HEADER *h;
  THREAD *thread, *top;
  sort_t *sortfunc;

  unset_option (OPTNEEDRESORT);

  if (!ctx)
    return;

  if (!ctx->msgcount) {
    /* this function gets called by mutt_sync_mailbox(), which may have just
     * deleted all the messages.  the virtual message numbers are not updated
     * in that routine, so we must make sure to zero the vcount member.
     */
    ctx->vcount = 0;
    mutt_clear_threads (ctx);
    return;                     /* nothing to do! */
  }

  if (!ctx->quiet)
    mutt_message _("Sorting mailbox...");

  if (option (OPTNEEDRESCORE) && option (OPTSCORE)) {
    for (i = 0; i < ctx->msgcount; i++)
      mutt_score_message (ctx, ctx->hdrs[i], 1);
  }
  unset_option (OPTNEEDRESCORE);

  if (option (OPTRESORTINIT)) {
    unset_option (OPTRESORTINIT);
    init = 1;
  }

  if (init && ctx->tree)
    mutt_clear_threads (ctx);

  if ((Sort & SORT_MASK) == SORT_THREADS) {
    AuxSort = NULL;
    /* if $sort_aux changed after the mailbox is sorted, then all the
       subthreads need to be resorted */
    if (option (OPTSORTSUBTHREADS)) {
      i = Sort;
      Sort = SortAux;
      if (ctx->tree)
        ctx->tree = mutt_sort_subthreads (ctx->tree, 1);
      Sort = i;
      unset_option (OPTSORTSUBTHREADS);
    }
    mutt_sort_threads (ctx, init);
  }
  else if ((sortfunc = mutt_get_sort_func (Sort)) == NULL ||
           (AuxSort = mutt_get_sort_func (SortAux)) == NULL) {
    mutt_error _("Could not find sorting function! [report this bug]");

    mutt_sleep (1);
    return;
  }
  else
    qsort ((void *) ctx->hdrs, ctx->msgcount, sizeof (HEADER *), sortfunc);

  /* adjust the virtual message numbers */
  ctx->vcount = 0;
  for (i = 0; i < ctx->msgcount; i++) {
    HEADER *cur = ctx->hdrs[i];

    if (cur->virtual != -1
        || (cur->collapsed && (!ctx->pattern || cur->limited))) {
      cur->virtual = ctx->vcount;
      ctx->v2r[ctx->vcount] = i;
      ctx->vcount++;
    }
    cur->msgno = i;
  }

  /* re-collapse threads marked as collapsed */
  if ((Sort & SORT_MASK) == SORT_THREADS) {
    top = ctx->tree;
    while ((thread = top) != NULL) {
      while (!thread->message)
        thread = thread->child;
      h = thread->message;

      if (h->collapsed)
        mutt_collapse_thread (ctx, h);
      top = top->next;
    }
    mutt_set_virtual (ctx);
  }

  if (!ctx->quiet)
    mutt_clear_error ();
}


syntax highlighted by Code2HTML, v. 0.9.1