"""
NOT USED -- This was an experiment that went nowhere (see ../README)

Make a code fragment that parses GUMS input data using the rudimentary
JSO C parser.

The generated code assumes it's running on a little-endian machine (it
mirrors all the bytes read).  That's kinda unfortunate since the bytes
are mirrored back on dump file generation.
"""

import sys

from gavo import api
from gavo.grammars import directgrammar

import getschema


class Code(object):
	"""The C code in the making.
	"""
	def __init__(self, destTable):
		self.destTable = destTable
		self.currentFuncInd = 0
		self.functionStack = [["int parseValues(PC *ctx)\n{"]]
		self.code = ["""
/* Code generated by bin/mkbooster.py.  Your edits will be clobbered.*/
#include <errno.h>
#include <stdlib.h>
#include "boosterskel.h"
#include "parsejso.h"

#define FP(x) (ctx->tuple+x)

void readNSDoubleAsFloat(PC *ctx, float *fp)
{
	double raw;

  fread(&raw, 1, sizeof(raw), ctx->inFile);
  mirrorBytes((char*)&raw, sizeof(raw));
	*fp = (float)raw;
}
"""]

		self.code.extend(directgrammar.getEnum(self.destTable, None))
		self.code.append("""
void shipout(PC *ctx)
{
	writeTuple(ctx->tuple, QUERY_N_PARS, stdout);
}
""")

	def pushFunction(self):
		self.functionStack.append(["int parseValues_%d(PC *ctx)\n{"%
			self.currentFuncInd])
	
	def popFunction(self):
		self.functionStack[-1].append("  return 0;\n}\n")
		self.code.extend(self.functionStack.pop())
		self.functionStack[-1].extend([
			"  pushValueParser(ctx, parseValues_%d);"%self.currentFuncInd,
			"  parse_object(ctx);",
			"  popValueParser(ctx);",]);
		self.currentFuncInd += 1

	_boostcodes = {
		'D': (8, 'VAL_DOUBLE'),
		'I': (4, 'VAL_INT'),
		'Z': (1, 'VAL_BOOL'),
		'J': (1, 'VAL_BIGINT'),
	}
	def makeParseCodeFor(self, name, typecode):
		name = name.lower()
		destName = "fi_"+name
		curCode = self.functionStack[-1]

		# special handling for double->float downgrading
		if (typecode=='D'
					and self.destTable.getColumnByName(name).type=='real'):
			curCode.append("  FP(%s)->type = VAL_FLOAT;"%destName)
			curCode.append(
				"  readNSDoubleAsFloat(ctx, &(FP(%s)->val.c_float));"%destName)
		elif typecode in self._boostcodes:
			size, type = self._boostcodes[typecode]
			curCode.append("  FP(%s)->type = %s;"%(destName, type))
			curCode.append("  fread(&(FP(%s)->val), 1, %d, ctx->inFile);"%(
				destName, size))
			curCode.append("  mirrorBytes((char*)&(FP(%s)->val), %d);"%(
				destName, size))
		elif typecode=='Ljava/lang/String;':
			curCode.append("  FP(%s)->type = VAL_TEXT;"%destName)
			curCode.append(
				"  FP(%s)->val.c_ptr = parseImmediateString(ctx);"%destName)
		else:
			ddt

	def finish(self):
# TODO: free strings allocated (e.g., by iterating over all strings found
# in ctx->tuple
		self.functionStack[-1].append("  shipout(ctx);  return 0;\n}")
		self.code.extend(self.functionStack.pop())
		parseFctList = ", ".join("parseValues%d"%i
			for i in range(self.currentFuncInd))
		parseFctList = parseFctList+", NULL"

		self.code.extend([
		'void createDumpfile(int argc, char **argv)',
		'{',
		'  PC *ctx;',
		'  char *sn="gaia.cu1.tools.dmimpl.GaiaRootImpl";',
		'  if (!(ctx = getParseContext(argv[1], QUERY_N_PARS, sn, parseValues))) {',
		'    perror("On startup: ");',
		'  }',
		'  if (setjmp(ctx->errJumpEnv)) {',
		'     fprintf(stderr, "Fatal at byte %ld: %s\\n",',
		'        ctx->errPos, ctx->errMsg);',
		'     exit(1);',
		'  }',
		'  drive(ctx, parse_stream);',
		'}'])

	def getCode(self):
		return "\n".join(self.code)


def main():
	rd = api.getRD("gums/q")
	destTable = rd.getById("mw")

	stuff = getschema.getDecodedDataFromGbin("/home/msdemlei/gavo/inputs/gums/data/GUMS-10/MW/N0/N0000000.gbin")
	fieldSequence = getschema.getFieldSequenceForInstance(stuff[0], "obj")
	code = Code(destTable)

	for name, path, type in fieldSequence:
		if name is None:
			if type=="<":
				code.popFunction()
			elif type==">":
				code.pushFunction()
			else:
				ddt
		else:
			code.makeParseCodeFor(name, type)
	code.finish()
	print code.getCode()


if __name__=="__main__":
	main()
