#!/usr/bin/perl
use strict; use warnings;
use Data::Dumper; 
use OpenILS::Utils::Fieldmapper;  


if(!$ARGV[1]) {
	print "usage: $0 <header_file> <source_file>\n";
	exit;
}

warn "Generating fieldmapper-c code...\n";

#open(HEADER, ">fieldmapper.h");
#open(SOURCE, ">fieldmapper.c");

print $ARGV[0] . "\n";
print $ARGV[1] . "\n";

open(HEADER, ">$ARGV[0]");
open(SOURCE, ">$ARGV[1]");

my $map = $Fieldmapper::fieldmap;

print HEADER <<C;

#include "objson/object.h"
#include "objson/json_parser.h"
#include "utils.h"

/* Fieldmapper-C.  Autogenerated from fieldmapper-c.pl.\n\nNote that object*'s passed in as
constructor parameters are copied in so the originals will need to be freed*/

/* fm_obj is the generic placeholder object */
struct fm_obj_struct { 
	char* classname; 
	object* array;
};
typedef struct fm_obj_struct fm_obj;

/* takes a json object and builds a fieldmapper object.  the object
	is then cast into an fm_obj and returned.  To verify an object is
	what is expected, check fm_obj_thing->classname. */
fm_obj* fm_factory(object*);

/* builds a new fieldmapper object by class name */
fm_obj* fm_factory_string(char*);

void fm_free(fm_obj* fm);
C

print SOURCE <<C;
#include "fieldmapper.h"

void fm_free(fm_obj* fm) {
	if(!fm) return;
	free_object(fm->array);
	free(fm);
}
C

my $factory_string = <<C;
fm_obj* fm_factory(object* obj) {

	if(!obj) return NULL;
	if(!obj->classname) return NULL;
	fm_obj* fm = NULL;

C

my $fstring_string = <<C;
fm_obj* fm_factory_string(char* string) {
	if(!string) return NULL;
	object* obj = new_object(NULL);
	object* tmp = NULL;
C

for my $object (keys %$map) {

	my $short_name				= $map->{$object}->{hint};
	my $func_header_string	= "";
	my $func_code_string		= "";
	my $constructor_string	= "";

	$factory_string .= <<CODE;
	if(!strcmp(obj->classname,"$short_name"))
		fm = (fm_obj*) fm_new_$short_name(obj);
CODE

	$fstring_string .= <<CODE;
	if(!strcmp(string, "$short_name")) {
		obj->set_class(obj, "$short_name");
CODE

	my $header_string = <<CODE;
struct fm_${short_name}_struct {
	char* classname;
	object* array;
	char*\t\t(*to_string) (struct fm_${short_name}_struct*);
CODE

	$func_header_string .= <<CODE;
char* fm_${short_name}_to_string(fm_$short_name*);
CODE

	my $to_string	= <<CODE;
char* fm_${short_name}_to_string(fm_$short_name* fm) {
	if(!fm || !fm->array) return NULL;
	growing_buffer* buf = buffer_init(256);
	buffer_fadd(buf, "%s => {\\n", fm->classname);
	object* o;
CODE

	$constructor_string .= <<CODE; 
fm_$short_name* fm_new_$short_name(object* obj_array) {

	fm_$short_name* fm = (fm_$short_name*) safe_malloc(sizeof(fm_$short_name));

	/* build an empty array object */
	if(!obj_array) {
		fm->array = new_object(NULL);
		fm->array->is_array = 1;
		fm->array->set_class(fm->array, "$short_name");

	} else {

		/* we don't want invalid objects */
		if (!obj_array->classname) return NULL;
		if(strcmp(obj_array->classname, "$short_name")) return NULL;
	
		char* s = obj_array->to_json(obj_array);
		fm->array = json_parse_string(s);
		free(s);
	}

	fm->classname = fm->array->classname;
	fm->to_string = &fm_${short_name}_to_string;
CODE

	for my $field (keys %{$map->{$object}->{fields}}) {
		my $position = $map->{$object}->{fields}->{$field}->{position};

		$to_string .= <<CODE;
	o = fm->array->get_index(fm->array, $position);
	if(o) {
		char* str = o->to_json(o);
		buffer_fadd(buf, "  $field\\t: %s\\n", str);
		free(str);
	};
CODE
		
		$fstring_string .= <<CODE;
		tmp = new_object(NULL);
		tmp->is_null = 1;
		obj->add_index(obj, $position, tmp);
CODE

		$header_string .= <<CODE;
	object*\t(*get_$field) (struct fm_${short_name}_struct*);
	void\t\t(*set_$field) (struct fm_${short_name}_struct*, object*);
CODE

		$constructor_string .= <<CODE;
	fm->set_$field = &fm_${short_name}_set_$field;
	fm->get_$field = &fm_${short_name}_get_$field;
CODE

		$func_header_string .= <<CODE;
object* fm_${short_name}_get_$field(fm_$short_name*);
void fm_${short_name}_set_$field(fm_$short_name*, object*);
CODE

		$func_code_string .= <<CODE
	object* fm_${short_name}_get_$field(fm_$short_name* fm) {
		if(!fm || !fm->array) return NULL;
		return fm->array->get_index(fm->array, $position);
	}

	void fm_${short_name}_set_$field(fm_$short_name* fm, object* obj) {
		if(!fm || !fm->array || !obj) return;
		fm->array->add_index(fm->array, $position, obj);
	}
CODE

	}

	$fstring_string .= <<CODE;
		return (fm_obj*) fm_new_$short_name(obj);
	}
CODE

	$to_string .= <<CODE;
	buffer_add(buf,"}\\n");
	char* b = buffer_data(buf);
	buffer_free(buf);
	return b;
}
CODE

	$constructor_string .= "\treturn fm;\n}\n";
	$header_string .= "};\n";
	$func_code_string = $constructor_string . $to_string . $func_code_string;

	$header_string .= "typedef struct fm_$short_name"."_struct fm_$short_name;\n\n";
	$header_string .= "fm_$short_name* fm_new_$short_name(object*);\n";
	$header_string .= "$func_header_string\n";

	print HEADER "\n$header_string\n";
	print SOURCE "\n$func_code_string\n";


}

$factory_string .= "\treturn fm;\n}\n";
#$fstring_string .= "\treturn NULL;\n}\n";
$fstring_string .= <<CODE;
	free_object(obj);
	return NULL;
}
CODE

print SOURCE "\n$factory_string\n";
print SOURCE "\n$fstring_string\n";

close HEADER;
close SOURCE;

warn  "done\n";

