// modes.js
//
// dan sandler <dsandler@be.com>
//
// classes for maintaining mutually-exclusive Modes and sets of Features.

var DEBUG_JS_MODES = true;

function Nop() { return true; }

// ======================================================================
// ModeMachine
// ======================================================================

function ModeMachine() {
	this.m_clusters = new Object();
}

function ModeMachine__toString() {
	return '[object ModeMachine]';
}; ModeMachine.prototype.toString = ModeMachine__toString;

function ModeMachine__AddCluster(clustername) {
	this.m_clusters[clustername] = new Object();
	this.m_clusters[clustername].name = clustername;
	this.m_clusters[clustername].activeMode = null;
	this.m_clusters[clustername].observers = new Array(0);
}; ModeMachine.prototype.AddCluster = ModeMachine__AddCluster;

function ModeMachine__AddMode(cluster, modename, aHook, dHook) {
	if (!this.m_clusters[cluster]) {
		beos.dprintf('ModeMachine.AddMode: error: no such cluster: "' + cluster + '"');
		return false;
	}
	var mode = this.m_clusters[cluster]["mode:"+modename] = new Object();
	mode.name = modename;
	mode.clustername = cluster;
	mode.asserted = false;
	mode.activateHook = aHook || Nop;
	mode.deactivateHook = dHook || Nop
}; ModeMachine.prototype.AddMode = ModeMachine__AddMode;

/* function ModeMachine__AddModeObserver(cluster, modename, object) {
	var c = this.m_clusters[cluster];
	if ( ! c ) {
		beos.dprintf('ModeMachine.AddModeObserver: error: no such cluster: "' + cluster + '"');
		return false;
	}
	var mode = c["mode:"+modename];
	if ( ! mode ) {
		beos.dprintf('ModeMachine.AddModeObserver: error: no such mode: "' + modename + '"');
		return false;
	}
	mode.observers.length++;
	if (DEBUG_JS_MODES) 
		beos.dprintf('ModeMachine:AddModeObserver: adding as mode '
			+mode.clustername+'='+mode.name+' observers['
			+(mode.observers.length-1)+']');
	mode.observers[mode.observers.length-1] = object;
}; ModeMachine.prototype.AddModeObserver = ModeMachine__AddModeObserver; */

function ModeMachine__AddClusterObserver(cluster, object) {
	var c = this.m_clusters[cluster];
	if ( ! c ) {
		beos.dprintf('ModeMachine.AddClusterObserver: error: no such cluster: "' + cluster + '"');
		return false;
	}
	c.observers.length++;
	if (DEBUG_JS_MODES) 
		beos.dprintf('ModeMachine:AddClusterObserver: adding as cluster '
			+c.name+' observers['
			+(c.observers.length-1)+']');
	c.observers[c.observers.length-1] = object;
}; ModeMachine.prototype.AddClusterObserver = ModeMachine__AddClusterObserver;


function ModeMachine__Set(cluster, modename) {
	var c = this.m_clusters[cluster];
	if ( ! c ) {
		beos.dprintf('ModeMachine.Set: error: no such cluster: "' + cluster + '"');
		return false;
	}
	var mode = c["mode:"+modename];
	if ( ! mode ) {
		beos.dprintf('ModeMachine.Set: error: no such mode: "' + modename + '"');
		return false;
	}
	var tmp; var i;
	for (i in c) {
		if (i.substr(0,4) == "mode:") {
			tmp = c[i];
			if (tmp.asserted) {
				if (DEBUG_JS_MODES)
					beos.dprintf('deactivating mode '+i.substr(5));
				if (!tmp.deactivateHook())
					return false;
				tmp.asserted = false;
			}
		}
	}

	this.NotifyCluster(c);

	if (mode.activateHook()) {
		mode.asserted = true;
		c.activeMode = modename;
	} 		
	return mode.asserted;
}; ModeMachine.prototype.Set = ModeMachine__Set;

function ModeMachine__Get(cluster) {
	var c;
	if (null == (c = this.m_clusters[cluster]) ) {
		beos.dprintf('ModeMachine.Get: error: no such cluster: "' + cluster + '"');
		return null;
	}
	return this.m_clusters[cluster].activeMode;
}; ModeMachine.prototype.Get = ModeMachine__Get;

/* function ModeMachine__NotifyMode(mode) {
	var updateFunction;
	if ( DEBUG_JS_MODES )
		beos.dprintf('ModeMachine.NotifyMode([mode '+mode.clustername
			+'='+mode.name+']): '
			+mode.observers.length+' observers');
	for(var i=0; i<mode.observers.length; i++) {
		if ( (mode.observers[i] != null)
			&& (updateFunction = mode.observers[i].Update) ) { // assign
			if ( DEBUG_JS_MODES ) 
				beos.dprintf('ModeMachine.NotifyMode: calling observers['+i+']: '
					+mode.observers[i]+'.Update()');
			updateFunction();
		}
	}
}; ModeMachine.prototype.NotifyMode = ModeMachine__NotifyMode; */

