/**
 * Custom JavaScript Web Service Client Library
 * depends: ExtJS or jQuery(must contains jQuery.JSON), csjsstdlib.js
 * version 2.0
 */

var WS_FATAL_ERROR = '-1';
var WS_LOGIC_ERROR = '-2';
var WS_NOTLOGIN_OR_TIMEOUT = '-3';
var WS_LOGIN_FAIL = '-4';

// 如果没有使用ExtJS库，则对模拟ExtJS对象进行扩充
if (!HasExtJS) {
    Ext.apply(Ext, {
        // 如果没有使用ExtJS库，则使用jQuery的JSON解码器
        decode: function(text) {
            return $.evalJSON(text);
        },

        // 如果没有使用ExtJS库，则使用jQuery的Ajax功能
        Ajax: {}
    });

    Ext.apply(Ext.Ajax, {
        request: function(options) {
            var ro = {
                url: options['url'],
                type: 'POST',
                success: this.onAjaxSuccess,
                error: this.onAjaxError,
                callOptions: options
            };

            var processData = options['processData'];
            if (!processData) {
                ro.contentType = 'application/json';
                ro.processData = false;
            }

            var jsonData = options['jsonData'];
            if (!Ext.isEmpty(jsonData))
                ro.data = $.toJSON(jsonData);

            $.ajax(ro);
        },

        onAjaxSuccess: function(data) {
            var response = {
                responseText: data
            };
            var options = this['callOptions'];
            options.success.call(options.scope, response, options);
        },

        onAjaxError: function(xhr) {
            var options = this['callOptions'];
            options.failure.call(options.scope, xhr, options);
        }
    });
}

/**
 * 全局登录对话框，用于会话超时后的处理
 */
var loginDlg = null;

/**
 * @class WebSvcError
 * Web服务所返回的错误信息类
 */
function WebSvcError() {
    this.errCode = null;
    this.errMessage = null;
    this.errDetail = null;
}

WebSvcError.prototype = {
    /**
     * {String} 错误代码
     */
    errCode: null,
    /**
     * {String} 错误信息
     */
    errMessage: null,
    /**
     * {String} 详细错误信息
     */
    errDetail: null
};

/**
 * @class ServiceInvokeError
 * Web服务调用失败信息类
 */
function ServiceInvokeError() {
    this.clientErrMsg = null;
    this.serverErrMsg = null;
    this.webSvcError = null;
}

ServiceInvokeError.prototype = {
    /**
     * {String} 客户端错误消息，此项不为空表明错误在客户端中发生
     */
    clientErrMsg: null,
    /**
     * {String} 服务器端错误消息，此项不为空表明错误在访问服务器时发生，并且说明服务器端方法尚未调用到
     */
    serverErrMsg: null,
    /**
     * {WebSvcError} Web服务错误信息，此项不为空表明错误在服务器端方法内部发生
     */
    webSvcError: null,

    /**
     * 获取错误信息
     */
    getErrMsg: function() {
        if (!Ext.isEmpty(this.clientErrMsg))
            return this.clientErrMsg;
        else if (!Ext.isEmpty(this.serverErrMsg))
            return this.serverErrMsg;
        else if (!Ext.isEmpty(this.webSvcError))
            if (this.webSvcError.errCode == WS_FATAL_ERROR)
                return this.webSvcError.errDetail;
            else
                return this.webSvcError.errMessage;
        else
            return '未描述的错误';
    }
};

// Ensure Common Utils
if (eval("typeof(Common)") == 'undefined') {
    var Common = new (function() {
        this.errDlg = function(options) {
            alert(options.msg);

            var dlgCloseListener = options.dlgCloseListener;
            if (!Ext.isEmpty(dlgCloseListener))
                CSUtils.dispachBasicEvent(dlgCloseListener);
        };
    });
}

/**
 * @class ServiceInvokeParam
 * Web服务访问参数类
 */
function ServiceInvokeParam() {
    this.pathToRoot = null;
    this.actionFunc = null;
    this.actionParams = null;
    this.returnRoot = null;
    this.invokeListener = null;
    this.progress = null;
}

ServiceInvokeParam.prototype = {
    /**
     * {String} 发起请求的页面所在目录返回到Web应用根目录的路径。如果页面就在根目录，则无需设置返回路径
     *   例：设根目录为/theApp，发起请求的页面所在目录为/theApp/aFolder，则返回路径就是../
     *   上例中，如果发起请求的页面所在目录为/theApp/aFolder/anotherFolder，则返回路径是../../，其余依此类推
     */
    pathToRoot: null,
    /**
     * {Function} Web服务动作函数
     */
    actionFunc: null,
    /**
     * {*} [可选] Web服务动作参数
     */
    actionParams: null,
    /**
     * {String} [可选] Web服务动作返回值的根节点标识符
     */
    returnRoot: null,
    /**
     * {SuccessOrNotListener} [可选] Web服务调用后的事件侦听器，参见SuccessOrNotListener。事件的eventData如下：
     *   success时，为Web服务返回的信息对象（如果Web服务不返回信息，则eventData不存在）
     *   failure时，为Web服务调用错误信息对象，参见ServiceInvokeError
     */
    invokeListener: null,
    /**
     * {Ext.MessageBox} [可选] 进度条对话框
     */
    progress: null
};

