var Util, DOM;

(function (Util, DOMTree) {
    'use strict';
    
    var DOMProto    = Object.getPrototypeOf(DOMTree);
    
    DOMProto.load   = new LoaderProto();
    
    function LoaderProto() {
        var Images  = DOM.Images,
            Events  = DOM.Events;
        
        /**
         * Функция создаёт элемент и загружает файл с src.
         * 
         * @param pParams_o = {
         * name, - название тэга
         * src', - путь к файлу
         * func, - обьект, содержаий одну из функций 
         *          или сразу две onload и onerror
         *          {onload: function() {}, onerror: function();}
         * style,
         * id,
         * element,
         * async, - true by default
         * inner: 'id{color:red, },
         * class, 
         * notAppend - false by default
         * }
         */
        function load (params) {
            var element, type,
                p           = params,
                func        = p.func,
                name        = p.name,
                parent      = p.parent || document.body,
                
                /*
                 * if passed arguments function
                 * then it's onload by default
                 *
                 * if object - then onload and onerror
                 */
                funcLoad     = function() {
                    var callback = func && func.onload || func;
                    
                    Events.remove('error', element, funcError);
                    
                    Util.exec(callback);
                },
                
                funcError    = function() {
                    var callback    = func.onerror || func.onload || func,
                        template    = 'file {{ src }} could not be loaded',
                        isQuit      = true,
                        msg         = Util.render(template, {
                            src: p.src
                        }),
                        error       = new Error(msg);
                    
                    parent.removeChild(element);
                    
                    Images.showError(msg, isQuit);
                    
                    Util.exec(callback, error);
                };
            
            /* убираем путь к файлу, оставляя только название файла */
            if (!p.id && p.src)
                p.id = load.getIdBySrc(p.src);
                    
            element = DOM.getById(p.id);
            
            if (element) {
                Util.exec(func);
            } else {
                element                 = document.createElement(name);
                
                if (name === 'script' || name === 'link')
                    Events.addOnce('load', element, funcLoad)
                          .addError(element, funcError);
                
                if (p.id)
                    element.id          = p.id;
                
                if (p.className)
                    element.className   = p.className;
                
                if (p.src) {
                    /* if work with css use href */
                    if (name === 'link') {
                        element.href    = p.src;
                        element.rel     = 'stylesheet';
                    } else
                        element.src     = p.src;
                }
                
                if (p.attribute) {
                    type = Util.getType(p.attribute);
                    
                    switch(type) {
                    case 'string':
                        element.setAttribute(p.attribute, '');
                        break;
                    
                    case 'object':
                        Object.keys(p.attribute).forEach(function(name) {
                            element.setAttribute(name, p.attribute[name]);
                        });
                        break;
                    }
                }
                
                if (p.style)
                    element.style.cssText = p.style;
                
                if (p.async && name === 'script' || p.async === undefined)
                    element.async = true;
                
                if (!p.notAppend)
                    parent.appendChild(element);
                
                if (p.inner)
                    element.innerHTML = p.inner;
            }
            
            return element;
        }
        
        /**
         * Function gets id by src
         * @param pSrc
         * 
         * Example: http://domain.com/1.js -> 1_js
         */
        load.getIdBySrc             = function(src) {
            var num, sub, id,
                isStr       = Util.isString(src);
            
            if (isStr) {
                num    = src.lastIndexOf('/') + 1,
                sub    = src.substr(src, num),
                id     = Util.rmStrOnce(src, sub);
                
                /* убираем точки */
                while (id.indexOf('.') > 0)
                    id = id.replace('.', '_');
            }
            
            return id;
        };
        
        /**
         * load file countent via ajax
         * 
         * @param pParams
         */
        load.ajax                    = function(params) {
            var data,
                p           = params, countProgress,
                isObject    = Util.isObject(p.data),
                isArray     = Util.isArray(p.data),
                type        = p.type || p.method || 'GET',
                xhr         = new XMLHttpRequest();
            
            xhr.open(type, p.url, true);
            
            if (p.responseType)
                xhr.responseType = p.responseType;
            
            if (isObject || isArray)
                data    = Util.stringifyJSON(p.data);
            else
                data    = p.data;
            
            Events.add('progress', xhr.upload, function(event) {
                var percent, count;
                
                if (event.lengthComputable) {
                    percent = (event.loaded / event.total) * 100;
                    count   = Math.round(percent);
                    
                    if (countProgress)
                        Images.setProgress(count);
                    
                    countProgress = true;
                }
            
            });
            
            Events.add('readystatechange', xhr, function(event) {
                var TYPE_JSON, type, data, isContain,
                    xhr         = event.target;
                
                if (xhr.readyState === 4 /* Complete */) {
                    Images.clearProgress();
                    TYPE_JSON   = 'application/json';
                    type        = xhr.getResponseHeader('content-type');
                    
                    if (xhr.status === 200 /* OK */) {
                        data        = xhr.response;
                        isContain   = Util.isContainStr(type, TYPE_JSON);
                        
                        if (p.dataType !== 'text')
                            /* If it's json - parse it as json */
                            if (type && isContain)
                                data = Util.parseJSON(xhr.response) || xhr.response;
                            
                            Util.exec(p.success, data, xhr.statusText, xhr);
                    }
                    /* file not found or connection lost */
                    else {
                        /* if html given or something like thet
                         * getBack just status of result
                         */
                        if (type && type.indexOf('text/plain') !== 0)
                            xhr.responseText = xhr.statusText;
                        
                        Util.exec(p.error, xhr);
                    }
                }
            });
            
            xhr.send(data);
        };
        
        load.ext                    = function(src, func) {
            var element,
                ext     =  Util.getExt(src);
            
            switch (ext) {
            case '.js':
                element = load.js(src, func);
                break;
            
            case '.css':
                element = load.css(src, func);
                break;
            
            default:
                element = load({
                    src     : src,
                    func    : func
                });
            }
            
            return element;
        };
        
        /**
         * create elements and load them to DOM-tree
         * one-by-one
         * 
         * @param params
         * @param callback
         */
        load.series                 = function(params, callback) {
            var funcs   = [];
            
            if (params) {
                funcs = params.map(function(url) {
                    return load.ext.bind(null, url);
                })
                .concat(callback);
                
                Util.exec.series(funcs);
            }
            
            return load;
        };
        
        /**
         * improve callback of funcs so
         * we pop number of function and
         * if it's last we call pCallBack
         * 
         * @param params
         * @param callback - onload function
         */
        load.parallel               = function(params, callback) {
            var funcs   = [];
            
            if (params) {
                funcs = params.map(function(url) {
                    return load.ext.bind(null, url);
                });
                
                Util.exec.parallel(funcs, callback);
            }
            
            return load;
        };
        
        /** 
         * Функция загружает js-файл
         * 
         * @param pSrc
         * @param pFunc
         */
        load.js                  = function(src, func) {
            var element = load({
                name : 'script',
                src  : src,
                func : func
            });
        
            return element;
        },
        
        load.css                = function(src, callback) {
            var element = load({
                name    : 'link',
                src     : src,
                parent  : document.head,
                func    : callback
            });
            
            return element;
        };
        
        /**
         * Функция создаёт елемент style и записывает туда стили 
         * @param pParams_o - структура параметров, заполняеться таким
         * образом: {src: ' ',func: '', id: '', element: '', inner: ''}
         * все параметры опциональны
         */    
        load.style                  = function(params) {
            if (!params.name)
                params.name      = 'style';
            
            if (!params.parent)
                params.parent    = document.head;
            
            return load(params);
        };
        
        return load;
    }
})(Util, DOM);
