// executor callbacks of dynamic form

var exec_field_current;
var exec_emb_tag;

function __hlp_exec_get_field(executor, key, subkey) {
	var id;

	id = form_field_types[key];
	if(id == "i!!" || id == "z!" || id == "s!" || id == "a!")
		return executor.error("cannot create data type for abstract input '"+id+"'");
	
	/* array type */
	if(id == "ab" || id == "an" || id == "as") {
		if(!subkey)
			return executor.error("you must specify a sub-key to access <"+key+">");
		if(id == "an")
			return new jsexecutor_type_float(load(ffe(key+"["+subkey+"]")));
		else
			return new jsexecutor_type_string(load(ffe(key+"["+subkey+"]")));
	} 
	
	/* scalar type */
	if(subkey)
		return executor.error("you must not specify a sub-key to access <"+key+">");
	if(id == "n")
		return new jsexecutor_type_float(load(ffe(key)));

	return new jsexecutor_type_string(load(ffe(key)));
}

function exec_callback_value(nparm, executor) {
	var arg, key, subkey;

	if(nparm > 1)
		return executor.error("wrong parameter count for \"value\"-function");
	key = exec_field_current+exec_emb_tag;
	subkey = "";
	if(nparm == 1) {
		arg = executor.stack_pop();
		subkey = arg.value("string");
	} 
	return __hlp_exec_get_field(executor, key, subkey);
}

function exec_callback_field(nparm, executor) {
	var arg, key, subkey;

	if(nparm != 1 && nparm != 2)
		return executor.error("wrong parameter count for \"field\"-function");
	subkey = "";
	if(nparm == 2) {
		arg = executor.stack_pop();
		subkey = arg.value("string");
	}
	arg = executor.stack_pop();
	key = arg.value("string")+exec_emb_tag;
	return __hlp_exec_get_field(executor, key, subkey);
}

function exec_callback_foreign(nparm, executor) {
	var arg, arg3, arg4, arg5;
	var form, nr, embnr, key, subkey;

	if(nparm < 3 && nparm > 5)
		return executor.error('wrong parameter count for \'foreign\'-function (3..5)');

	subkey = '';
	if(nparm == 5) 
		arg5 = executor.stack_pop();
	if(nparm >= 4)	
		arg4 = executor.stack_pop();
	if(nparm >= 3)	
		arg3 = executor.stack_pop();
	arg = executor.stack_pop();
	nr = arg.value('int');
	arg = executor.stack_pop();
	form = arg.value('string');

	if(arg3.d_type == 'string') {
		key = arg3.value('string');
		if(arg4)
			subkey = arg4.value('string');
		if(arg5)
			return executor.error('wrong parameter count for \'foreign\'-function (5)');
	} else {
		embnr = arg3.value('int');
		if(!arg4)
			return executor.error('wrong parameter count for \'foreign\'-function (3)');
		key = arg4.value('string');
		if(arg5)
			subkey = arg5.value('string');
	}
	
	switch(form) {
		case 'parent':
			if(!embnr && nr == db_form_nr)	
        			return __hlp_exec_get_field(executor, key, subkey);
			break;
		case 'this':
		case '':
			if(embnr)
				break;
        		if(exec_emb_tag) {
               			key += "_i"+nr;
                		return __hlp_exec_get_field(executor, key, subkey);
			} 
			if(nr == db_form_nr)	
        			return __hlp_exec_get_field(executor, key, subkey);
			break;
		default:	
			if(nr == db_form_nr && embnr)
				for(var i in db_embedded_forms)
					if(db_embedded_forms[i] == form) {
               					key += "_i"+embnr;
                				return __hlp_exec_get_field(executor, key, subkey);
					}
			break;
	}

	return executor.error('wrong parameters for client-side \'foreign\'-function (form \''+form+'\').');
}

function exec_callback_empty(nparm, executor) {
	var arg, key, subkey;
	var mainkey;
	var type;
	var elems;
	var check_val;
	var val;
	var i;

        if(nparm > 2)
                return executor.error("wrong parameter count for \"empty\"-function ("+nparm+").");

        if(nparm == 2) {
                arg = executor.stack_pop();
                subkey = "["+arg.value("string")+"]";
        } else
                subkey = "";

        if(nparm >= 1) {
                arg = executor.stack_pop();
                mainkey = arg.value("string");
        }
	if(nparm == 0 || !mainkey)
                mainkey = exec_field_current;

        mainkey += exec_emb_tag;
        key = mainkey+subkey;
        type = form_field_types[mainkey];
        if (nparm < 2 && type.charAt(0) == 'a') {
                // for array types: look at all values
                elems = ffa(key);
                if (type == 'ab')
                        check_val = 'false';
                else
                        check_val = "";
                for (i=0; i<elems.length; i++) {
                        val = load(elems[i]);
                        if (!(load(elems[i])===check_val))
                                return new jsexecutor_type_int(0);
                }

                return new jsexecutor_type_int(1);
        } else
                return new jsexecutor_type_int((load(ffe(key))==="")?1:0);
}