/**
 * @class ServiceBase
 * @param serviceName Web服务名
 * 自定义Web服务代理基类
 */
function ServiceBase(serviceName) {
    this.SIMPLE_RETURN = 'retValue';
    this.PAGEDLIST_RETURN = 'pagedListPack';
    this.serviceName = serviceName;
    this.entranceUrl = 'ssow/ServiceEntrance';
    this.decodeResponse = true;

    var baseProps = [], propName;
    for (propName in ServiceBase.prototype)
        baseProps.push(propName);
    this.serviceMethods = [];
    for (propName in this) {
        if (baseProps.indexOf(propName) >= 0)
            continue;
        if ((this[propName] instanceof Function) && (propName != 'override') && (propName.substring(0, 2) != 'on'))
            this.serviceMethods.push(propName);
    }
}

ServiceBase.prototype = {
    /**
     * 简单返回类型
     */
    SIMPLE_RETURN: 'retValue',
    /**
     * 分页列表返回类型
     */
    PAGEDLIST_RETURN: 'pagedListPack',

    /**
     * Web服务名称，通常情况下派生类的构造函数调用祖先类构造函数时必须提供该成员的值
     */
    serviceName: null,
    /**
     * Web服务调用入口点，通常情况下派生类不应当修改此成员
     */
    entranceUrl: 'ssow/ServiceEntrance',
    /**
     * 是否解码HttpResponse
     */
    decodeResponse: true,

    // private
    serviceMethods: [],

    /**
     * Web服务访问接口。派生类直接以正确的动作名和参数调用此接口即可实现对Web服务的访问
     * @param {ServiceInvokeParam} params Web服务访问参数，参见ServiceInvokeParam
     */
    invoke: function(params) {
        var url = this.entranceUrl;
        if (Ext.type(params.pathToRoot) == 'string')
            url = params.pathToRoot + url;
        Ext.Ajax.request({
            url: url,
            success: this.invokeSuccess,
            failure: this.invokeFailure,
            scope: this,
            jsonData: {
                service: this.serviceName,
                action: this.getMethodName(params.actionFunc),
                params: params.actionParams
            },
            invokeParams: params
        });
    },

    // private
    getMethodName: function(actionFunc) {
        for (var i = 0; i < this.serviceMethods.length; i ++)
            if (this[this.serviceMethods[i]] == actionFunc)
                return this.serviceMethods[i];
        return null;
    },

    // protected
    invokeSuccess: function(response, options) {
        var invokeParams = options.invokeParams;

        var returnPack, error;
        try {
            if (this.decodeResponse)
                returnPack = Ext.decode(response.responseText);
            else
                returnPack = response.responseText;
        } catch (e) {
            error = new ServiceInvokeError();
            error.clientErrMsg = e.message;
            this.invokeError(error, invokeParams);
            return;
        }

        var webSvcError;
        if (this.decodeResponse)
            webSvcError = returnPack['webSvcError'];
        else
            webSvcError = null;
        if (Ext.isEmpty(webSvcError)) {
            try {
                var invokeListener = invokeParams.invokeListener;
                if (!Ext.isEmpty(invokeListener)) {
                    var returnRoot = invokeParams.returnRoot;
                    if (Ext.isEmpty(returnRoot))
                        if (this.decodeResponse)
                            CSUtils.dispatchSuccessOrNotEvent(invokeListener);
                        else
                            CSUtils.dispatchSuccessOrNotEvent(invokeListener, returnPack);
                    else
                        CSUtils.dispatchSuccessOrNotEvent(invokeListener, returnPack[returnRoot]);
                }
            } finally {
                this.tryCloseProgress(invokeParams.progress);
            }
        } else {
            error = new ServiceInvokeError();
            error.webSvcError = webSvcError;
            this.invokeError(error, invokeParams);
        }
    },

    // protected
    invokeFailure: function(response, options) {
        var error = new ServiceInvokeError();
        error.serverErrMsg = response.responseText;
        this.invokeError(error, options.invokeParams);
    },

    // private
    invokeError: function(error, invokeParams) {
        this.tryCloseProgress(invokeParams.progress);

        var webSvcError = error.webSvcError;
        var invokeListener = invokeParams.invokeListener;
        if ((!Ext.isEmpty(webSvcError)) && (webSvcError.errCode == WS_NOTLOGIN_OR_TIMEOUT)) {
            try {
                if (!Ext.isEmpty(invokeListener))
                    CSUtils.dispatchSuccessOrNotEvent(invokeListener, error, true);
            } finally {
                if (eval("typeof(WndLoginDlg)") == 'undefined')
                    this.showErrDlg(error, null);
                else {
                    if (loginDlg == null)
                        loginDlg = eval("new WndLoginDlg()");
                    loginDlg.show();
                }
            }
        } else
            this.showErrDlg(error, invokeListener);
    },

    // private
    showErrDlg: function(error, invokeListener) {
        var dialogOnly;
        if (Ext.isEmpty(error.webSvcError))
            dialogOnly = Ext.isEmpty(error.serverErrMsg);
        else
            dialogOnly = (error.webSvcError.errCode != WS_FATAL_ERROR) ||
                    ((error.webSvcError.errCode == WS_FATAL_ERROR) && HasExtJS);

        var errorMsg = error.getErrMsg();
        var caption = '服务器处理请求时发生错误';
        if (Ext.isEmpty(errorMsg)) {
            errorMsg = caption;
            caption = '错误';
        }

        if (!dialogOnly)
            this.openErrorPage(errorMsg);

        Common.errDlg({
            caption: caption,
            msg: errorMsg,
            dlgCloseListener: {
                scope: this,
                fn: this.onErrDlgClose,
                passParam: {
                    invokeListener: invokeListener,
                    error: error
                }
            }
        });
    },

    // private
    onErrDlgClose: function(params) {
        var invokeListener = params.invokeListener;
        if (!Ext.isEmpty(invokeListener))
            CSUtils.dispatchSuccessOrNotEvent(invokeListener, params.error, true);
    },

    // private
    openErrorPage: function(errorMsg) {
        try {
            if (!Ext.isEmpty(errorMsg)) {
                var errorWindow = window.open("about:blank");
                errorWindow.document.write(errorMsg);
            }
        } catch (e) {
            // Just do nothing
        }
    },

    // protected
    tryCloseProgress: function(progress) {
        try {
            if (!Ext.isEmpty(progress))
                progress.hide();
            Common.closeSharedProgress();
        } catch (e) {
            // Just do nothing
        }
    }
};

