#ifndef _inlinequeue_h_INCLUDED
#define _inlinequeue_h_INCLUDED

#include "internal.h"
#include "logging.h"

static inline void
kissat_update_queue (kissat * solver, const links * links, unsigned idx)
{
  assert (!DISCONNECTED (idx));
  const unsigned stamp = links[idx].stamp;
  LOG ("queue updated to %s stamped %u", LOGVAR (idx), stamp);
  solver->queue.search.idx = idx;
  solver->queue.search.stamp = stamp;
}

static inline void
kissat_enqueue_links (kissat * solver, unsigned i, links * links,
		      queue * queue)
{
  struct links *p = links + i;
  assert (DISCONNECTED (p->prev));
  assert (DISCONNECTED (p->next));
  const unsigned j = p->prev = queue->last;
  queue->last = i;
  if (DISCONNECTED (j))
    queue->first = i;
  else
    {
      struct links *l = links + j;
      assert (DISCONNECTED (l->next));
      l->next = i;
    }
  if (queue->stamp == UINT_MAX)
    {
      kissat_reassign_queue_stamps (solver);
      assert (p->stamp == queue->stamp);
    }
  else
    p->stamp = ++queue->stamp;
}

static inline void
kissat_dequeue_links (unsigned i, links * links, queue * queue)
{
  struct links *l = links + i;
  const unsigned j = l->prev, k = l->next;
  l->prev = l->next = DISCONNECT;
  if (DISCONNECTED (j))
    {
      assert (queue->first == i);
      queue->first = k;
    }
  else
    {
      struct links *p = links + j;
      assert (p->next == i);
      p->next = k;
    }
  if (DISCONNECTED (k))
    {
      assert (queue->last == i);
      queue->last = j;
    }
  else
    {
      struct links *n = links + k;
      assert (n->prev == i);
      n->prev = j;
    }
}

static inline void
kissat_enqueue (kissat * solver, unsigned idx)
{
  assert (idx < solver->vars);
  links *links = solver->links, *l = links + idx;
  l->prev = l->next = DISCONNECT;
  kissat_enqueue_links (solver, idx, links, &solver->queue);
  LOG ("enqueued %s stamped %u", LOGVAR (idx), l->stamp);
  if (!VALUE (LIT (idx)))
    kissat_update_queue (solver, links, idx);
  kissat_check_queue (solver);
}

static inline void
kissat_dequeue (kissat * solver, unsigned idx)
{
  assert (idx < solver->vars);
  LOG ("dequeued %s", LOGVAR (idx));
  links *links = solver->links;
  if (solver->queue.search.idx == idx)
    {
      struct links *l = links + idx;
      unsigned search = l->next;
      if (search == DISCONNECT)
	search = l->prev;
      if (search == DISCONNECT)
	{
	  solver->queue.search.idx = DISCONNECT;
	  solver->queue.search.stamp = 0;
	}
      else
	kissat_update_queue (solver, links, search);
    }
  kissat_dequeue_links (idx, links, &solver->queue);
  kissat_check_queue (solver);
}

static inline void
kissat_move_to_front (kissat * solver, unsigned idx)
{
  queue *queue = &solver->queue;
  links *links = solver->links;
  if (idx == queue->last)
    {
      assert (DISCONNECTED (links[idx].next));
      return;
    }
  assert (idx < solver->vars);
  const value tmp = VALUE (LIT (idx));
  if (tmp && queue->search.idx == idx)
    {
      unsigned prev = links[idx].prev;
      if (!DISCONNECTED (prev))
	kissat_update_queue (solver, links, prev);
      else
	{
	  unsigned next = links[idx].next;
	  assert (!DISCONNECTED (next));
	  kissat_update_queue (solver, links, next);
	}
    }
  kissat_dequeue_links (idx, links, queue);
  kissat_enqueue_links (solver, idx, links, queue);
  LOG ("moved-to-front %s stamped %u", LOGVAR (idx), LINK (idx).stamp);
  if (!tmp)
    kissat_update_queue (solver, links, idx);
  kissat_check_queue (solver);
}

#endif
