/**
 * Файл: UB.ux.data.proxy.UBProxy.js
 * Автор: Игорь Ноженко
 * 
 * Proxy
 * 
 * Формирует запросы
 * Обеспечивает работу с кэшируемыми сущностями
 */

Ext.define('UB.ux.data.proxy.UBProxy', {
    extend: 'Ext.data.proxy.Server',
//extend: 'Ext.data.proxy.Ajax',
    alias: 'proxy.ubproxy',

    requires: [
        'UB.core.UBCommand',
        'UB.core.UBDomainManager',
        'UB.core.UBService',
        'UB.ux.data.reader.UBArray',
        'UB.core.UBUtil',
        'UB.core.UBAppConfig'
    ],
	
	uses: [
		'UB.view.ErrorWindow'
	],
	
    constructor: function(config) {
        Ext.apply(this, {
//            actionMethods: {
//                read: 'POST'
//            },
            reader: {
                type: 'ubarray',
                root: 'data',
                totalProperty: 'total'
            }
        });

        this.callParent(arguments);

        this.addListener('exception', this.onException, this);

        return this;
    },
    allLoaded: false,
    statics: {
        /**
         * Convert store filters to where list
         * @param {Array} filters
         * @param {String} entityName
         * @returns {Object}
         */
        ubFilterToWhereList: function(filters, entityName) {
            var whereList = {}, me = this, doFilter;
            doFilter = function(filter, index) {
                if(!filter.disabled && filter.filterFn){
                    var rFilter = me.ubFiltersItemToUBWhereListItem(filter, $App.domainInfo.get(entityName).attr(filter.property).dataType);
                    switch(filter.condition){
                        case "match":
                            whereList.match = rFilter;
                            break;
                        case "between":
                            whereList.between = rFilter;
                            break;
                        default:
                            whereList['x' + index] = rFilter;
                            break;
                    }
                }
            };
            if (filters instanceof  Ext.util.MixedCollection){
                filters.each(doFilter);
            } else {
                Ext.each(filters || [], doFilter);
            }
            return whereList;
        },

        /**
         *
         * @param {Object} fItem //todo - this is Ext.util.Filter or not?
         * @param {String} dataType
         * @return {Object}
         */
        ubFiltersItemToUBWhereListItem: function(fItem, dataType) {
            var
                conditions =  UB.core.UBCommand.condition,
                ubWLItem = {
                    values: {}
                };

            ubWLItem.expression = '[' + fItem.property + ']';
            if(dataType === UBDomain.ubDataTypes.String){
                if(fItem.exactMatch){
                    ubWLItem.condition =  conditions.sqlecEqual;
                } else if(fItem.startWith){
                    ubWLItem.condition =  conditions.sqlecStartWith;
                } else {
                    ubWLItem.condition =  conditions.sqlecLike;
                }
            }
            if(fItem.operator){
                switch (fItem.operator){
                    case '>=':
                        ubWLItem.condition = conditions.sqlecMoreEqual;
                        break;
                    case '<=':
                        ubWLItem.condition = conditions.sqlecLessEqual;
                        break;
                    case '<':
                        ubWLItem.condition = conditions.sqlecLess;
                        break;
                    case '=':
                        ubWLItem.condition = conditions.sqlecEqual;
                        break;
                    case '>':
                        ubWLItem.condition = conditions.sqlecMore;
                        break;
                    case '!=':
                        ubWLItem.condition = conditions.sqlecNotEqual;
                        break;
                    default:
                        ubWLItem.condition = conditions.sqlecEqual;
                }
            }
            if (fItem.condition){
                ubWLItem.condition =  fItem.condition;
            }
            ubWLItem.condition = ubWLItem.condition || conditions.sqlecEqual;
            ubWLItem.values[fItem.property] = fItem.value;
            // for special filter type. (foe example fts)
            switch(fItem.condition){
                case 'match':
                    ubWLItem = {
                        condition: "match",
                        values: {"any": fItem.value}
                    };
                    break;
                case 'between':
                    ubWLItem = {
                        condition: "between",
                        values: {"v1": fItem.valueFrom, "v2": fItem.valueTo}
                    };
                    break;
            }
            return ubWLItem;
        }
    },
    /**
     * 
     * @param {Ext.data.proxy.Proxy} proxy
     * @param {Object} response
     * @param {Ext.data.Operation} operation
     */
    onException: function(proxy, response, operation) {
        throw new Error({errMsg: operation.getError()});
    },

    /**
     * Take Ext.data.Operation and perform actual request using UnityBase cache rules.
     * For cached entities - do all filtration on client, for non-cached - on server
     * @override
     * @param {Ext.data.Operation} operation
     * @param {Object} operation.ubRequest
     * @param {Function} callback
     * @param {Object} scope
     */
    doRequest: function(operation, callback, scope) {
        var
            me = this,
            serverRequest = Ext.clone(operation.ubRequest),
            start, limit,
            operationFilterWhereList, operationOrderList,
            resultSet, fnFilters = [],
            successFn = function(resultSet) {
                operation.resultSet = resultSet;
                operation.setCompleted();
                operation.setSuccessful();
                scope = scope || {};
                //prevent callback execution in case of store (scope) start destroying
                if(!scope.isDestroyed){
                    Ext.callback(callback, scope, [operation]);
                }
            };

        if(operation.action !== 'read'){
            //TODO - implement all other operation type to allow directly communicate via store
            throw new Error('unsupported operation for UBProxy: ' + operation.action);
        }
        //merge operation filters to whereList
        operationFilterWhereList = me.operationFilter2WhereList(operation, serverRequest.entity, fnFilters) || {};
        UB.apply(operationFilterWhereList, serverRequest.whereList);
        if (Object.keys(operationFilterWhereList).length){
            serverRequest.whereList = operationFilterWhereList;
        } else {
            delete serverRequest.whereList;
        }

        //merge operation sorters to orderList
        operationOrderList = me.operationOrder2OrderList(operation) || {};
        UB.apply(operationOrderList, serverRequest.orderList);
        if (Object.keys(operationOrderList).length){
            serverRequest.orderList = operationOrderList;
        } else {
            delete serverRequest.orderList;
        }
        //TODO - что делать если лимиты были в реквесте??
        limit = (operation.limit && operation.limit > 0) ? operation.limit : undefined;
        start = (operation.start && operation.start > 0) ? operation.start : undefined;
        if (start || limit){
            serverRequest.options = serverRequest.options || {};
            if (start) {
                serverRequest.options.start = start;
            }
            if (limit) {
                serverRequest.options.limit = limit;
            }

            if (limit && !start) {
                serverRequest.options.start = 0;
            }
        }
        $App.connection.select(serverRequest).done(function(response){
            resultSet = me.getReader().read({data: response.resultData.data});
            if (fnFilters.length > 0){
               me.applyFilterFn(resultSet, fnFilters);
            }
            resultSet.total = response.total;
            resultSet.totalRecords = resultSet.total;

            resultSet.resultLock = response.resultLock;
            resultSet.resultAls = response.resultAls;
            /*
            if (scope) {  //TODO - check scope is Store
                scope.resultLock = response.resultLock;
                if (response.resultAls) {
                    scope.resultAls = response.resultAls;
                }
            }
            */
            successFn(resultSet);
        },function(reason){
            operation.setException(reason);

            if(!scope.isDestroyed){
                Ext.callback(callback, scope, [operation]);
            }

        });
    },


    applyFilterFn: function(data, fnFilters){
        var i, record, y, result = [], useIt;
        for(i = 0; i < data.records.length; i++){
            record = data.records[i];
            useIt = true;
            for(y = 0; y < fnFilters.length; y++){
                if (fnFilters[y](record) !== true){
                    useIt = false;
                    break;
                }
            }
            if (useIt){
                result.push(record);
            }
        }
        data.records = result;
        data.count = result.length;
        console.warn('Attention! Filter using the function works without paging');
    },

    /**
     * Transform operation filters to ubRequest whereList
     * @private
     * @param {Ext.data.Operation} operation
     * @param {String} entityName
     * @param {Array} [fnFilters] For filters that use a function
     * @return {Object}
     */
    operationFilter2WhereList: function(operation, entityName, fnFilters) {
        var
            len, i, result, filterItem, start = 100, filter;
        if(operation.filters && (len=operation.filters.length) > 0) {
            result = {};
            for(i=0; i<len; ++i){
                filterItem = operation.filters[i];
                if (fnFilters && filterItem.initialConfig.filterFn && !filterItem.initialConfig.property){
                    fnFilters.push(filterItem.initialConfig.filterFn);
                    continue;
                }
                if(!filterItem.disabled && !filterItem.property ){
                    throw new Error('invalid filter');
                }
                filter = UB.ux.data.proxy.UBProxy.ubFiltersItemToUBWhereListItem(
                    filterItem,
                    $App.domainInfo.get(entityName).attr(filterItem.property).dataType
                );
                result['x' + (start++)] = filter;
            }
        }
        return result;
    },

    /**
     * Transform operation.sorters to ubRequest orderList
     * @param {Ext.data.Operation} operation
     * @return {Object}
     */
    operationOrder2OrderList: function(operation) {
        var
            len, i,
            result,
            sorters = operation.sorters,
            start = 100;

        if(sorters && (len=sorters.length) > 0) {
            result = {};
            for(i=0; i<len; ++i){
                result['x' + start++] = {
                    expression: sorters[i].property,
                    order: sorters[i].direction === 'ASC' ?  'asc' : 'desc'
                };
            }
        }
        return result;
    }
});