function DownloadUploadManager($rootScope, __opt) {
    "use strict";

    var options = mcService.Marge({
        name    : "",
        upload  : {
            enable          : false,
            done            : function () {},
            onBeforeFileAdd : function () {return false},
            onAfterFileAdd  : function () {return false}
        },
        download: {
            enable: false,
            ftp_download: false
        },
        direct  : {
            enable: false
        },
        images  : {
            enable: false
        }

    }, __opt);

    this._name = __opt.name;

    var duManager     = this;
    var direct        = null;
    var images        = null;
    var fileUploader  = null;
    var visualisation = null;
    var canStart      = false;
    var isStarted     = false;
    var uinDirect     = 0;

    // --- Draw Progress Bar =================================================

    visualisation = new McVisualisation($rootScope);

    // --- Functions =========================================================

    function _downloadUrl(url, fileInfo, auto) {
        if (!isStarted && !$rootScope.isWebClient && visualisation.getState() === visualisation.constants.NOPROCESS) {
            $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                mcConst._CMD_.ce_file_download_url,
                url,
                fileInfo,
                auto
            ]);
        }
    }

    function _openDownloadUrl(url, fileInfo) {
        if (!isStarted && !$rootScope.isWebClient && visualisation.getState() === visualisation.constants.NOPROCESS) {
            $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                mcConst._CMD_.ce_file_open_or_download,
                url,
                fileInfo
            ]);
        }
    }

    function _openInFolder(hash, uin, path) {
        $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
            mcConst._CMD_.ce_file_open_in_folder,
            hash,
            uin,
            path
        ]);
    }

    function _openFolder(path) {
        $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
            mcConst._CMD_.ce_file_open_folder,
            path
        ]);
    }

    function _sendFilesToUser(fromBuffer) {
        var uin = $rootScope.GetChatID();

        if (!isStarted && $rootScope.GetChatType() === $rootScope.chatAliases.UIN && uin && visualisation.getState() === visualisation.constants.NOPROCESS){
            $rootScope.$broadcast(window._messages_.dialogsList.getUserState, [$rootScope.GetChatID(), function (state) {
                if (mcConst.states.offline === state){
                    webix.alert(mcLang(585)); // "585":"Передавать файлы напрямую можно только пользователям онлайн!",
                } else {
                    uinDirect = uin;

                    if (!$rootScope.isWebClient && !isStarted) {
                        $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                            mcConst._CMD_.ce_file_direct_upload_prepare,
                            uinDirect,
                            fromBuffer
                        ]);
                    }
                }
            }]);
        }
    }

    function _ftpDownload(path, fileName, size) {
        if (!isStarted && !$rootScope.isWebClient) {
            $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                mcConst._CMD_.ce_ftp_download,
                path,
                fileName, 
                size
            ]);
        }
    }

    function _destroy(){
        if (images) images.destroy();

        direct        = null;
        images        = null;
        visualisation = null;
        fileUploader  = null;
        isStarted     = false;

        uinDirect = 0;

        for (var i in window._messages_.downloadUpload){
            if (i !== '---') $rootScope.$off(window._messages_.downloadUpload[i]);
        }
    }

    // --- Direct Receive ====================================================

    if (options.direct.enable) {
        direct = new McDirectReceive($rootScope);

        direct.setVisualisation(visualisation);
        direct.onAcceptReceive = function(uin){
            uinDirect = uin;
            isStarted = true;
        };

        visualisation.setDirectReceive(direct);

        duManager.sendFilesToUser         = _sendFilesToUser;
    }

    // --- Loading Images ====================================================

    if (options.images.enable){
        images = new McLoadingImages($rootScope);

        duManager.addImageToLoad          = images.addImageToLoad;
        duManager.loadThumbs              = images.loadThumbs;
        duManager.loadAllImages           = images.loadAllImages;
        duManager.loadAllThumbs           = images.loadAllThumbs;
    }

    // --- Upload Via Server And Direct ======================================

    if (options.upload.enable){
        fileUploader = new McUploaderViaServer($rootScope, options.upload);

        fileUploader .setVisualisation(visualisation);
        visualisation.setFileUploader (fileUploader);
        visualisation.onAbortButton = function(){
            setTimeout(function () { // todo: передалать весь модуль нах
                isStarted = false;
            }, 50);
        };

        duManager.onPasteItemFromClipboard= function () {
            // if (visualisation.getState() === visualisation.constants.NOPROCESS){
                fileUploader._onPasteItemFromClipboard.apply(null, arguments);
            // }
        };
        // duManager.removePasteView         = fileUploader.
        duManager.setPastePreviewWindow   = function (cb) {
            fileUploader.pastImagePreview = cb || null;
        };
        duManager.setPastePreviewWindowFile= function (cb) {
            fileUploader.pastFilePreview = cb || null;
        };
        duManager.uploadFile              = function() {
            if (!isStarted && visualisation.getState() === visualisation.constants.NOPROCESS) {
                fileUploader._uploadFile.apply(null, arguments);
            }
        };
        duManager.abortUploading          = fileUploader.abortUploading;
        duManager.removePasteView         = fileUploader.removePasteView;
    }

    // =======================================================================

    if (options.download.enable){
        duManager.downloadUrl             = _downloadUrl;
        duManager.openDownloadUrl         = _openDownloadUrl;
        duManager.openInFolder            = _openInFolder;
        duManager.openFolder              = _openFolder;
    }

    // =======================================================================

    duManager.getViews    = visualisation._getViews;
    duManager.initViews   = visualisation._initViews;
    duManager.isActive    = visualisation.active;
    duManager.ftpDownload = _ftpDownload;
    duManager.destroy     = _destroy;
    duManager.canStart    = function () { canStart = true;  };
    duManager.noStart     = function () { canStart = false; };

    // ========================================================================
    // ========================================================================

    var _msg = window._messages_.downloadUpload = {
        changeStateForUser      : 'changeStateForUser',
        pastFilesFromClipboard  : 'pastFilesFromClipboard',
        acceptReceiveFiles      : 'acceptReceiveFiles',
        rejectReceiveFiles      : 'rejectReceiveFiles',
        sendFilesToUser         : 'sendFilesToUser',

        download_ftp_file       : 'download_ftp_file',

        on_file_download_progress      : 'on_file_download_progress',
        on_file_download_start         : 'on_file_download_start',
        on_file_download_complete      : 'on_file_download_complete',
        on_file_download_aborted       : 'on_file_download_aborted',
        on_file_download_file_not_found: 'on_file_download_file_not_found',

        on_file_upload_prepare_progress: 'on_file_upload_prepare_progress',
        on_file_upload_start           : 'on_file_upload_start',
        on_file_upload_prepare_start   : 'on_file_upload_prepare_start',
        on_file_upload_aborted         : 'on_file_upload_aborted',
        on_file_upload_progress        : 'on_file_upload_progress',
        on_file_upload_complete        : 'on_file_upload_complete',
        on_file_upload_error           : 'on_file_upload_error',

        on_file_check_exist            : 'on_file_check_exist',
        on_file_drop                   : 'on_file_drop',
        on_files_transfer_progress     : 'on_files_transfer_progress',

        on_file_request_transfer       : 'on_file_request_transfer',

        on_file_direct_upload_wait_accept         : 'on_file_direct_upload_wait_accept',
        on_file_direct_upload_accept              : 'on_file_direct_upload_accept',
        on_file_direct_upload_complete            : 'on_file_direct_upload_complete',
        on_file_direct_upload_no_files            : 'on_file_direct_upload_no_files',
        on_file_direct_download_complete          : 'on_file_direct_download_complete',
        on_file_direct_download_file_received     : 'on_file_direct_download_file_received',
        on_file_direct_receive_client_disconnected: 'on_file_direct_receive_client_disconnected',

        duManagerNoStart : 'duManagerNoStart',


        // openDownloadFolder  : 'openDownloadFolder',

        // on_file_direct_upload_file_sended   : 'on_file_direct_upload_file_sended',

        '---':'---'
    };

    // ========================================================================

    $rootScope.$on(_msg.duManagerNoStart, function () {
        duManager.noStart();
    });

    $rootScope.$on(_msg.on_files_transfer_progress, function (e, args) {
        if (isStarted && visualisation.active()) {
            visualisation._updateProgress.apply(visualisation, args);
        }
    });

    // --- download ===========================================================

    if (options.download.enable){
        if (options.download.ftp_download) {
            $rootScope.$on(_msg.download_ftp_file, function (e, args) {
                if (!isStarted && visualisation.getState() === visualisation.constants.NOPROCESS) {
                    _ftpDownload.apply(null, args);
                }
            });
        }

        $rootScope.$on(_msg.on_file_download_start, function (e, args) {
            if (!isStarted && visualisation.active.apply(null, args) && visualisation.getState() === visualisation.constants.NOPROCESS) {
                visualisation._showProgressInfo(args[1] === "ftp" ? visualisation.constants.DOWNLOADFTP : visualisation.constants.DOWNLOAD, false);
                var _savePath = args[2];

                if (_savePath) {
                    $rootScope.Storage.options.save({
                        currentDownloadFolder : _savePath
                    });

                    mcConst.currentDownloadFolder = _savePath;
                }

                // console.log(options.name);

                isStarted = true;
            }
        });

        $rootScope.$on(_msg.on_file_download_progress, function (e, args) {
            var rayID = args[0].rayID;

            if (isStarted && visualisation.active(rayID)) {
                visualisation._updateProgress.apply(visualisation, args);
            }
        });

        $rootScope.$on(_msg.on_file_download_aborted, function (e, args) {
            if (isStarted && visualisation.active.apply(null, args)) {
                visualisation._hideProgressInfo();

                setTimeout(function () { // todo: передалать весь модуль нах
                    isStarted = false;
                }, 50);
            }
        });

        $rootScope.$on(_msg.on_file_download_file_not_found, function (e, args) {
            if (isStarted && visualisation.active.apply(null, args)) {
                visualisation._hideProgressInfo();

                webix.alert("File not found on the server!");
                
                setTimeout(function () { // todo: передалать весь модуль нах
                    isStarted = false;
                }, 50);
            }
        });

        $rootScope.$on(_msg.on_file_download_complete, function (e, args) {
            if (isStarted && visualisation.active.apply(null, args) && visualisation.getState() !== visualisation.constants.NOPROCESS) {
                isStarted = false;

                visualisation._hideProgressInfo(true);

                // setTimeout(function () { // todo: передалать весь модуль нах
                // }, 50);
            }
        });
    }

    // --- upload =============================================================

    if (fileUploader || direct){
        $rootScope.$on(_msg.pastFilesFromClipboard, function (e, args) {
            if (canStart && !isStarted && visualisation.active()) {
                var files = args[0];

                if (files && files.length) { //  && $rootScope.GetChatType() === $rootScope.chatAliases.UIN
                    if (fileUploader.pastFilePreview){
                        fileUploader.pastFilePreview(files, function () {
                            fileUploader._uploadFile(mcConst._CMD_.msgType.FILE_NEW, files, false);

                            if (!$rootScope.isWebClient) $rootScope.$broadcast(window._messages_.mcStatistics.setStatValue, ["n_file_ctrlc", 1, false]); // false - увеличить значение на "1"
                        });
                    } else {
                        fileUploader._uploadFile(mcConst._CMD_.msgType.FILE_NEW, files, false);

                        if (!$rootScope.isWebClient) $rootScope.$broadcast(window._messages_.mcStatistics.setStatValue, ["n_file_ctrlc", 1, false]); // false - увеличить значение на "1"
                    }
                }
            }
        });

        $rootScope.$on(_msg.on_file_check_exist, function (e, args) {
            if (canStart && visualisation.active()) {
                // var id   = args[0].id;
                var info = args[0].info;

                if (info) {
                    $rootScope.SendCMDToServer([
                        mcConst._CMD_.cs_is_file_exists,
                        mcConst.SessionID,

                        info.Hash,     // SHA1 хэш файла
                        info.Type,     // тип файла. 2 - изображение, 4 - обычный файл
                        info.FileName, //
                        info.Where,    // куда вставлять файл (priv, conf, bbs, broadcast, forum, kanban)
                        info.ID,       // число-идентификатор, для кого отправлять файл:
                        mcService.fileTimeStamp(info.lastModifiedDate),
                        info.fileSize,

                        function (data) {
                            if (data.Present) {
                                fileUploader.sendFileMessage(info);

                                isStarted = false;

                                $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                                    mcConst._CMD_.ce_file_check_exist,
                                    true,
                                    {
                                        Where    : info.Where,
                                        ID       : info.ID,
                                        Type     : info.Type,
                                        chatType : info.chatType
                                    }
                                ]);
                            } else {
                                $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                                    mcConst._CMD_.ce_file_check_exist,
                                    false,
                                    {
                                        Where    : info.Where,
                                        ID       : info.ID,
                                        Type     : info.Type,
                                        chatType : info.chatType
                                    }
                                ]);
                            }
                        }
                    ]);
                }
            }
        });

        $rootScope.$on(_msg.on_file_drop, function (e, args) {
            var haveRights =
                (
                    (mcService.checkRights(mcConst._CMD_.RS.QTxtConfSendFiles) || mcService.checkRights(mcConst._CMD_.RS.QSendImages)) &&
                    $rootScope.GetChatType() === $rootScope.chatAliases.UID
                ) ||
                (
                    (mcService.checkRights(mcConst._CMD_.RS.QPrivInsertFiles)  || mcService.checkRights(mcConst._CMD_.RS.QImagesPrivates)) &&
                    $rootScope.GetChatType() === $rootScope.chatAliases.UIN
                );

            if (visualisation.active() && haveRights) {
                var dataTransfer = args[0];

                !isStarted && canStart && fileUploader._uploadFile(mcConst._CMD_.msgType.FILE_NEW, true, dataTransfer);
            } else
            if (!haveRights) {
                if ($rootScope.GetChatType() === $rootScope.chatAliases.UID) {
                    $rootScope.$broadcast('ErrorMsg', [222, mcService.Error(222)]); //"222": "у вас недостаточно прав для вставки файлов в конференции",
                } else {
                    $rootScope.$broadcast('ErrorMsg', [220, mcService.Error(220)]); //"220": "у вас недостаточно прав для вставки файлов в приват",
                }
            }
        });

        $rootScope.$on(_msg.on_file_upload_start, function () {
            if (isStarted && canStart && visualisation.active()) {
                visualisation._showProgressInfo(visualisation.constants.UPLOADBUFFER);
            }
        });

        $rootScope.$on(_msg.on_file_upload_error, function (e, args) {
            var err = args[0];

            console.warn(err);

            setTimeout(function () { // todo: передалать весь модуль нах
                isStarted = false;
            }, 50);

            visualisation._hideProgressInfo();

            $rootScope.$broadcast('ErrorMsg', [err.code, err.msg]);
        });

        $rootScope.$on(_msg.on_file_upload_progress, function (e, args) {
            if (isStarted && visualisation.active()) {
                visualisation._updateProgress.apply(visualisation, args);
            }
        });

        $rootScope.$on(_msg.on_file_upload_aborted, function (e, args) {
            if (isStarted && visualisation.active()) {
                var uin = args[0];

                if (fileUploader) {
                    fileUploader.abortUploading();
                } else {
                    visualisation.abortTransfer(uin);

                    // isStarted = false;
                    // uinDirect = 0;
                }

                if (uin) {
                    $rootScope.$broadcast(window._messages_.chatFrame.addCustomMSG, [{
                        UIN: uin,
                        Msg: mcLang(584) // "584":"Передача файлов прервана",
                    }]);

                    webix.message(mcLang(584));

                    $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                        mcConst._CMD_.ce_file_direct_upload_abort
                    ]);
                }

                // setTimeout(function () { // todo: передалать весь модуль нах
                    isStarted = false;
                // }, 50);
            }
        });

        $rootScope.$on(_msg.on_file_upload_complete, function (e, args) {
            if (isStarted && visualisation.active()) {
                var info = args[0];

                visualisation._hideProgressInfo(true);

                fileUploader.sendFileMessage(info);

                setTimeout(function () { // todo: передалать весь модуль нах
                    isStarted = false;
                }, 50);
            }
        });

        $rootScope.$on(_msg.on_file_upload_prepare_start, function () {
            if (!isStarted && canStart && visualisation.active()){
                visualisation._showProgressInfo(visualisation.constants.PREPARING);

                isStarted = true;
            }
        });

        $rootScope.$on(_msg.on_file_upload_prepare_progress, function (e, args) {
            if (isStarted && visualisation.active()) {
                var percent = args[0].percent;

                visualisation._onPrepareProgress(percent);
            }
        });
    }

    // --- direct =============================================================

    if (direct) {
        // --- upload ---

        $rootScope.$on(_msg.changeStateForUser, function (e, args) {
            if (isStarted && visualisation.active() && (visualisation.getState() === visualisation.constants.UPLOADIRECT || visualisation.getState() === visualisation.constants.WAITANSWER)) {
                var uin   = args[0];
                var state = args[1];

                if (direct.getCurrentReceiveUIN() == uin && state == mcConst.states.offline) {
                    direct.clear();

                    $rootScope.$broadcast(window._messages_.chatWrapper.hideTool, [mcConst.dataModels.ReceiveFiles]);
                } else
                if (uinDirect == uin) {
                    visualisation._hideProgressInfo();
                }

                isStarted = false;
            }
        });

        $rootScope.$on(_msg.on_file_direct_upload_wait_accept, function (e, args) {
            if (!isStarted && visualisation.active()) {
                visualisation._showProgressInfo(visualisation.constants.WAITANSWER);
            }
        });

        $rootScope.$on(_msg.on_file_direct_upload_accept, function (e, args) {
            if (!isStarted && visualisation.active() && visualisation.getState() === visualisation.constants.WAITANSWER) {
                var info = args[0];

                visualisation._showProgressInfo(visualisation.constants.UPLOADIRECT);

                isStarted = true;

                $rootScope.$broadcast(window._messages_.clientData.sendCMDToElectron, [
                    mcConst._CMD_.ce_file_direct_upload_start,
                    info
                ]);

                if (!$rootScope.isWebClient) {
                    $rootScope.$broadcast(window._messages_.mcStatistics.setStatValue, ["n_file_direct", 1, false]); // false - увеличить значение на "1"
                }
            }
        });

        $rootScope.$on(_msg.on_file_direct_upload_complete, function (e, args) {
            if (isStarted && visualisation.active() && uinDirect) {
                var files = args[0];

                $rootScope.$broadcast(window._messages_.chatFrame.addCustomMSG, [{
                    MsgType: mcConst._CMD_.msgType.SHOW_UPLOADED_FILES,
                    UIN: uinDirect,
                    FilesList: files.FilesList,
                    Count: files.FilesCount,
                    Size: files.FilesSize,
                    Path: files.filesPath
                }]);

                visualisation._hideProgressInfo(true);

                isStarted = false;
            }
        });

        $rootScope.$on(_msg.sendFilesToUser, function (e, args) {
            if (!isStarted && visualisation.active()) {
                _sendFilesToUser.apply(null, args);
            }
        });

        // --- download ---

        $rootScope.$on(_msg.acceptReceiveFiles, function (e, args) {
            if (!isStarted && visualisation.active()){
                direct.accept.apply(direct, args); // isStarted = true in onAccept

                if (!$rootScope.isWebClient) {
                    $rootScope.$broadcast(window._messages_.mcStatistics.setStatValue, ["n_frcvd", 1, false]); // false - увеличить значение на "1"
                }
            }
        });

        $rootScope.$on(_msg.rejectReceiveFiles, function () {
            if (isStarted && visualisation.active()) {
                direct.reject();

                isStarted = false;
            }
        });

        $rootScope.$on(_msg.on_file_request_transfer, function (e, args) {
            if (!isStarted && visualisation.active()) {
                visualisation._showProgressInfo(visualisation.constants.DOWNLOADPREP, true);

                visualisation._showDownloadFileList.apply(visualisation, args);
            }
        });

        $rootScope.$on(_msg.on_file_direct_upload_no_files, function () {
            if (!isStarted && visualisation.active()) {
                visualisation._hideProgressInfo(true);

                isStarted = false
            }
        });

        $rootScope.$on(_msg.on_file_direct_download_complete, function () {
            if (visualisation.active()) {
                var files = direct.getInfo();

                $rootScope.$broadcast(window._messages_.chatFrame.addCustomMSG, [{
                    MsgType: mcConst._CMD_.msgType.SHOW_RECEIVED_FILES,
                    UIN: files.UIN,
                    FilesList: files.FilesList,
                    Count: files.Count,
                    Size: files.Size,
                    Path: files.DownloadPath
                }]);

                visualisation._hideProgressInfo(true);

                isStarted = false;
                uinDirect = 0;
            }
        });

        $rootScope.$on(_msg.on_file_direct_download_file_received, function (e, args) {
            if (isStarted && visualisation.active()) {
                direct.addReceiveFile.apply(null, args);
            }
        });

        $rootScope.$on(_msg.on_file_direct_receive_client_disconnected, function (e, args) {
            if (isStarted && visualisation.active() && direct.getCurrentReceiveUIN() === args[0]) {
                visualisation.abortTransfer(args[1]);

                isStarted = false;
                uinDirect = 0;
            }
        });
    }
}
