/*
 * Copyright (c)  2006-2009 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @class HTTP 応答メッセージ (XML 文書) から取得した値をレイアウト内のマスカット部品に設定するクラスです。
 *
 * @name maskat.event.ResponseUnmarshaller
 */ 
maskat.lang.Class.declare("maskat.event.ResponseUnmarshaller", {

	/** @scope maskat.event.ResponseUnmarshaller.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.soap = false;
		this.ns = "";
		this.rootNode = null;
		this.targets = [];
		this.configured = false;
		this.logger = maskat.log.LogFactory.getLog("maskat.event");
	},

	unmarshal: function(doc, layout) {
		if (!this.configured) {
			this.configure();
		}

		for (var i = 0; i < this.targets.length; i++) {
			var target = this.targets[i];
			switch (target.type) {
			case "local":
				/* ローカルデータバインディングを実行 */
				var src = layout.getWidget(target.node);
				var value;
				if (src) {
					value = src.getValue();
				} else {
					value = layout.getVariable(target.node);
				}
				
				var dst = layout.getWidget(target.widgetId);
				if (target.workType) {
					var arg1 = src ? src.unwrap() : value;
					var arg2 = dst ? dst.unwrap() : layout.getVariable(target.widgetId);

					this.logger.trace(maskat.util.Message.format("BINDING_HANDLE", {
							functionName: target.workType.functionName,
							src: target.node, dst: target.widgetId}));

					target.workType(arg1, arg2);
				} else {
					this.logger.trace(maskat.util.Message.format("LOCAL_BINDING_START", {
							src: target.node, dst: target.widgetId}));

					if (dst) {
						dst.setValue(value);
					} else {
						layout.setVariable(target.widgetId, value);
					}
					this.logger.trace(maskat.util.Message.format("BINDING_VALUE", {
							value: maskat.lang.Object.encodeJSON(value)}));
				}
				break;

			case "remote":
				/* 応答メッセージのバインディングを実行 */
				var values = doc ? target.read(doc) : null;
				var widget = layout.getWidget(target.widgetId);
				if (widget) {
					try {
						widget.setValue(values[i], target.teleType);
					} finally {
						this.logger.trace(maskat.util.Message.format("BINDING_WIDGET", {
							widget: target.widgetId,
							method: target.teleType ? target.teleType : "",
							value: maskat.lang.Object.encodeJSON(values[i])}));
					}
				} else {
					layout.setVariable(target.widgetId, values[i]);
					this.logger.trace(maskat.util.Message.format("BINDING_VARIABLE", {
						variable: target.widgetId,
						value: maskat.lang.Object.encodeJSON(values[i])}));
				}
				break;
			}
		}
	},

	configure: function() {
		this.configured = true;

		for (var i = 0; i < this.targets.length; i++) {
			var target = this.targets[i];
			if (target.type == "local") {
				continue;
			}
			
			if (this.soap) {
				// TODO: add soap envelope
			} else {
				target.setRootElement(this.ns, this.rootNode);
			}
	
			/* 応答メッセージのルート要素に対応するバインディングを生成 */
			var root = target.addElementBinding(this.ns, this.rootNode);

			var wrapper;
			if (target.node) {
				/*
				 * アンマーシャル対象のデータはルート要素の子要素 (wrapper) の
				 * 内部に格納されている
				 */
				wrapper = target.addElementBinding(this.ns, target.node);
				root.addChildBinding(target.node);
				root.addChildBinding("*", { validate: false });
			} else {
				/* アンマーシャル対象のデータはルート要素の内部に格納されている */
				wrapper = root;
			}
	
			var object;
			if (target.childNode) {
				/*
				 * 繰り返し要素が定義されている場合、繰り返し単位となる個々の
				 * 要素をオブジェクトへバインドし、それらを要素とする配列を
				 * マスカット部品または変数にバインドする
				 */
				object = target.addElementBinding(this.ns, target.childNode);
				wrapper.addChildBinding(target.childNode, { property: i, repeat: true });
				wrapper.addChildBinding("*", { validate: false });
			} else if (target.binds) {
				/*
				 * 繰り返し要素は未定義だが、プロパティへのバインドが定義されて
				 * いる場合、単一のオブジェクトへのバインドを定義
				 */
				root.addChildBinding(target.node, { property: i });
				object = wrapper;
			} else {
				/* テキスト要素から文字列へのバインドを定義 */
				wrapper.addChildBinding("#text", { property: i });
				object = null;
			}
	
			if (object && target.binds) {
				/* オブジェクトのプロパティへのバインドを定義 */
				for (var j = 0; j < target.binds.length; j++) {
					var bind = target.binds[j];
					var property = target.addElementBinding(this.ns, bind.node);
					property.addChildBinding("#text", { property: bind.property });
					object.addChildBinding(bind.node);
				}
				object.addChildBinding("*", { validate: false });
			}
		}
	}

});
