#include "lib9.h"
#include "logfs.h"
#include "nandfs.h"
#include "local.h"

uchar _nandfsvalidtags[] = {
	LogfsTnone,
	LogfsTboot,
	LogfsTlog,
	LogfsTdata,
};

int _nandfsvalidtagscount = nelem(_nandfsvalidtags);

static int
l2(long n)
{
	int i;
	for (i = 0; i < 32; i++)
		if ((1 << i) >= n)
			return i;
	return 0;
}

char *
nandfsinit(void *magic, long rawsize, long rawblocksize,
	char *(*read)(void *magic, void *buf, long nbytes, ulong offset),
	char *(*write)(void *magic, void *buf, long nbytes, ulong offset),
	char *(*erase)(void *magic, long blockaddr),
	char *(*sync)(void *magic),
	LogfsLowLevel **llp)
{
	Nandfs *nandfs;
	nandfs = nandfsrealloc(nil, sizeof(*nandfs));
	if (nandfs == nil)
		return Enomem;
	if (rawblocksize % NandfsFullSize)
		return "unsupported block size";
	if (rawsize % rawblocksize)
		return "size not multiple of block size";
	nandfs->read = read;
	nandfs->write = write;
	nandfs->erase = erase;
	nandfs->sync = sync;
	nandfs->magic = magic;
	nandfs->limitblock = rawsize / rawblocksize;
//print("rawsize %ld\n", rawsize);
//print("rawblocksize %ld\n", rawblocksize);
//print("limitblock %ld\n", nandfs->limitblock);
	nandfs->rawblocksize = rawblocksize;
	/* fill in upper interface */
	nandfs->ll.pathbits = NandfsPathBits;
	nandfs->ll.blocks = 0;
	nandfs->ll.l2pagesize = NandfsL2PageSize;
	nandfs->ll.l2pagesperblock = l2(rawblocksize / NandfsFullSize);
	nandfs->ll.open = (LOGFSOPENFN *)nandfsopen;
	nandfs->ll.getblocktag = (LOGFSGETBLOCKTAGFN *)nandfsgettag;
	nandfs->ll.setblocktag = (LOGFSSETBLOCKTAGFN *)nandfssettag;
	nandfs->ll.getblockpath = (LOGFSGETBLOCKPATHFN *)nandfsgetpath;
	nandfs->ll.setblockpath = (LOGFSSETBLOCKPATHFN *)nandfssetpath;
	nandfs->ll.getblockpartialformatstatus = (LOGFSGETBLOCKPARTIALFORMATSTATUSFN *)nandfsgetblockpartialformatstatus;
	nandfs->ll.findfreeblock = (LOGFSFINDFREEBLOCKFN *)nandfsfindfreeblock;
	nandfs->ll.readpagerange = (LOGFSREADPAGERANGEFN *)nandfsreadpagerange;
	nandfs->ll.writepage = (LOGFSWRITEPAGEFN *)nandfswritepage;
	nandfs->ll.readblock = (LOGFSREADBLOCKFN *)nandfsreadblock;
	nandfs->ll.writeblock = (LOGFSWRITEBLOCKFN *)nandfswriteblock;
	nandfs->ll.eraseblock = (LOGFSERASEBLOCKFN *)nandfseraseblock;
	nandfs->ll.formatblock = (LOGFSFORMATBLOCKFN *)nandfsformatblock;
	nandfs->ll.reformatblock = (LOGFSREFORMATBLOCKFN *)nandfsreformatblock;
	nandfs->ll.markblockbad = (LOGFSMARKBLOCKBADFN *)nandfsmarkblockbad;
	nandfs->ll.getbaseblock = (LOGFSGETBASEBLOCKFN *)nandfsgetbaseblock;
	nandfs->ll.getblocksize = (LOGFSGETBLOCKSIZEFN *)nandfsgetblocksize;
	nandfs->ll.calcrawaddress = (LOGFSCALCRAWADDRESSFN *)nandfscalcrawaddress;
	nandfs->ll.getblockstatus = (LOGFSGETBLOCKSTATUSFN *)nandfsgetblockstatus;
	nandfs->ll.calcformat = (LOGFSCALCFORMATFN *)nandfscalcformat;
	nandfs->ll.getopenstatus = (LOGFSGETOPENSTATUSFN *)nandfsgetopenstatus;
	nandfs->ll.free = (LOGFSFREEFN *)nandfsfree;
	nandfs->ll.sync = (LOGFSSYNCFN *)nandfssync;
	*llp = (LogfsLowLevel *)nandfs;
	return nil;
}

void
nandfsfree(Nandfs *nandfs)
{
	if (nandfs) {
		nandfsfreemem(nandfs->blockdata);
		nandfsfreemem(nandfs);
	}
}

void
nandfssetmagic(Nandfs *nandfs, void *magic)
{
	nandfs->magic = magic;
}

char *
nandfssync(Nandfs *nandfs)
{
	return (*nandfs->sync)(nandfs->magic);
}
