#ifndef SP_I_H_
#define SP_I_H_

/*
 * sophia database
 * sphia.org
 *
 * Copyright (c) Dmitry Simonenko
 * BSD License
*/

typedef struct spipage spipage;
typedef struct spi spi;
typedef struct spii spii;

struct spipage {
	uint16_t count;
	spv *i[];
} sppacked;

struct spi {
	spa *a;
	int pagesize;
	spipage **i;
	uint32_t itop;
	uint32_t icount;
	uint32_t count;
	spcmpf cmp;
	void *cmparg;
};

struct spii {
	spi *i;
	long long p, n;
};

int sp_iinit(spi*, spa*, int, spcmpf, void*);
void sp_ifree(spi*);
int sp_itruncate(spi*);
int sp_ireset(spi*);
int sp_isetorget(spi *i, spv*, spii*);
int sp_idelraw(spi*, char*, int, spv**);
spv *sp_igetraw(spi*, char*, int);

static inline int
sp_idel(spi *i, spv *v, spv **old) {
	return sp_idelraw(i, v->key, v->size, old);
}

static inline spv*
sp_iget(spi *i, spv *v) {
	return sp_igetraw(i, v->key, v->size);
}

static inline void*
sp_imax(spi *i) {
	if (spunlikely(i->count == 0))
		return NULL;
	return i->i[i->icount-1]->i[i->i[i->icount-1]->count-1];
}

static inline void
sp_ifirst(spii *it) {
	it->p = 0;
	it->n = 0;
}

static inline void
sp_ilast(spii *it) {
	it->p = it->i->icount - 1;
	it->n = it->i->i[it->i->icount - 1]->count - 1;
}

static inline void
sp_iopen(spii *it, spi *i) {
	it->i = i;
	sp_ifirst(it);
}

static inline int
sp_ihas(spii *it) {
	return (it->p >= 0 && it->n >= 0) &&
	       (it->p < it->i->icount) &&
	       (it->n < it->i->i[it->p]->count);
}

static inline void
sp_ivalset(spii *it, spv *v) {
	it->i->i[it->p]->i[it->n] = v;
}

static inline spv*
sp_ival(spii *it) {
	if (spunlikely(! sp_ihas(it)))
		return NULL;
	return it->i->i[it->p]->i[it->n];
}

static inline int
sp_inext(spii *it) {
	if (spunlikely(! sp_ihas(it)))
		return 0;
	it->n++;
	while (it->p < it->i->icount) {
		spipage *p = it->i->i[it->p];
		if (spunlikely(it->n >= p->count)) {
			it->p++;
			it->n = 0;
			continue;
		}
		return 1;
	}
	return 0;
}

static inline int
sp_iprev(spii *it) {
	if (spunlikely(! sp_ihas(it)))
		return 0;
	it->n--;
	while (it->p >= 0) {
		if (spunlikely(it->n < 0)) {
			if (it->p == 0)
				return 0;
			it->p--;
			it->n = it->i->i[it->p]->count-1;
			continue;
		}
		return 1;
	}
	return 0;
}

static inline void
sp_iinv(spi *i, spii *ii) {
	ii->i = i;
	ii->p = -1;
	ii->n = -1;
}

int sp_ilte(spi*, spii*, char*, int);
int sp_igte(spi*, spii*, char*, int);

static inline int
sp_iset(spi *i, spv *v, spv **old)
{
	spii pos;
	int rc = sp_isetorget(i, v, &pos);
	if (splikely(rc <= 0))
		return rc;
	*old = sp_ival(&pos);
	sp_ivalset(&pos, v);
	return 1;
}

#endif