function exec_callback_changed(nparm, executor) {
	var arg, key, subkey;
	var varname, rv;

	if(nparm != 0 && nparm != 1)
		return executor.error("wrong parameter count for \"changed\"-function");
	if(nparm == 0)
		 return new jsexecutor_type_int(1);
	arg = executor.stack_pop();
	subkey = arg.value("string");
	varname = "changed_"+exec_field_current+exec_emb_tag+"_"+subkey;
	eval("rv = "+varname+";");
	return new jsexecutor_type_int((rv>0)?1:0);
}

function exec_callback_regex(nparm, executor) {
	var arg, key, subkey;
	var regex, rv;

	if(nparm != 1 && nparm != 2)
		return executor.error("wrong parameter count for \"regex\"-function");
	key = exec_field_current+exec_emb_tag;
	if(nparm == 2) {
		arg = executor.stack_pop();
		key = key+"["+arg.value("string")+"]";
	}
	arg = executor.stack_pop();
	regex = arg.value("string");
	eval("rv = "+regex+".test(load(ffe('"+key+"')));");
	return new jsexecutor_type_int(rv?1:0);
}

function exec_callback_missing(nparm, executor) {
	var arg, key;
	var xs;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"missing\"-function");
	arg = executor.stack_pop();
	key = arg.value("string")+exec_emb_tag;
	xs = rtrv(ffe("_stat["+key+"]"));
	return new jsexecutor_type_int((xs=="notappl" || xs=="missing")?1:0);
}

function exec_callback_except(nparm, executor) {
	var arg, key;
	var xs;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"except\"-function");
	arg = executor.stack_pop();
	key = arg.value("string")+exec_emb_tag;
	xs = rtrv(ffe("_stat["+key+"]"));
	return new jsexecutor_type_int((xs!="")?1:0);
}

function exec_callback_set(nparm, executor) {
	var arg, key, subkey;
	var val, id;

	if(nparm!=2 && nparm!=3)
		return executor.error("wrong parameter count for \"set\"-function");

	val = executor.stack_pop();
	subkey = "";
	if(nparm == 3) {
		arg = executor.stack_pop();
		subkey = arg.value("string");
	} 
	arg = executor.stack_pop();
	key = arg.value("string");
	if (!key)
		key = exec_field_current;
	key += exec_emb_tag;

	id = form_field_types[key];

	if(id == "i!!" || id == "z!" || id == "s!" || id == "a!")
		return executor.error("cannot set abstract input '"+id+"' to any value");
	
	/* array type */
	if(id == "ab" || id == "an" || id == "as") {
		if(!subkey)
			return executor.error("you must specify a sub-key in <"+key+"> that will be assinged a value");
		if(id == "an") {
			if(val.value("string") == "") {
				stor(ffe(key+"["+subkey+"]"),"");
				return new jsexecutor_type_string("");
			}
			stor(ffe(key+"["+subkey+"]"),val.value("float"));
			return new jsexecutor_type_float(val.value("float"));
		} else {
			stor(ffe(key+"["+subkey+"]"),val.value("string"));
			return new jsexecutor_type_string(val.value("string"));
		}
	} 
	
	/* scalar type */
	if(subkey)
		return executor.error("you must not specify a sub-key to assign a value to <"+key+">");
	if(id == "n") {
		if(val.value("string") == "") {
			stor(ffe(key),"");
			return new jsexecutor_type_string("");
		}
		stor(ffe(key),val.value("float"));
		return new jsexecutor_type_float(val.value("float"));
	}

	stor(ffe(key),val.value("string"));
	return new jsexecutor_type_string(val.value("string"));
}

function exec_callback_trigger(nparm, executor) {
	var arg, name, tmp;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"trigger\"-function");
	arg = executor.stack_pop();
	name = arg.value("string")+exec_emb_tag;
	tmp = exec_field_current;
	eval("check_"+name+"()");
	exec_field_current = tmp;
	return new jsexecutor_type_int(1);
}

function exec_callback_echo(nparm, executor) {
	var arg;
	var text;

	if(!nparm)
		return executor.error("wrong parameter count for \"echo\"-function");

	if(nparm == 1) {
		arg = executor.stack_pop();
		text = arg.value("string");
	} else {
		var i;
		var arr = new Array();
		for(i=0; i<nparm-1; i++) {
			arg = executor.stack_pop();
			arr.push(arg.value("string"));
		}
		arr = arr.reverse();
		arg = executor.stack_pop();
		format = arg.value("string");
		text = sprintf(format, arr); 
	}

	alert(text);
	return new jsexecutor_type_int(1);
}

