#include "pqueue.h"
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include "heapsort.h"

struct _pqelem_t {
  int priority;
  intptr_t data;
};

static int pqelem_highest_cmp(const void *a,const void *b) // {{{
{
  return ((pqelem_t*)a)->priority-((pqelem_t*)b)->priority;
}
// }}}

static int pqelem_lowest_cmp(const void *a,const void *b) // {{{
{
  return ((pqelem_t*)b)->priority-((pqelem_t*)a)->priority;
}
// }}}

static int pqelem_lowest_also_data_cmp(const void *a,const void *b) // {{{
{
  pqelem_t *pa=(pqelem_t*)a,*pb=(pqelem_t*)b;
  const int ret=pb->priority - pa->priority;
  if (ret!=0) {
    return ret;
  }
  return pb->data - pa->data;
}
// }}}


PQUEUE *new_pq(int reservesize,int mode) // {{{
{
  PQUEUE *pqueue=malloc(sizeof(pqueue[0]));
  if (!pqueue) {
    return NULL;
  }
  init_pq(pqueue,reservesize,mode);
  return pqueue;
}
// }}}

void init_pq(PQUEUE *pqueue,int reservesize,int mode) // {{{
{
  if (!pqueue) {
    return;
  }
  if (reservesize<=0) {
    reservesize=10;
  }
  pqueue->size=reservesize;
  pqueue->q=malloc(sizeof(pqueue->q[0])*pqueue->size);
  // q==NULL is error state
  pqueue->len=0;
  switch (mode) {
  case PQ_MODE_LOWEST:
    pqueue->compa=pqelem_lowest_cmp;
    break;
  case PQ_MODE_LOWEST_ALSO_DATAPTR:
    pqueue->compa=pqelem_lowest_also_data_cmp;
    break;
  default:
    assert(0);
  case PQ_MODE_HIGHEST:
    pqueue->compa=pqelem_highest_cmp;
    break;
  }
}
// }}}

void free_pq(PQUEUE *pqueue) // {{{
{
  if (pqueue) {
    free(pqueue->q);
    free(pqueue);
  }
}
// }}}

void free_pq_data(PQUEUE *pqueue) // {{{
{
  if (pqueue) {
    free(pqueue->q);
    pqueue->q=NULL;
  }
}
// }}}

void clear_pq(PQUEUE *pqueue) // {{{
{
  if ( (!pqueue)||(!pqueue->q) ) {
    assert(0);
    return;
  }
  pqueue->len=0;
}
// }}}

void add_pq(PQUEUE *pqueue,int priority,intptr_t data) // {{{
{
  if ( (!pqueue)||(!pqueue->q) ) {
    assert(0);
    return;
  }
  if (pqueue->len>=pqueue->size) {
    pqueue->size*=2;
    pqelem_t *tmp=realloc(pqueue->q,sizeof(pqueue->q[0])*pqueue->size);
    if (!tmp) {
      free(pqueue->q);
      pqueue->q=NULL; // error
      return;
    }
    pqueue->q=tmp;
  }
  pqueue->q[pqueue->len].priority=priority;
  pqueue->q[pqueue->len].data=data;
  pqueue->len++;
  push_heap(pqueue->q,pqueue->len,sizeof(pqueue->q[0]),pqueue->compa);
}
// }}}

int peek_pq(PQUEUE *pqueue) // {{{
{
  if ( (!pqueue)||(!pqueue->q) ) {
    assert(0);
    return INT_MIN;
  }
  if (!pqueue->len) {
    return INT_MIN;
  }
  return pqueue->q[0].priority;
}
// }}}

intptr_t get_pq(PQUEUE *pqueue) // {{{
{
  if ( (!pqueue)||(!pqueue->q) ) {
    assert(0);
    return (intptr_t)NULL;
  }
  if (!pqueue->len) {
    return (intptr_t)NULL;
  }
  pop_heap(pqueue->q,pqueue->len,sizeof(pqueue->q[0]),pqueue->compa);
  pqueue->len--;
  return pqueue->q[pqueue->len].data;
}
// }}}