/**
 * @class RequestParamBase
 * 程序访问Web服务代理类时的请求参数基类
 */
function RequestParamBase() {
    // Just do nothing
}

RequestParamBase.prototype = {
    /**
     * 从自身创建传递给Web服务端的参数对象
     *
     * @param skipProps {Array} 字符串数组，给出要跳过的属性名
     * @param boolProps {Array} 字符串数组，给出要转换成布尔型值的属性名称
     * @returns {Object}
     */
    createRemoteParam: function(skipProps, boolProps) {
        var remoteParam = CSUtils.cloneValueProps(this, skipProps);
        if (Ext.isArray(boolProps))
            for (var i = 0; i < boolProps.length; i ++)
                remoteParam[boolProps[i]] = !Ext.isEmpty(this[boolProps[i]]);
        return remoteParam;
    }
};

/**
 * @class PagedListParam
 * 取分页列表数据参数基类
 */
var PagedListParam = Ext.extend(RequestParamBase, {
    pageSize: 50,
    pageIndex: 1,
    jumpToId: null,

    list: null,

    constructor: function() {
        PagedListParam.superclass.constructor.call(this);

        this.pageSize = 50;
        this.pageIndex = 1;
        this.jumpToId = null;

        this.list = null;
    },

    createRemoteParam: function(skipProps, boolProps) {
        if (Ext.isArray(skipProps))
            skipProps.push('list');
        else
            skipProps = ['list'];
        var remoteParam = PagedListParam.superclass.createRemoteParam.call(this, skipProps, boolProps);
        if (remoteParam.pageIndex < 1)
            remoteParam.pageIndex = 1;
        return remoteParam;
    }
});

/**
 * @class TextRequestParam
 * 简单文本请求参数类
 */
function TextRequestParam() {
    this.url = null;
    this.requestListener = null;
}

TextRequestParam.prototype = {
    /**
     * 简单文本请求地址
     */
    url: null,
    /**
     * {SuccessOrNotListener} [可选] 简单文本请求成功或失败的事件回调定义对象，参见SuccessOrNotListener。
     * 回调函数的eventData如下：
     *   success时，无
     *   failure时，{ServiceInvokeError} Web服务调用错误信息对象，参见ServiceInvokeError
     */
    requestListener: null
};

/**
 * @class  TextRequestor
 * 简单文本请求类
 */
var TextRequestor = Ext.extend(ServiceBase, {
    constructor: function() {
        TextRequestor.superclass.constructor.call(this);
        this.entranceUrl = null;
        this.decodeResponse = false;
    },

    /**
     * 简单文本请求
     *
     * @param params {TextRequestParam} 请求参数，TextRequestParam
     */
    request: function(params) {
        var invokeParam = new ServiceInvokeParam();
        invokeParam.actionFunc = this.request;
        invokeParam.invokeListener = params.requestListener;

        Common.openSharedProgress(Common.gettingDlg);

        Ext.Ajax.request({
            url: params.url,
            method: 'GET',
            disableCaching: true,
            success: this.invokeSuccess,
            failure: this.invokeFailure,
            scope: this,
            invokeParams: invokeParam
        });
    }
});
