/*
 * Copyright (c)  2006-2007 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.
 */
maskat.lang.Class.declare("maskat.event.RequestMarshaller")
	.extend("maskat.xml.XMLObjectBinder", {

	initialize: function(){
		this.base();
		this.rootNode = null;
		this.ns = "";
		this.soap = false;
		this.sources = [];
		this.configured = false;
	},

	marshal: function(layout) {
		if (!this.rootNode) {
			return null;
		}
		if (!this.configured) {
			this.configure();
		}

		var values = [];
		for (var i = 0; i < this.sources.length; i++) {
			var source = this.sources[i];
			var widgetId = source.obj;

			var widget = layout.getWidget(widgetId);
			var value;

			if (source.idxRef) {
				var reference = layout.getWidget(source.idxRef);
				if (reference) {
					value = reference.getValue(source.teleType);
				} else {
					value = layout.getVariable(source.idxRef);
				}
			} else {
				if (widget) {
					value = widget.getValue(source.teleType);
	            } else {
	            	value = layout.getVariable(widgetId);
				}
			}
            
			if (typeof(source.fromkey) != "undefined") {
			    /* 選択されたセルを送信 */
				var index = widget.getValue("selectedIndex");
				values[i] = value[index][source.fromkey];
			} else if (typeof(source.idxRef) != "undefined") {
                if (typeof(source.childNode) == "undefined") {
				    /* 選択された単一行を送信 */
                    var index = widget.getValue("selectedIndex");
                    values[i] = value[index];
                } else {
				    /* 選択された複数行を送信 */
					var indexes = widget.getValue("selectedIndexes");
					var rows = [];
					for (var j = 0; j < indexes.length; j++) {
						rows.push(value[indexes[j]]);
					}
					values[i] = rows;
				}
			} else {
				/* マスカット部品の値をそのまま使用する */
				values[i] = value;
			}
			
			/* sendBlankElement 属性への対応 */
			switch (typeof(values[i])) {
			case "string":
				/* 空文字列の場合 */
				if (values[i] == "" && !source.sendBlankElement) {
					values[i] = undefined;
				}
				break;
				
			case "object":
				/* null または空の配列の場合 */
				if (values[i] == null ||
				    values[i] instanceof Array && values[i].length == 0) {
					values[i] = source.sendBlankElement ? null : undefined;
				}
				break;

			case "undefined":
				/* 未定義の場合 */
				values[i] = source.sendBlankElement ? null : undefined;
				break;
			}
		}

		return this.write(values);
	},

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

		if (this.soap) {
			// TODO: add soap envelope
		} else {
			this.setRootElement(this.ns, this.rootNode);
		}

		if (this.ns) {
			this.addPrefixMapping("", this.ns);
		}

		/* 応答メッセージのルート要素に対応するバインディングを生成 */
		var root = this.addElementBinding(this.ns, this.rootNode);

		for (var i = 0; i < this.sources.length; i++) {
			var source = this.sources[i];
			var wrapper;
			if (source.node) {
				/*
				 * マーシャル対象のデータをルート要素の子要素 (wrapper) の
				 * 内部に出力する
				 */
				root.addChildBinding(source.node, { property: i });
				wrapper = this.addElementBinding(this.ns, source.node);
			}

			var object;
			if (source.childNode) {
				/*
				 * 繰り返し要素が定義されている場合、マスカット部品または変数
				 * から配列を取得し、配列の各要素を XML 要素へバインドする
				 */
				if (source.node) {
					wrapper.addChildBinding(source.childNode, { repeat: true });
				} else {
					/*
					 * childNode 属性が指定された場合は node 属性は省略可能
					 * この場合、ルート要素の直下に繰り返し要素を出力する
					 */
					root.addChildBinding(source.childNode, { property: i, repeat: true });
				}
				object = this.addElementBinding(this.ns, source.childNode);
			} else if (source.binds) {
				/*
				 * 繰り返し要素は未定義だが子要素へのバインドが定義されている
				 * 場合、マスカット部品または変数からオブジェクトを取得し、
				 * そのプロパティを子要素へバインドする
				 */
				object = wrapper;
			} else {
				/* 文字列からテキストノードへのバインドを定義 */
				wrapper.addChildBinding("#text", {});
			}
	
			if (object && source.binds) {
				/* 子要素へのバインドを定義 */
				for (var j = 0; j < source.binds.length; j++) {
					var bind = source.binds[j];
					var property = this.addElementBinding(this.ns, bind.node);
					property.addChildBinding("#text", { property: bind.property });
					object.addChildBinding(bind.node);
				}
			}
		}
	}

});