/*
  POP3 UID database

	Copyright (c) 2010 MAD Partners, Ltd. (rweikusat@mssgmbh.com)

	This file is being published in accordance with the GPLv2 terms
	contained in the COPYING file being part of the fetchmail
	6.3.17 release, including the OpenSSL exemption.
*/
#ifndef fetchmail_uid_db_h
#define fetchmail_uid_db_h

/* If ffs() is missing on, for instance, Solaris, make sure that config.h or fetchmail.h
 * contains #define __EXTENSIONS__ - autoconf should set it in config.h. */
#include "config.h"

/*  includes */
#include <stddef.h>

/*  types */
struct uid_db_record {
    struct uid_db_record *next;
    char *id;
    size_t id_len;

    /*
      num	-	message number assigned by server
      status	-	message status (eg seen, deleted, ...)
      pos	-	position in record list
    */
    unsigned num, status, pos;
};

struct num_ndx {
    /*
      Used to find uid records by message number.

      pos_0_value	-	highest message number
      end_value		-	lowest known message number

      Grows downwards because the fastuidl-code may record
      message numbers in non-ascending order but the
      lookup array should ideally only be large enough to
      store pointers to interesting ('new') messages.
    */
    struct uid_db_record **records;
    unsigned pos_0_value, end_value;
};

struct uid_db
{
    struct pat_node *pat_root;

    struct uid_db_record **records;
    unsigned records_max, records_next;

    struct num_ndx num_ndx;
};

typedef int uid_db_traversal_routine(struct uid_db_record *, void *);

/*  routines */
/**  initialization/ finalization */
void init_uid_db(struct uid_db *db);

void free_uid_db(struct uid_db *db);

static inline void clear_uid_db(struct uid_db *db)
{
    free_uid_db(db);
    init_uid_db(db);
}

/**  message number index handling */
static inline unsigned uid_db_num_ofs(struct num_ndx const *num_ndx, unsigned num)
{
    return num_ndx->pos_0_value - num;
}

void set_uid_db_num(struct uid_db *db, struct uid_db_record *rec,
		    unsigned num);

static inline void set_uid_db_num_pos_0(struct uid_db *db, unsigned num)
{
    db->num_ndx.pos_0_value = num;
    db->num_ndx.end_value = num + 1;
}

void reset_uid_db_nums(struct uid_db *db);

/**  various uidl db manipulatiors */
struct uid_db_record *uid_db_insert(struct uid_db *db,
				    char const *id, unsigned status);

void swap_uid_db_data(struct uid_db *db_0, struct uid_db *db_1);

/**  search routines */
struct uid_db_record *find_uid_by_id(struct uid_db *db, char const *id);

static inline struct uid_db_record *
find_uid_by_num(struct uid_db *db, unsigned num)
{
    struct num_ndx *num_ndx;

    num_ndx = &db->num_ndx;
    return num >= num_ndx->end_value ?
	num_ndx->records[uid_db_num_ofs(num_ndx, num)] : NULL;
}

static inline struct uid_db_record *
find_uid_by_pos(struct uid_db *db, unsigned pos)
{
    return pos < db->records_next ? db->records[pos] : NULL;
}

static inline struct uid_db_record *
first_uid_in_db(struct uid_db *db, char const *id)
{
    return find_uid_by_id(db, id);
}

struct uid_db_record *last_uid_in_db(struct uid_db *db, char const *id);

/**  various accessors */
static inline unsigned uid_db_n_records(struct uid_db const *db)
{
    return db->records_next;
}

/*
  Traverses the struct uid_db records array in insert order,
  invoking the subroutine pointed to by r with a pointer to
  each record and the arg pointer as arguments. If the return
  value of that is non-zero, traverse_uid_db immediately returns
  with this value. Otherwise, zero is returned after the last
  record was visited.

  The uid_db_traversal_routine must not modify the uid_db during
  traversal.
*/
int traverse_uid_db(struct uid_db *db,
		    uid_db_traversal_routine *r, void *arg);

#endif
