#include "stdinc.h"
#include "dat.h"
#include "fns.h"

int			queueWrites = 0;

static Packet		*readILump(Lump *u, IAddr *ia, u8int *score, int rac);

Packet*
readLump(u8int *score, int type, u32int size)
{
	Lump *u;
	Packet *p;
	IAddr ia;
	u32int n;
	int rac;

	vtLock(stats.lock);
	stats.lumpReads++;
	vtUnlock(stats.lock);
	u = lookupLump(score, type);
	if(u->data != nil){
		n = packetSize(u->data);
		if(n > size){
			setErr(EOk, "read too small: asked for %d need at least %d", size, n);
			putLump(u);

			return nil;
		}
		p = packetDup(u->data, 0, n);
		putLump(u);

		return p;
	}

	if(!lookupScore(score, type, &ia, &rac)){
		//ZZZ place to check for someone trying to guess scores
		setErr(EOk, "no block with that score exists");

		putLump(u);
		return nil;
	}
	if(ia.size > size){
		setErr(EOk, "read too small 1: asked for %d need at least %d", size, ia.size);

		putLump(u);
		return nil;
	}

	p = readILump(u, &ia, score, rac);
	putLump(u);

	return p;
}

/*
 * save away a lump, and return it's score.
 * doesn't store duplicates, but checks that the data is really the same.
 */
int
writeLump(Packet *p, u8int *score, int type, u32int creator)
{
	Lump *u;
	int ok;

	vtLock(stats.lock);
	stats.lumpWrites++;
	vtUnlock(stats.lock);

	packetSha1(p, score);

	u = lookupLump(score, type);
	if(u->data != nil){
		ok = 1;
		if(packetCmp(p, u->data) != 0){
			setErr(EStrange, "score collision");
			ok = 0;
		}
		packetFree(p);
		putLump(u);
		return ok;
	}

	if(queueWrites)
		return queueWrite(u, p, creator);

	ok = writeQLump(u, p, creator);

	putLump(u);
	return ok;
}

int
writeQLump(Lump *u, Packet *p, int creator)
{
	ZBlock *flat;
	Packet *old;
	IAddr ia;
	int ok;
	int rac;

	if(lookupScore(u->score, u->type, &ia, &rac)){
		/*
		 * if the read fails,
		 * assume it was corrupted data and store the block again
		 */
		old = readILump(u, &ia, u->score, rac);
		if(old != nil){
			ok = 1;
			if(packetCmp(p, old) != 0){
				setErr(EStrange, "score collision");
				ok = 0;
			}
			packetFree(p);
			packetFree(old);

			return ok;
		}
		logErr(EAdmin, "writelump: read %V failed, rewriting: %R\n", u->score);
	}

	flat = packet2ZBlock(p, packetSize(p));
	ok = storeClump(mainIndex, flat, u->score, u->type, creator, &ia);
	freeZBlock(flat);
	if(ok)
		ok = insertScore(u->score, &ia, 1);
	if(ok)
		insertLump(u, p);
	else
		packetFree(p);

	return ok;
}

static void
readAhead(u64int a, Arena *arena, u64int aa, int n)
{
	u8int buf[ClumpSize];
	Clump cl;
	IAddr ia;

	while(n > 0) {
		if (aa >= arena->used)
			break;
		if(readArena(arena, aa, buf, ClumpSize) < ClumpSize)
			break;
		if(!unpackClump(&cl, buf))
			break;
		ia.addr = a;
		ia.type = cl.info.type;
		ia.size = cl.info.uncsize;
		ia.blocks = (cl.info.size + ClumpSize + (1 << ABlockLog) - 1) >> ABlockLog;
		insertScore(cl.info.score, &ia, 0);
		a += ClumpSize + cl.info.size;
		aa += ClumpSize + cl.info.size;
		n--;
	}
}

static Packet*
readILump(Lump *u, IAddr *ia, u8int *score, int rac)
{
	Arena *arena;
	ZBlock *zb;
	Packet *p, *pp;
	Clump cl;
	u64int a, aa;
	u8int sc[VtScoreSize];

	arena = amapItoA(mainIndex, ia->addr, &aa);
	if(arena == nil)
		return nil;

	zb = loadClump(arena, aa, ia->blocks, &cl, sc, paranoid);
	if(zb == nil)
		return nil;

	if(ia->size != cl.info.uncsize){
		setErr(EInconsist, "index and clump size mismatch");
		freeZBlock(zb);
		return nil;
	}
	if(ia->type != cl.info.type){
		setErr(EInconsist, "index and clump type mismatch");
		freeZBlock(zb);
		return nil;
	}
	if(!scoreEq(score, sc)){
		setErr(ECrash, "score mismatch");
		freeZBlock(zb);
		return nil;
	}

	if(rac == 0) {
		a = ia->addr + ClumpSize + cl.info.size;
		aa += ClumpSize + cl.info.size;
		readAhead(a, arena, aa, 20);
	}

	p = zblock2Packet(zb, cl.info.uncsize);
	freeZBlock(zb);
	pp = packetDup(p, 0, packetSize(p));
	insertLump(u, pp);
	return p;
}
