// @tag enterprise
/**
 * The SOAP Proxy class is an {@link Ext.data.proxy.Ajax Ajax Proxy} to access v1.1 SOAP
 * (Simple Object Access Protocol) services.  SOAP Proxy constructs a SOAP Envelope and 
 * submits an AJAX request to load a SOAP response from the server.
 * 
 * For help getting started please refer to the [Soap Guide](#/guide/soap).
 * @class Ext.data.soap.Proxy
 */
Ext.define('Ext.data.soap.Proxy', {
    extend: 'Ext.data.proxy.Ajax',
    alias: 'proxy.soap',

    requires: [
        'Ext.data.soap.Reader'
    ],

    /**
     * @cfg {Object} api
     * An object containing "create", "read", "update" and "destroy" properties that define
     * SOAP operations for each CRUD action method. These operations will be appended to
     * the {@link #url} as the {@link #operationParam} for each request type.
     * 
     *     api: {
     *         create: undefined,
     *         read: undefined,
     *         update: undefined,
     *         destroy: undefined
     *     }
     *     
     * At least one operation is required, but additional operations do not need to be configured
     * if they will not be used.  For example, if this proxy is only used for read operations
     * the following configuration will be sufficient:
     * 
     *     api: {
     *         read: 'Foo'
     *     }
     */

    /**
     * @cfg {Object} soapAction
     * An object containing "create", "read", "update" and "destroy" properties that define
     * the [SOAPAction](http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528) header
     * for each CRUD action method. A soapAction must be specified for each operation
     * configured in {@link #api}  Defaults to:
     * 
     *     soapAction: {
     *         create: undefined,
     *         read: undefined,
     *         update: undefined,
     *         destroy: undefined
     *     }
     */

    /**
     * @cfg {String} [operationParam='op']
     * The name of the operation parameter to be appened to the SOAP endpoint url
     */
    operationParam: 'op',

    /**
     * @cfg {Object/String/Ext.data.soap.Reader} [reader='soap']
     * The {@link Ext.data.soap.Reader} to use to decode the server's response. This can
     * either be a SOAP Reader instance, a SOAP Reader config object or 'soap'.
     */
    reader: 'soap',

    /**
     * @cfg {String} url
     * The SOAP endpoint url that this proxy will use to request the SOAP data. This can
     * be a proxied url to work around same-origin policy if the SOAP endpoint url is on
     * a different domain from your application.
     */

    /**
     * @cfg [envelopeTpl=undefined]
     * The template used to create the SOAP envelope.  Defaults to:
     * 
     *     [
     *         '<?xml version="1.0" encoding="utf-8" ?>',
     *         '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">',
     *             '{[values.bodyTpl.apply(values)]}',
     *         '</soap:Envelope>'
     *     ]
     */
    envelopeTpl: [
        '<?xml version="1.0" encoding="utf-8" ?>',
        '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">',
            '{[values.bodyTpl.apply(values)]}',
        '</soap:Envelope>'
    ],

    /**
     * @cfg {Ext.XTemplate/Array} createBodyTpl
     * The template used to create the SOAP body for the "create" action. If not specified
     * {@link #writeBodyTpl} will be used for the "create" action.
     */

    /**
     * @cfg {Ext.XTemplate/Array} [readBodyTpl=undefined]
     * The template used to create the SOAP body for the "read" action.  Defaults to: 
     * 
     *     [
     *         '<soap:Body>',
     *             '<{operation} xmlns="{targetNamespace}">',
     *                 '<tpl foreach="params">',
     *                     '<{$}>{.}</{$}>',
     *                 '</tpl>',
     *             '</{operation}>',
     *         '</soap:Body>'
     *     ]
     */
    readBodyTpl: [
        '<soap:Body>',
            '<{operation} xmlns="{targetNamespace}">',
                '<tpl foreach="params">',
                    '<{$}>{.}</{$}>',
                '</tpl>',
            '</{operation}>',
        '</soap:Body>'
    ],

    /**
     * @cfg {Ext.XTemplate/Array} updateBodyTpl
     * The template used to create the SOAP body for the "update" action. If not specified
     * {@link #writeBodyTpl} will be used for the "update" action.
     */

    /**
     * @cfg {Ext.XTemplate/Array} destroyBodyTpl
     * The template used to create the SOAP body for the "destroy" action. If not specified
     * {@link #writeBodyTpl} will be used for the "destroy" action.
     */

    /**
     * @cfg {Ext.XTemplate/Array} [writeBodyTpl=undefined]
     * The default template used to create the SOAP body for write actions (create, update,
     * and destroy). The individual body templates for each write action can be configured
     * using {@link #createBodyTpl}, {@link #updateBodyTpl}, and {@link #destroyBodyTpl}.
     * Defaults to:
     * 
     *     [
     *          '<soap:Body>',
     *              '<{operation} xmlns="{targetNamespace}">',
     *                  '<tpl for="records">',
     *                      '{% var recordName=values.modelName.split(".").pop(); %}',
     *                      '<{[recordName]}>',
     *                          '<tpl for="fields">',
     *                              '<{name}>{[parent.get(values.name)]}</{name}>',
     *                          '</tpl>',
     *                      '</{[recordName]}>',
     *                  '</tpl>',
     *              '</{operation}>',
     *          '</soap:Body>'
     *      ]
     */
    writeBodyTpl: [
        '<soap:Body>',
            '<{operation} xmlns="{targetNamespace}">',
                '<tpl for="records">',
                    '{% var recordName=values.modelName.split(".").pop(); %}',
                    '<{[recordName]}>',
                        '<tpl for="fields">',
                            '<{name}>{[parent.get(values.name)]}</{name}>',
                        '</tpl>',
                    '</{[recordName]}>',
                '</tpl>',
            '</{operation}>',
        '</soap:Body>'
    ],

    /**
     * @cfg {String} targetNamespace
     * namespace URI used by {@link #createBodyTpl}, {@link #readBodyTpl}, {@link #updateBodyTpl},
     * and {@link #destroyBodyTpl} as the "xmlns" attribute for the operation element.
     */
    
    /**
     * @property {Object} actionMethods
     * @readonly
     * Mapping of action name to HTTP request method.  All SOAP actions are mapped to 'POST'
     */
    
    constructor: function(config) {
        this.callParent(arguments);
        this.api = config.api || {};
        this.soapAction = config.soapAction || {};
    },

    doRequest: function(operation, callback, scope) {
        var me = this,
            XTemplate = Ext.XTemplate,
            action = operation.action,
            soapOperation = me.api[action],
            params = Ext.applyIf(operation.params || {}, me.extraParams || {}),
            bodyTplName = action + 'BodyTpl',
            xmlData = XTemplate.getTpl(me, 'envelopeTpl').apply({
                operation: soapOperation,
                targetNamespace: me.targetNamespace,
                params: params,
                records: operation.records,
                bodyTpl: XTemplate.getTpl(me, me[bodyTplName] ? bodyTplName : 'writeBodyTpl')
            }),
            request = new Ext.data.Request({
                url: me.url + '?' + me.operationParam + '=' + soapOperation,
                method: 'POST',
                action: action,
                operation: operation,
                xmlData: xmlData,
                headers: Ext.apply({
                    SOAPAction: me.soapAction[action]
                }, me.headers),
                timeout: me.timeout,
                scope: me,
                disableCaching: false // explicitly set it to false, ServerProxy handles caching
            });

        request.callback = me.createRequestCallback(request, operation, callback, scope);

        Ext.Ajax.request(request);
                
        return request;
    }
});