var Util, DOM, CloudFunc, join;

(function(scope, Util, DOM, CloudFunc) {
    'use strict';
    
    scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);
    
    function CloudCmdProto(Util, DOM, CloudFunc) {
        var Key,
            Debug,
            log                     = function(str) {
                if (Debug)
                    console.log(str);
            },
            
            TITLE,
            Listeners,
            Files                   = DOM.Files,
            Images                  = DOM.Images,
            Info                    = DOM.CurrentInfo,
            CloudCmd                = this,
            Storage                 = DOM.Storage,
            type                    = Util.type;
        
        this.log                    = log,
        this.PREFIX                 = '',
        this.LIBDIR                 = '/lib/';
        this.LIBDIRCLIENT           = '/lib/client/';
        this.MIN_ONE_PANEL_WIDTH    = 1155;
        this.HOST                   = location.origin ||
                                      location.protocol + '//' + location.host;
        
        this.TITLE                  = 'Cloud Commander';
        
        TITLE                       = this.TITLE;
        
        log.enable     = function() {
            Debug = true;
        };
        
        log.disable    = function() {
            Debug = false;
        };
        
        /**
         * Функция привязываеться ко всем ссылкам и
         *  загружает содержимое каталогов
         *
         * @param params - {
         *          paramLink - ссылка
         *          needRefresh - необходимость обязательной загрузки данных с сервера
         *          panel
         *      }
         * @param callback
         */
        this.loadDir                = function(params, callback) {
            var imgPosition, panelChanged,
                noCurrent,
                isRefresh, panel, history,
                p               = params;
            
            if (params) {
                isRefresh       = p.isRefresh;
                panel           = p.panel;
                history         = p.history;
                noCurrent       = p.noCurrent;
            }
            
            if (!noCurrent)
                if (panel && panel !== Info.panel) {
                    DOM.changePanel();
                    panelChanged = true;
                }
            
            if (panelChanged || isRefresh || !history)
                imgPosition = 'top';
            
            Images.show.load(imgPosition, panel);
            
            /* загружаем содержимое каталога */
            ajaxLoad(p.path, {
                refresh     : isRefresh,
                history     : history,
                noCurrent   : noCurrent
            }, panel, callback);
        };
        
        /**
         * function load modules
         * @pParams = {name, path, func, dobefore, arg}
         */
        function loadModule(params) {
            var name, path, func, doBefore,
                funcName, isContain;
                    
            if (params) {
                name        = params.name,
                path        = params.path,
                func        = params.func,
                funcName    = params.funcName,
                doBefore    = params.dobefore;
                
                if (path && !name) {
                    name = getStrBigFirst(path);
                    name = name.replace(/.js$/, '');
                }
                
                isContain   = /\.js/.test(path);
                
                if (!isContain)
                    path += '.js';
                
                if (!CloudCmd[name]) {
                    CloudCmd[name] = function() {
                        var pathFull    = CloudCmd.LIBDIRCLIENT + path,
                            args        = arguments;
                        
                        Util.exec(doBefore);
                        
                        return DOM.load.js(pathFull, func ||
                            function(error) {
                                var Proto = CloudCmd[name];
                                
                                if (!error && type.function(Proto))
                                    CloudCmd[name] = applyConstructor(Proto, args);
                            });
                    };
                    
                    CloudCmd[name][funcName] = CloudCmd[name];
                }
            }
        }
        
        /*
         * apply arguemnts to constructor
         *
         * @param constructor
         * @param args
         */
        function applyConstructor(constructor, args) {
            var F = function () {
                return constructor.apply(this, args);
            };
            
            F.prototype = constructor.prototype;
            return new F();
        }
        
        /** Конструктор CloudClient, который
         * выполняет весь функционал по
         * инициализации
         */
        this.init                    = function(prefix) {
            var func        = function() {
                    Util.exec.series([
                        initModules,
                        baseInit,
                        function() {
                            CloudCmd.route(location.hash);
                        }
                    ]);
                },
                
                funcBefore  = function(callback) {
                    var src = prefix + '/join:' + [
                            CloudCmd.LIBDIRCLIENT + 'polyfill.js',
                            '/modules/domtokenlist-shim/dist/domtokenlist.min.js',
                        ].join(':');
                    
                    DOM.loadJquery(function() {
                        DOM.load.js(src, callback);
                    });
                };
            
            CloudCmd.PREFIX         = prefix;
            CloudCmd.LIBDIR         = prefix + '/lib/';
            CloudCmd.LIBDIRCLIENT   = prefix + '/lib/client/';
            
            DOM.Files.get('config', function(error, config) {
                var options = {
                    htmlDialogs: !error && config.htmlDialogs
                };
                
                if (error)
                    CloudCmd.log(error);
                
                DOM.Dialog = new DOM.Dialog(prefix, options);
            });
            
            Util.exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore);
        };
        
        this.join                   = function(urls) {
            var prefix  = CloudCmd.PREFIX;
            
            if (!Array.isArray(urls))
                throw Error('urls should be array!');
            
            urls = urls.map(function(url) {
                return url.replace(prefix, '');
            });
            
            return prefix + join(urls);
        };
        
        this.route                   = function(path) {
            var module, file, current, msg,
                query   = path.split('/');
            
            if (path) {
                module      = query[0];
                module      = module.slice(1).join('');
                module      = getStrBigFirst(module);
                
                file        = query[1];
                current     = DOM.getCurrentByName(file);
                
                if (file && !current) {
                    msg = CloudFunc.formatMsg('set current file', file, 'error');
                    CloudCmd.log(msg);
                } else {
                    DOM.setCurrentFile(current);
                    CloudCmd.execFromModule(module, 'show');
                }
            }
        };
        
        this.logOut = function() {
            DOM.load.ajax({
                url: '/logout',
                error: function() {
                    document.location.reload();
                }
            });
        };
        
        function initModules(callback) {
            Util.exec.if(CloudCmd.Key, function() {
                Key          = new CloudCmd.Key();
                CloudCmd.Key = Key;
                Key.bind();
            }, function(callback) {
                loadModule({
                    /* привязываем клавиши к функциям */
                    path    : 'key.js',
                    func    : callback
                });
            });
            
            Files.get('modules', function(error, modules) {
                var storageObj, mod, path,
                    STORAGE     = 'storage',
                    showLoad    = Images.show.load,
                    
                    doBefore    = {
                        'edit'                  : showLoad,
                        'menu'                  : showLoad,
                        'storage/_filepicker'   : showLoad
                    },
                    
                    load = function(name, path, func) {
                        var isTmpl = path === 'template';
                        
                        loadModule({
                            name        : name,
                            funcName    : isTmpl ? 'get' : 'show',
                            path        : path,
                            dobefore    : func
                        });
                    };
                
                if (!modules)
                    modules = [];
                
                modules.forEach(function(module) {
                    var isStr = type.string(module);
                    
                    if (isStr)
                        load(null, module, doBefore[module]);
                });
                
                storageObj = Util.findObjByNameInArr(modules, STORAGE),
                mod        = Util.getNamesFromObjArray(storageObj);
                
                mod.forEach(function(name) {
                    path = STORAGE + '/_' + name.toLowerCase();
                    
                    load(name, path, doBefore[path]);
                });
                
                callback();
            });
        }
        
        function baseInit(callback) {
            var dirPath = '',
                files   = DOM.getFiles();
                    
            /* выделяем строку с первым файлом */
            if (files)
                DOM.setCurrentFile(files[0]);
            
            dirPath     = DOM.getCurrentDirPath(),
            Listeners   = CloudCmd.Listeners;
            Listeners.init();
            
            Listeners.setOnPanel('left');
            Listeners.setOnPanel('right');
            
            Listeners.initKeysPanel();
            
            Storage.get(dirPath, function(error, data) {
                if (!data) {
                    data    = getJSONfromFileTable();
                    Storage.set(dirPath, data);
                }
            });
            
            callback();
        }
        
        this.execFromModule         = function(moduleName, funcName) {
            var args    = [].slice.call(arguments, 2),
                obj     = CloudCmd[moduleName],
                isObj   = Util.type.object(obj);
            
            Util.exec.if(isObj,
                function() {
                    var obj     = CloudCmd[moduleName],
                        func    = obj[funcName];
                    
                    func.apply(null, args);
                }, obj);
        };
        
        this.refresh                =  function(panelParam, options, callback) {
            var panel           = panelParam || Info.panel,
                noCurrent,
                NEEDREFRESH     = true,
                path            = DOM.getCurrentDirPath(panel);
            
            if (options)
                noCurrent       = options.noCurrent;
            
            if (!callback && typeof options === 'function') {
                callback    = options;
                options     = {};
            }
            
            CloudCmd.loadDir({
                path        : path,
                isRefresh   : NEEDREFRESH,
                history     : false,
                panel       : panel,
                noCurrent   : noCurrent
            }, callback);
        };
        
        /**
         * Функция загружает json-данные о Файловой Системе
         * через ajax-запрос.
         * @param path - каталог для чтения
         * @param options
         * { refresh, history } - необходимость обновить данные о каталоге
         * @param panel
         * @param callback
         *
         */
        function ajaxLoad(path, options, panel, callback) {
            var create = function(error, json) {
                var RESTful     = DOM.RESTful,
                    name        = Info.name,
                    obj         = Util.json.parse(json),
                    isRefresh   = options.refresh,
                    noCurrent   = options.noCurrent;
                
                if (!isRefresh && json)
                    createFileTable(obj, panel, options, callback);
                else
                    RESTful.read(path, 'json', function(error, obj) {
                        if (!error) {
                            Storage.set(path, obj);
                            
                            createFileTable(obj, panel, options, function() {
                                var current;
                                
                                if (isRefresh && !noCurrent) {
                                    current = DOM.getCurrentByName(name);
                                    DOM.setCurrentFile(current);
                                }
                                
                                Util.exec(callback);
                            });
                        }
                    });
            };
            
            if (!options)
                options    = {};
            
            CloudCmd.log('reading dir: "' + path + '";');
            
            Files.get('config', function(error, config) {
                var dirStorage,
                    Dialog = DOM.Dialog;
                
                if (error)
                    Dialog.alert(TITLE, error);
                else
                    dirStorage = config.dirStorage;
                
                if (dirStorage)
                    Storage.get(path, create);
                else
                    create();
            });
        }
        
        /**
         * Функция строит файловую таблицу
         * @param json  - данные о файлах
         * @param panelParam
         * @param history
         * @param callback
         */
        function createFileTable(json, panelParam, options, callback) {
            var history     = options.history,
                noCurrent   = options.noCurrent,
                names       = ['file', 'path', 'link', 'pathLink'];
            
            Files.get(names, function(error, templFile, templPath, templLink, templPathLink) {
                var childNodes, i,
                    Dialog      = DOM.Dialog,
                    current,
                    panel       = panelParam || DOM.getPanel(),
                    
                    dir         = Info.dir,
                    name        = Info.name;
                
                if (error) {
                    Dialog.alert(TITLE, error.responseText);
                } else {
                    childNodes  = panel.childNodes;
                    i           = childNodes.length;
                    
                    while (i--)
                        panel.removeChild(panel.lastChild);
                    
                    panel.innerHTML = CloudFunc.buildFromJSON({
                        data        : json,
                        id          : panel.id,
                        template    : {
                            file        : templFile,
                            path        : templPath,
                            pathLink    : templPathLink,
                            link        : templLink
                        }
                    });
                    
                    Listeners.setOnPanel(panel);
                    
                    if (!noCurrent) {
                        if (name === '..' && dir !== '/')
                            current = DOM.getCurrentByName(dir);
                        
                        if (!current)
                            current = DOM.getFiles(panel)[0];
                            
                        DOM.setCurrentFile(current, {
                            history: history
                        });
                    }
                    
                    Util.exec(callback);
                }
            });
        }
        
        /**
         * Функция генерирует JSON из html-таблицы файлов и
         * используеться при первом заходе в корень
         */
        function getJSONfromFileTable() {
            var name, size, owner, mode, ret,
                path        = DOM.getCurrentDirPath(),
                infoFiles   = Info.files || [],
                
                fileTable   = {
                    path    : path,
                    files   : []
                },
                
                files       = fileTable.files;
            
            [].forEach.call(infoFiles, function(current) {
                name        = DOM.getCurrentName(current);
                size        = DOM.getCurrentSize(current);
                owner       = DOM.getCurrentOwner(current);
                mode        = DOM.getCurrentMode(current);
                
                if (name !== '..')
                    files.push({
                        name    : name,
                        size    : size,
                        mode    : mode,
                        owner   : owner
                    });
            });
            
            ret = Util.json.stringify(fileTable);
            
            return ret;
        }
        
        this.goToParentDir          = function() {
            var path        = Info.dirPath,
                parentPath  = Info.parentDirPath;
            
            if (path !== parentPath) {
                path    = parentPath;
                
                CloudCmd.loadDir({
                    path: path
                });
            }
        };
        
        function getStrBigFirst(str) {
            var first;
            
            if (!str)
                throw Error('str could not be empty!');
            
            first   = str[0].toUpperCase();
            str     = first + str.slice(1);
            
            return str;
        }
        
    }
})(this, Util, DOM, CloudFunc);