function exec_callback_show(nparm, executor) {
	var arg, iid;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"show\"-function");
	arg = executor.stack_pop();
	iid = "id_"+arg.value("int")+exec_emb_tag;
	show(iid);
	return new jsexecutor_type_int(1);
}

function exec_callback_hide(nparm, executor) {
	var arg, iid;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"hide\"-function");
	arg = executor.stack_pop();
	iid = "id_"+arg.value("int")+exec_emb_tag;
	hide(iid);
	return new jsexecutor_type_int(1);
}

function exec_callback_pretitle(nparm, executor) {
	if(nparm != 1)
		return executor.error("wrong parameter count for \"pretitle\"-function");
	return new jsexecutor_type_int(1);
}

function exec_callback_title(nparm, executor) {
	var key;

	if(nparm > 1)
		return executor.error("wrong parameter count for \"title\"-function");
	if(nparm == 1) {
		arg = executor.stack_pop();
		key = arg.value("string");
	} else 
		key = exec_field_current;
		
	return new jsexecutor_type_string(form_field_titles[key+exec_emb_tag]);
}

/* Anzahl vergangener Schalttage seit Jahr 0 bis *exklusive* year */
function _xcb_leapdays(year) {
	year--;
	return ((year/4)|0) - ((year/100)|0) + ((year/400)|0);
}

function _xcb_isleap(year) {
	return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

var _xcb_mdays = new Array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

/* year=[0..] month=[1..12]  day=[1..31] */
function _xcb_makestamp(year, month, day) {
	var days;
		
	if(year<1)
		return 0; /* provide "defined" behaviour */
	days = 365*(year-1/* no year 0*/) + _xcb_leapdays(year);
	for(i=1; i<month; i++)
		days += _xcb_mdays[i];
	if(month>2 && _xcb_isleap(year))
		days++;
	days += day-1;
	return days;
}
	
function _xcb_pad0(str,len) {
	while(str.length < len)
		str = '0'+str;
	return str;
}

function _xcb_makedate(stamp) {
	var day, days, days2, month, year, tmp;
		
	days = stamp|0; // cast to int
	if(days < 0)
		return "";
	year = ((days+366 /* "virtual" leap year 0 */) / 365.2425)|0;
	days2 = _xcb_makestamp(year,1,1)|0;
	for(i=1; i<=12; i++) {
		tmp = _xcb_mdays[i];
		if(i==2 && _xcb_isleap(year))
			tmp++;
		if(days2 + tmp > days) {
			month = i;
			day = days - days2 + 1;
			break;
		}
		days2 += tmp;
	}
	return _xcb_pad0(String(year),4)+"-"+_xcb_pad0(String(month),2)+"-"+_xcb_pad0(String(day),2);
}

function exec_callback_date(nparm, executor) {
	var d, stamp;

	if(nparm > 0)
		return executor.error("wrong parameter count for \"date\"-function");
	d = new Date();
	stamp = _xcb_makestamp(d.getUTCFullYear(), d.getUTCMonth()+1, d.getUTCDate());
	return new jsexecutor_type_int(stamp);
}

function exec_callback_date2stamp(nparm, executor) {
	var arg, dstr, stamp, re;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"date2stamp\"-function");
	arg = executor.stack_pop();
	dstr = arg.value("string");
	re = /^([123][0-9][0-9][0-9])-([01]?[0-9]|[1-9]?)-([0123]?[0-9]|[1-9]?)$/;
        re.exec(dstr);
	if(!RegExp.$1 || !RegExp.$2 && RegExp.$3)
        	return new jsexecutor_type_int(0);
	stamp = _xcb_makestamp(parseInt(RegExp.$1,10), parseInt(RegExp.$2?RegExp.$2:1,10), parseInt(RegExp.$3?RegExp.$3:1,10));
	return new jsexecutor_type_int(stamp);
}

function exec_callback_stamp2date(nparm, executor) {
	var arg, stamp;

        if(nparm != 1)
                return executor.error("wrong parameter count for \"stamp2date\"-function");
        arg = executor.stack_pop();
        stamp = arg.value("int");
	return new jsexecutor_type_string(_xcb_makedate(stamp));
}

