/*
 * 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.RemoteEventHandler")
	.extend("maskat.event.EventHandler", {

	/**
	 * コンストラクタ
	 */
	initialize: function(){
		this.base.apply(this, arguments);
		this.url = null;
		this.method = "POST";
		this.headers = null;
		this.onBeforeRequest = null;
		this.onAfterResponse = null;
		this.onTimeoutError = null;
		this.marshaller = null;
		this.unmarshaller = null;
	},

	/**
	 * このイベントハンドラを管理するイベントディスパッチャを設定します。
	 * マスカット部品で発生したイベントは最初にイベントディスパッチャに
	 * 通知され、適切なハンドラが起動されます。
	 *
	 * イベントディスパッチャはレイアウト内のイベント処理に共通する設定を
	 * 保持しています。
	 *
	 * @param dispatcher イベントディスパッチャ
	 */
	setDispatcher: function(dispatcher){
		this.dispatcher = dispatcher;

		/* リモート URL の省略時はデフォルト URL を使用 */
		if (!this.url) {
			this.url = dispatcher.url;
		}

		/* グローバルヘッダを追加 */
		if (dispatcher.headers) {
			this.headers = dispatcher.headers.concat(this.headers || []);
		}
	},

	/**
	 * リモートイベントの処理を行います。
	 *
	 * リモートイベントハンドラはサーバとの XML HTTP 通信によってイベントを
	 * 処理します。レイアウト内のマスカット部品や変数のデータを抽出して要求
	 * メッセージを生成し、サーバに HTTP 要求を送信します。
	 *
	 * @param event マスカット部品で発生したイベント
	 */
	handle: function(event) {
		try {
			/* 要求メッセージのデータバインディングを実行 */
			var context = new maskat.event.RemoteEventContext(this, event);
			context.requestMessage = this.marshal(event.layout);
			context.send();
		} catch (e) {
			event.cancel = true;
			var msg = maskat.util.Message.format("REQUEST_BIND_ERROR", {
				widgetId: event.widgetId,
				type: event.type
			});
			var submsg = e.getMessages ? e.getMessages().join("\n") : e.message;
			this.logger.error(msg + "\n" + submsg);
		}
	},

	marshal: function(layout) {
		if (this.marshaller) {
			return this.marshaller.marshal(layout);
		}
		return undefined;
	},
	
	/**
	 * 応答メッセージの受信処理を行います。
	 *
	 * リモートイベントハンドラは応答メッセージの情報を抽出し、レイアウト内の
	 * マスカット部品や変数に格納します。
	 *
	 * @param event マスカット部品で発生したイベント
	 * @param context 通信終了後のコンテキスト
	 */
	handleResponse: function(event, context){
		/* 応答メッセージのデータバインディングを実行 */
		try {
			var doc = context.responseXML || context.responseMessage;
			if (doc && doc.nodeName && doc.documentElement.nodeName == "errors") {
				if (this.unmarshaller.onErrorTele) {
					this.unmarshaller.onErrorTele(doc);
				} else {
					throw new maskat.lang.InterruptedError("INTERRUPTION_ERROR");
				}
			} else {
				this.unmarshal(doc, event.layout);
			}
		} catch (e) {
			event.cancel = true;
			throw new maskat.lang.Error("RESPONSE_BIND_ERROR", {
				widgetId: event.widgetId,
				type: event.type
			}, e);
		}

		/* イベントハンドラの終了処理を実行 */
		this.finishHandle(event);
	},

	unmarshal: function(doc, layout) {
		if (doc && this.unmarshaller) {
			this.unmarshaller.unmarshal(doc, layout);
		}
	},

	/**
	 * 応答メッセージ待ちのタイムアウト処理を行います。
	 *
	 * 応答メッセージ待ち状態のまま指定されたタイムアウト時間が経過した場合、
	 * タイムアウト処理が起動されます。
	 *
	 * @param event マスカット部品で発生したイベント
	 */
	handleRequestTimeout: function(event) {
		event.cancel = true;
		if (this.onRequestTimeout) {
			/* タイムアウト発生時のコールバック関数を実行 */
			this.onRequestTimeout();
		} else {
			/* コールバック関数が指定されていない場合はログを出力 */
			var msg = maskat.util.Message.format("RESPONSE_TIMEOUT", {
				widgetId: event.widgetId,
				type: event.type
			});
			this.logger.error(msg);
		}
	},

	/**
	 * エラー処理を行います。
	 *
	 * @param event マスカット部品で発生したイベント
	 */
	handleError: function(event, error) {
		event.cancel = true;
		this.logger.error(error.message);
	}

});
