// $Id: signature.c,v 1.17 2008/03/11 13:42:26 lynx Exp $ // vim:syntax=lpc
//
// generic implementation of http://about.psyc.eu/Signature
//
// maps an anonymous ordered value list to a named parametric var mapping
// according to the signature of a method. probably should
// also be able to do it in the other direction.
//
// needed both for converting slash commands into _request commands
// and to implement a protocol level optimization where variable names
// can be left out as long as the values are in the proper order.
//
// currently we only do the first of the two applications (actually the
// second being an extension of the first). to achieve that we hold a hash
// of command methods which point to their respective signatures.

#include <net.h>
#include <signature.h>

/* we still don't use structs because they make the driver a lot fatter
 * we'll get by using arrays and a couple of macros from signature.h
 *
struct Signature {
	closure handler;
	mixed extra;
	array(string) vars;
};
 *
 * also, we don't use closures as closures stick to the blueprint instead
 * of operating on the working object. apparently bind_lambda() can rebind
 * a CLOSURE_LFUN, but i didn't find out how to create that type of closure
 * and having something like
 *      if (blueprintp(ME)) return
 *          previous_object()->myself(myargs);
 * in *each* handler is a very ugly enterprise. I finally decided that the
 * little performance trade off in looking up an object's function in its
 * function table is well worth the simplicity we get by keeping all
 * signatures in a central place, being here. first I tried to make a custom
 * signature hash for each blueprint of a place, but that is just too messy
 * to go for.
 */

	// these could be generated by an external tool