function exec_callback_checkdate(nparm, executor) {
	var arg, dstr, res, year, month, day, days_in_month;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"checkdate\"-function");
	arg = executor.stack_pop();
	dstr = arg.value("string");
	res = /^([123][0-9][0-9][0-9])-([01]?[0-9])-([0123]?[0-9])$/.exec(dstr);
	if(!res)
		return new jsexecutor_type_int(0);
	year = parseInt(res[1], 10);
	month = parseInt(res[2], 10);
	day = parseInt(res[3], 10);

	// most of the following should've been catched by the regex anyway
	if(year<1 || month<1 || month>12 || day<1)
		return new jsexecutor_type_int(0);

	if(_xcb_isleap(year) && month==2)
		days_in_month = 29;
	else
		days_in_month = _xcb_mdays[month];

	if(day > days_in_month)
		return new jsexecutor_type_int(0);

	return new jsexecutor_type_int(1);
}

function exec_callback_substr(nparm, executor) {
	var arg, count, pos, str;

	if(nparm != 3)
		return executor.error("wrong parameter count for \"substr\"-function");

	arg = executor.stack_pop();
	count = arg.value("int");
	arg = executor.stack_pop();
	pos = arg.value("int");
	arg = executor.stack_pop();
	str = arg.value("string");

	return new jsexecutor_type_string(str.substr(pos, count));
}

function exec_callback_strlen(nparm, executor) {
	var arg, str;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"strlen\"-function");

	arg = executor.stack_pop();
	str = arg.value("string");

	return new jsexecutor_type_int(str.length);
}

function exec_callback_parameter(nparm, executor) {
	var arg, parm;

	if(nparm != 1)
		return executor.error("wrong parameter count for \"parameter\"-function");

	arg = executor.stack_pop();
	parm = arg.value("string");
	if(parm == "fkey")
		return new jsexecutor_type_string(db_form_fkey);
	else if(parm == "nr") 
		if(exec_emb_tag) // FIXME: better way to determine embnr?
			return new jsexecutor_type_int(exec_emb_tag.substr(2));
		else 
			return new jsexecutor_type_int(db_form_nr);	
	else if(parm == "parentnr")
		return new jsexecutor_type_int(db_form_nr);	
	else if(parm == "url")
		return new jsexecutor_type_string(document.location.href);	
	else if(parm == "client")
		return new jsexecutor_type_int(1);	
	else if(parm == "server")
		return new jsexecutor_type_int(0);	
		
	return executor.error("client-side parameter function only allows parameters 'fkey', 'nr', 'url', 'client' or 'server");
}

function exec_callback_server(nparm, executor) {
	return executor.error("server-only function called client-side");
}

var exec_callbacks = {
	value:exec_callback_value,
	field:exec_callback_field,
	foreign:exec_callback_foreign,
	empty:exec_callback_empty,
	changed:exec_callback_changed,
	regex:exec_callback_regex,
	missing:exec_callback_missing,
	except:exec_callback_except,
	set:exec_callback_set,
	trigger:exec_callback_trigger,
	echo:exec_callback_echo,
	error:exec_callback_echo,
	show:exec_callback_show,
	hide:exec_callback_hide,
	pretitle:exec_callback_pretitle,
	substr:exec_callback_substr,
	strlen:exec_callback_strlen,
	title:exec_callback_title,
	time:exec_callback_date,
	date:exec_callback_date,
	date2stamp:exec_callback_date2stamp,
	stamp2date:exec_callback_stamp2date,
	checkdate:exec_callback_checkdate,
	parameter:exec_callback_parameter,
	php:exec_callback_server,
	mail:exec_callback_server,
	changedx:exec_callback_server,
	max:exec_callback_server,
	session_get:exec_callback_server,
	session_set:exec_callback_server,
	parameter_set:exec_callback_server,
	access_profile_group:exec_callback_server
};

var exec_exec = new jsexecutor(exec_callbacks);

var exec_queue = new Array();
var exec_running = false;

function exec_oa_sync(oa) {
	var acc, rv, error;

	try {
		acc = exec_exec.execute(oa);
		rv = acc.is_zero()?false:true;
	} catch(error) {
		alert('execution of condition for field \''+exec_field_current+'\' failed: '+error);
		rv = false;
	}
	return rv;
}


function exec(oa_c, oa_t, oa_f, field_current, emb_tag) {
	var rv, elem;

	elem = new Array(oa_c, oa_t, oa_f, field_current, emb_tag);
	exec_queue.push(elem);

	if(exec_running)
		return;

	exec_running = true;
	while(exec_queue.length) {
		elem = exec_queue.shift();

		exec_field_current = elem[3];
		exec_emb_tag = elem[4];
		rv = exec_oa_sync(elem[0]);
		if(rv) {
			if(elem[1].length)
				exec_oa_sync(elem[1]);
		} else {
			if(elem[2].length)
				exec_oa_sync(elem[2]);
		}
	}
	exec_running = false;
}
