coreFramework.provider('cfNetwork', function () {

    var baseUrl = '';
    var suffix = '';
    var bootstrapPromises = [];
    var headers = {};
    var request = function () {};
    var http = function () {};
    var initPromise = {};

    this.setBaseUrl = function (url) {
        baseUrl = url;

        if (baseUrl.slice(-1) != '/') {
            baseUrl += '/';
        }
    };

    this.setUrlSuffix = function (urlSuffix) {
        suffix = urlSuffix;
    };

    function resolveBootstrap (localRequest) {
        var processedSuccess = function () {};
        var processedError = function () {};

        this.afterBootstrap()
            .then(function () {
                return initPromise.promise;
            }).then(function () {
                var executedRequest = localRequest();
                executedRequest.success(processedSuccess).error(processedError);
            });

        return angular.extend({
            then: function (success, error) {
                processedSuccess = interceptResponse(success);
                processedError = interceptResponse(error);
            }
        }, this);
    }

    function processRequest (obj) {

        if (angular.isUndefined(obj)) {
            obj = {url: ''};
        }
        if (angular.isUndefined(obj.url)) {
            obj.url = '';
        }

        obj.url = processUrl(obj.url);


        return function () {
            extendHeaders(obj);
            interceptRequest(obj);
            return http(obj);
        };
    }

    function setRequestName (config, args) {

        if (typeof config == 'string') {
            config = {};
        }
        if (args && args.length > 1) {
            var name = args[args.length - 1];
            if (typeof name == 'string') {
                angular.extend(config, { name: name });
            }
        }

        return config;

    }

    function processUrl (url) {
        if (!angular.isString(url)) {
            return url;
        }
        if (url.indexOf('http://') == -1 && url.indexOf('https://') == -1) {

            if (url.substring(0,1) == '/') {
                url = url.substring(1);
            }
            url = baseUrl + url;
        }

        if (url.slice(-1) != suffix && url.indexOf('?') == -1) {
            url += suffix;
        }

        return url;
    }

    var extendHeaders = function (conf) {
        var headerCopy = {};

        if (angular.isUndefined(conf.headers)) {
            conf.headers = {};
        }

        angular.copy(headers, headerCopy);
        conf.headers = angular.extend(headerCopy, conf.headers);
    };

    var addHeaders = function (obj) {
        angular.extend(headers, obj);
    };

    this.setDefaultHeaders = addHeaders;


    ///////////  INTERCEPTORS  //////////////

    //////////  response  ///////////////

    var responseCallbacks = [];

    this.addResponseCallback = addResponseCallback;

    function interceptResponse  (success) {
        return function (data, status, headers, config) {

            // var maxPriority = Math.max.apply(this, responseCallbacks.map(function (val) {
            //     return isNaN(status) || (!isNaN(val.conditions) && parseInt(val.conditions) == status) ? val.priority : 0;
            // }));

            angular.forEach(responseCallbacks, function (val) {

                // if (maxPriority && val.priority < maxPriority) {
                //     return;
                // }

                var match = true;
                var privateData;

                if (!isNaN(val.conditions) && parseInt(val.conditions) == status) {
                    privateData = val.callback(data, status, headers, config);
                    data = privateData ? privateData : data;
                } else if (angular.isObject(val.conditions)) {
                    var localHeaders = headers();

                    if (angular.isDefined(val.conditions.url) && processUrl(val.conditions.url) != config.url) {
                        match = false;
                    }

                    if (angular.isDefined(val.conditions.status) && (isNaN(val.conditions.status) || parseInt(val.conditions.status) != status)) {
                        match = false;
                    }

                    if (angular.isDefined(val.conditions.headers)) {
                        angular.forEach(val.conditions.headers, function (value, key) {
                            if (value != localHeaders[key]) {
                                match = false;
                            }
                        });
                    }
                    if (match) {
                        privateData = val.callback(data, status, headers, config);
                        data = privateData ? privateData : data;
                    }
                }
            });

            if (angular.isDefined(success)) {
                return success(data, status, headers, config);
            }
        };
    }

    function addResponseCallback (status, callback, priority, id) {

        priority = priority || 0;

        // replace callback
        if (id) {
            for (var i = 0, item; i < responseCallbacks.length; i++) {
                item = responseCallbacks[i];
                if (status == item.conditions && priority == item.priority && id == item.id) {
                    responseCallbacks[i].callback = callback;
                    return;
                }
            }
        }

        // add callback
        responseCallbacks.push({conditions: status, callback: callback, priority: priority, id: id});

    }

    //////////////// request ///////////////////

    var requestCallbacks = [];

    this.addRequestCallback = addRequestCallback;

    function interceptRequest (request) {
//        console.log(request);

        //angular.extend(request, { name: this.requestName });
        //console.log(request, { name: this.requestName });

        angular.forEach(requestCallbacks, function (callbackObj) {
            var match = true;

            if (angular.isDefined(callbackObj.conditions.method) && request.method != callbackObj.conditions.method) {
                match = false;
            } else if (angular.isDefined(callbackObj.conditions.url) && request.url != processUrl(callbackObj.conditions.url)) {
                match = false;
            } else if (angular.isDefined(callbackObj.conditions.params)) {
                if (request.params && callbackObj.conditions.params && Object.keys(callbackObj.conditions.params).length) {
                    angular.forEach(callbackObj.conditions.params, function (param, key) {
                        if (param != request.params[key]) {
                            match = false;
                        }
                    });
                } else {
                    match = false;
                }
            } else if (angular.isDefined(callbackObj.conditions.headers)) {
                if (request.headers && callbackObj.conditions.headers && Object.keys(callbackObj.conditions.headers).length) {
                    angular.forEach(callbackObj.conditions.headers, function (param, key) {
                        if (param != request.headers[key]) {
                            match = false;
                        }
                    });
                } else {
                    match = false;
                }
            }

            if (match) {
                callbackObj.callback(request);
            }
        });

        return request;
    }

    function addRequestCallback (obj, callback) {
        requestCallbacks.push({conditions: obj, callback: callback});
    }

    ///////////  INTERCEPTORS END  /////////////

    this.$get = function ($q, $http) {

        initPromise = $q.defer();
        http = $http;

        return {

            init: function () {
                initPromise.resolve();
            },

            afterBootstrap: function () {
                return $q.all(bootstrapPromises);
            },

            addBootstrap: function (req) {

                return (function (req) {

                    var self = {};
                    self.deferred = $q.defer();
                    self.request = (processRequest(req)());

                    bootstrapPromises.push(self.deferred.promise.then());

                    return {
                        then: function (success, error) {
                            self.request.success(success).then(function (){
                                self.deferred.resolve();
                            });
                            self.request.error(error).then(function () {
                                self.deferred.reject();
                            });
                        }
                    };
                }(req));
            },

            request: function (obj) {

                request = processRequest(obj);

                return resolveBootstrap.call(this, request);
            },

            get: function (url,config) {

                config = config ? config : {};
                config = setRequestName(config, arguments);

                config.url = url;
                config.method = 'GET';

                request = processRequest(config);

                return resolveBootstrap.call(this, request);
            },

            post: function (url, data, config) {

                config = config ? config : {};
                config = setRequestName(config, arguments);

                config.url = url;
                config.method = 'POST';
                config.data = data;

                request = processRequest(config);

                return resolveBootstrap.call(this, request);
            },

            patch: function (url, data, config) {

                config = config ? config : {};
                config = setRequestName(config, arguments);

                config.url = url;
                config.method = 'PATCH';
                config.data = data;

                request = processRequest(config);

                return resolveBootstrap.call(this, request);
            },

            'delete': function (url,config) {

                config = config ? config : {};
                config = setRequestName(config, arguments);

                config.url = url;
                config.method = 'DELETE';

                request = processRequest(config);

                return resolveBootstrap.call(this, request);
            },

//            then: function (success, error) {
//                var savedRequest = request;
//                return this.afterBootstrap()
//                    .then(function () {
//                        return initPromise.promise;
//                    }).then(function () {
//                        var processedSuccess = interceptResponse(success);
//                        var processedError = interceptResponse(error);
//
//                        return savedRequest().success(processedSuccess).error(processedError);
//                    });
//            },

            addDefaultHeaders: addHeaders,
            addResponseCallback: addResponseCallback,
            addRequestCallback: addRequestCallback

        };

    };

})

.provider('cfStates', function () {

    var _states = {};

    function State (state) {
        var _states = ['default', 'requested', 'resolved', 'rejected'];
        var _state = _states.indexOf(state) >= 0 ? state : _states[0];
        for (var s in _states) {
            (function (object, property) {
                Object.defineProperty(object, property, {
                    enumerable: true,
                    get: function() {
                        return _state == property;
                    },
                    set: function(value) {
                        value = !!value;
                        if (value) {
                            _state = property;
                        }
                    }
                });
            })(this, _states[s]);
        }
    }

    this.$get = function () {

        return {

            get: function (name) {
                if (!_states[name]) {
                    this.set(name);
                }
                return _states[name];
            },
            set: function (name) {
                _states[name] = new State();
            }

        };

    };

});