private volatile mapping _sigs = ([
	// "OFFICIAL" METHODS as seen on [[Command]].
		// ahem... _do? consistency please!
	"_request_do_show_log":	({ "_request_history", 0, "_parameter" }),
	      // can either be _amount or _match or _parameter
	"_request_history":	({ "_request_history", 0, "_parameter" }),
	"_request_kick":	({ "_request_kick", 0, "_person" }),
	"_request_nickname":	({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
	// the real thing, maybe? method inheritance could even lead to here
	// for all of the _request_set_something methods. good? bad?
	"_request_place":	({ "_request_set", 0, "_key", "_value" }),
	"_request_set":		({ "_request_set", 0, "_key", "_value" }),
	// when called by _request_set(), value might be in _value
	"_request_set_masquerade": ({ "_request_masquerade", 0, "_flag_masquerade" }),
	"_request_set_owners":	({ "_request_owners", 0, "_list_owners" }), // _tab
	"_request_set_public":	({ "_request_public", 0, "_flag_public" }),
	"_request_set_style":	({ "_request_set_style", 0, "_uniform_style" }),
	"_request_set_topic":	({ "_request_set_topic", 0, "_value" }),
	"_request_topic":	({ "_request_set_topic", 0, "_value" }),
	"_request_topi":	({ "_request_set_topic", 0, "_value" }),
	"_request_top":		({ "_request_set_topic", 0, "_value" }),
	"_request_to":		({ "_request_set_topic", 0, "_value" }),
	"_request_t":		({ "_request_set_topic", 0, "_value" }),
	// "INTERNAL" METHODS
	// all of the following "fake" _request methods are just the psyced
	// way to handle command name variations and shortcuts. never use this
	// in your clients. always use the official versions listed on
	// [[Command]]. when sending arbitrary commands, use _request_execute,
	// don't make up _request_something like psyced does internally.
	"_request_hist":	({ "_request_history", 0, "_parameter" }),
	"_request_style":	({ "_request_set_style", 0, "_uniform_style" }),
	"_request_owners":	({ "_request_owners", 0, "_list_owners" }), // _tab
	"_request_elrid":	({ "_request_kick", 0, "_person" }),
	"_request_masquerade":	({ "_request_masquerade", 0, "_flag_masquerade" }),
	"_request_masq":	({ "_request_masquerade", 0, "_flag_masquerade" }),
	"_request_nick":	({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
	"_request_ni":		({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
	"_request_public":	({ "_request_public", 0, "_flag_public" }),
	"_request_pub":		({ "_request_public", 0, "_flag_public" }),
	// threads
	"_request_entries":	({ "_request_entries", 0, "_num" }),
	"_request_ents":	({ "_request_entries", 0, "_num" }),
	"_request_entry":	({ "_request_entry", 0, "_id" }),
	"_request_ent":		({ "_request_entry", 0, "_id" }),
	"_request_entry_reply":	({ "_request_entry_reply", 0, "_parent", "_text" }),
	"_request_comment":	({ "_request_entry_reply", 0, "_parent", "_text" }),
	"_request_com":		({ "_request_entry_reply", 0, "_parent", "_text" }),
	"_request_entry_add":	({ "_request_entry_add", 0, "_text" }),
	"_request_addentry":	({ "_request_entry_add", 0, "_text" }),
	"_request_addent":	({ "_request_entry_add", 0, "_text" }),
	"_request_submit":	({ "_request_entry_add", 0, "_text" }),
	"_request_blog":	({ "_request_entry_add", 0, "_text" }),
	"_request_entry_del":	({ "_request_entry_del", 0, "_id" }),
	"_request_delentry":	({ "_request_entry_del", 0, "_id" }),
	"_request_delent":	({ "_request_entry_del", 0, "_id" }),
	"_request_unsubmit":	({ "_request_entry_del", 0, "_id" }),
	"_request_unblog":	({ "_request_entry_del", 0, "_id" }),
	"_request_entry_edit":	({ "_request_entry_edit", 0, "_id", "_text" }),
	"_request_editentry":	({ "_request_entry_edit", 0, "_id", "_text" }),
	"_request_edentry":	({ "_request_entry_edit", 0, "_id", "_text" }),
	"_request_edent":	({ "_request_entry_edit", 0, "_id", "_text" }),
	"_request_set_addact":	({ "_request_set_addaction", 0, "_value" }),
	"_request_set_addaction":({ "_request_set_addaction", 0, "_value" }),
	"_request_set_editact":	({ "_request_set_editaction", 0, "_value" }),
	"_request_set_editaction":({ "_request_set_editaction", 0, "_value" }),
	"_request_set_showform":({ "_request_set_showform", 0, "_value" }),
	"_request_set_showcomments":({ "_request_set_showcomments", 0, "_value" }),
#ifdef _flag_enable_module_microblogging
	"_request_add":		({ "_request_add", 0, "_person" }),
	"_request_remove":	({ "_request_remove", 0, "_person" }),
	"_request_set_priv":	({ "_request_set_privacy", 0, "_value" }),
	"_request_set_privacy":	({ "_request_set_privacy", 0, "_value" }),
# ifdef TWITTER
	"_request_set_tw":	({ "_request_set_twitter", 0, "_value" }),
	"_request_set_twitter":	({ "_request_set_twitter", 0, "_value" }),
# endif
# ifdef IDENTICA
	"_request_set_ica":	({ "_request_set_identica", 0, "_value" }),
	"_request_set_identica":({ "_request_set_identica", 0, "_value" }),
# endif
#endif
#ifdef EXPERIMENTAL
	// stuff to play around with
	"_request_pset":	({ "_request_set", 0, "_key", "_value" }),
	"_request_cset":	({ "_request_set", 0, "_key_set", "_value" }),
	"_request_tt":		({ "_request_set", ([ "_key": "_topic" ]), "_value" }),
#endif
]);

varargs int call_signature(string source, string mc, mixed data,
	    mapping origvars, varargs array(mixed) more) {
	Signature s = _sigs[mc];
	mapping vars;
	string last;
	int i, j;

	unless (s) return 0;
	// ASSERT("call_signature:closurep", closurep(s[SHandler]), s)
	// vars = ([ "_INTERNAL_method_signature": mc ]);
	vars = ([]);
	if (mappingp(origvars) && sizeof(origvars))
	     vars += origvars;
	if (mappingp(s[SPreset])) vars += s[SPreset];
	/* else if (s[SPreset])
	    vars["_INTERNAL_preset"] = s[SPreset]; */
	if (pointerp(data) && sizeof(data)) {
		vars["_INTERNAL_command"] = data[0];
		for (i=1, j=SKeys; i<sizeof(data); i++, j++) {
			if (j < sizeof(s))
			    // check var type here.. TODO
			    vars[ last = s[j] ] = data[i];
			else
			    vars[ last ] += " "+ data[i];
		}
		data = 0;
	}
	P2(("call_signature: created %O as vars\n", vars))
	// this is how i tried to do it with closures..
	//bind_lambda(s[SHandler]);
	//return apply(s[SHandler], vars, more);
	return call_direct(previous_object(), s[SHandler],
			   source, mc, data, vars, more...);
}

#ifdef _flag_extend_backend
mapping register_signature(mapping newsigs) {
	if (newsigs) {
		_sigs = newsigs;
		ASSERT("register_signature",
		       mappingp(_sigs) && sizeof(_sigs), _sigs)
	}
	return _sigs;
}
#endif