function ModeMachine__NotifyCluster(cluster) {
	var updateFunction;
	if ( DEBUG_JS_MODES )
		beos.dprintf('ModeMachine.NotifyCluster([cluster '+cluster.name
			+']): '+cluster.observers.length+' observers');
	for(var i=0; i<cluster.observers.length; i++) {
		if ( DEBUG_JS_MODES ) 
			beos.dprintf('ModeMachine.NotifyCluster: checking observer '+i);
		if ( (cluster.observers[i] != null)
			&& (cluster.observers[i].Update != null) ) { 
			if ( DEBUG_JS_MODES ) 
				beos.dprintf('ModeMachine.NotifyCluster: calling observers['+i+']: '
					+cluster.observers[i]+'.Update()');
			cluster.observers[i].Update();
		} else {
			beos.dprintf('ModeMachine.NotifyCluster: observer '+i
				+' null or has no Update() method');
		}
	}
}; ModeMachine.prototype.NotifyCluster = ModeMachine__NotifyCluster;



// ======================================================================
// FeatureMachine
// ======================================================================

function FeatureMachine() {
	this.m_features = new Object();
}

function FeatureMachine__toString() {
	return '[object FeatureMachine]';
}; FeatureMachine.prototype.toString = FeatureMachine__toString;

function FeatureMachine__Add(featurename, aHook, dHook) {
	var f = this.m_features["feature:"+featurename] = new Object();
	f.name = featurename;
	f.asserted = false;
	f.activateHook = aHook || Nop;
	f.deactivateHook = dHook || Nop;
	f.observers = new Array(0);
}; FeatureMachine.prototype.Add = FeatureMachine__Add;

function FeatureMachine__AddFeatureObserver(featurename, object) {
	var f = this.m_features["feature:"+featurename];
	if ( ! f ) {
		beos.dprintf('FeatureMachine.AddFeatureObserver: error: no such feature: "' + featurename + '"');
		return;
	}
	f.observers.length++;
	if (DEBUG_JS_MODES) 
		beos.dprintf('FeatureMachine:AddFeatureObserver: adding as feature '
			+f.name+' observers['+(f.observers.length-1)+']');
	f.observers[f.observers.length-1] = object;
}; FeatureMachine.prototype.AddFeatureObserver = FeatureMachine__AddFeatureObserver;

function FeatureMachine__Open(featurename) {
	var f = this.m_features["feature:"+featurename];
	if ( ! f ) {
		beos.dprintf('FeatureMachine.Open: error: no such feature: "' + featurename + '"');
		return false;
	}
	if (DEBUG_JS_MODES) 
		beos.dprintf('activating feature '+featurename);
	
	if (f.activateHook()) {
		f.asserted = true;

		this.NotifyFeature(f);
	}
	return f.asserted;
}; FeatureMachine.prototype.Open = FeatureMachine__Open;

function FeatureMachine__Close(featurename) {
	var f = this.m_features["feature:"+featurename];
	if ( ! f ) {
		beos.dprintf('FeatureMachine.Close: error: no such feature: "' + featurename + '"');
		return;
	}
	if (DEBUG_JS_MODES)
		beos.dprintf('deactivating feature '+featurename);

	this.NotifyFeature(f);
	
	if (f.deactivateHook()) {
		f.asserted = false;
	}
	return f.asserted;
}; FeatureMachine.prototype.Close = FeatureMachine__Close;

function FeatureMachine__Get(featurename) {
	var f = this.m_features["feature:"+featurename];
	if ( ! f ) {
		beos.dprintf('FeatureMachine.Get: error: no such feature: "' + featurename + '"');
		return false;
	}
	return f.asserted;
}; FeatureMachine.prototype.Get = FeatureMachine__Get;

function FeatureMachine__NotifyFeature(f) {
	if ( DEBUG_JS_MODES )
		beos.dprintf('FeatureMachine.NotifyFeature([feature '+f.name+']): '
			+f.observers.length+' observers');
	var updateFunction;
	for(var i=0; i<f.observers.length; i++) {
		if ( (f.observers[i] != null)
			&& (updateFunction = f.observers[i].Update) ) { // assign
			if ( DEBUG_JS_MODES ) 
				beos.dprintf('FeatureMachine.NotifyFeature: calling observers['+i+']: '
					+f.observers[i]+'.Update()');
			updateFunction();
		}
	}
}; FeatureMachine.prototype.NotifyFeature = FeatureMachine__NotifyFeature;

