#ifndef SP_CORE_H_
#define SP_CORE_H_

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

#define SP_VERSION_MAJOR 1
#define SP_VERSION_MINOR 1

typedef struct sp sp;
typedef struct spenv spenv;

enum spmagic {
	SPMCUR  = 0x15481936L,
	SPMENV  = 0x06154834L,
	SPMDB   = 0x00fec0feL,
	SPMNONE = 0L
};

typedef enum spmagic spmagic;

enum sptxn {
	SPTSS, SPTMS
};

typedef enum sptxn sptxn;

struct spenv {
	spmagic m;
	spe e;
	int inuse;
	spallocf alloc;
	void *allocarg;
	spcmpf cmp;
	void *cmparg;
	uint32_t flags;
	char *dir;
	int merge;
	uint32_t mergewm;
	uint32_t page;
	uint32_t dbnewsize;
	float dbgrow;
	int gc;
	float gcfactor;
};

struct sp {
	spmagic m;
	spenv *env;
	spa a;
	sprep rep;
	sptxn txn;          /* transaction mode: single or multi-stmt */
	spi *i, i0, i1;
	spi itxn;
	int iskip;          /* skip second index during read */
	uint64_t psn;       /* page sequence number */
	spcat s;
	spbatch lb;         /* log batch related */
	volatile int stop;
	sptask merger;
	sprefset refs;      /* pre allocated key buffer (page merge) */
	spfile lockdb;      /* per-process database lock */
	int lockc;          /* incremental cursor lock */
	spspinlock lockr;   /* repository lock */
	spspinlock locks;   /* space lock */
	spspinlock locki;   /* index lock */
	spe e, em;          /* separate error contexts: fe and merger */
};

int sp_rotate(sp*, spe*);

static inline int sp_active(sp *s) {
	return !s->stop;
}

static inline spi*
sp_ipair(sp *s) {
	return (s->i == &s->i0 ? &s->i1 : &s->i0);
}

static inline spi*
sp_iswap(sp *s) {
	spi *old = s->i;
	s->i = sp_ipair(s);
	return old;
}

static inline void
sp_iskipset(sp *s, int v) {
	sp_lock(&s->locki);
	s->iskip = v;
	sp_unlock(&s->locki);
}

static inline void
sp_glock(sp *s) {
	if (s->lockc > 0)
		return;
	sp_lock(&s->lockr);
	sp_replockall(&s->rep);
	sp_lock(&s->locki);
	sp_lock(&s->locks);
	s->lockc++;
}

static inline void
sp_gunlock(sp *s) {
	s->lockc--;
	if (s->lockc > 0)
		return;
	sp_unlock(&s->locks);
	sp_unlock(&s->locki);
	sp_repunlockall(&s->rep);
	sp_unlock(&s->lockr);
}

static inline int
sp_evalidate(sp *s) {
	return sp_echeck(&s->e) + sp_echeck(&s->em);
}

static inline int
sp_e(sp *s, int type, ...) {
	va_list args;
	va_start(args, type);
	sp_vef(&s->e, type, args);
	va_end(args);
	return -1;
}

static inline int
sp_em(sp *s, int type, ...) {
	va_list args;
	va_start(args, type);
	sp_vef(&s->em, type, args);
	va_end(args);
	return -1;
}

static inline int
sp_ee(spenv *e, int type, ...) {
	va_list args;
	va_start(args, type);
	sp_vef(&e->e, type, args);
	va_end(args);
	return -1;
}

#endif
