/**
 * dfs-remote.js
 * Copyright (c) 2000 - 2017 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Contact:
 * Sungmin Kim <sm.art.kim@samsung.com>
 * Jonghwan Park <iwin100.park@samsung.com>
 * Kitae Kim <kt920.kim@samsung.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 * - S-Core Co., Ltd
**/

var path = require('path');
var extfs = require('fs-extra');
var fs = require('fs');
var async = require('async');

var DError = require('../../core/exception.js');
var dibs = require('../../core/dibs.js');
var dfs = require('./dist-fs.js');
var utils = require('../../lib/utils.js');
var FileSystem = require('../dibs.core/filesystem.js');

/**
 * @module core/dfs/dfs-remote
 */

/**
 * @function __existsFile
 * @param {string} dfsPath - rPath
 * @param {module:lib/utils.callback_err_bool} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, result )
module.exports.__existsFile = function (dfsPath, callback) {
    var current = dibs.thisServer;

    if (dfs === undefined) {
        callback(null, false);
        return;
    }

    // check index server
    if (current.id !== dfs.indexServer.id) {
        callback(null, false);
        return;
    }

    callback(null, dfs.masterIndex.existsFile(dfsPath));
};


// callback( err, result )
module.exports.__searchFiles = function (opts, callback) {
    var current = dibs.thisServer;

    // check index server
    if (current.id !== dfs.indexServer.id) {
        callback(new DError('DFS008'));
        return;
    }

    // register
    dfs.masterIndex.searchFiles(opts, callback);
};


/**
 * @function __addUploadedFile
 * @param {string} incomingPath - rPath
 * @param {string} dfsPath - rPath
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, dfsPath )
module.exports.__addUploadedFile = function (incomingFileName, dfsPath, callback) {
    if (dfs === undefined) {
        var error = new DError('DFS006');
        callback(error, null);
        return;
    }

    var lPath = path.join(dfs.getIncomingPath(), incomingFileName);
    var newLPathDir = path.join(utils.getTempDirPath(),
        utils.generateTimeStamp(true));
    var newLPath = '';

    async.waterfall([
        // check if incoming file exists
        function (cb) {
            utils.retryCheckingFileExists(lPath, 3, function (exists) {
                if (!exists) {
                    cb(new DError('DFS021', {
                        file: lPath
                    }));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            var exist = true;
            var count = 0;
            var oldDir = newLPathDir;
            async.doWhilst(
                function (dcb) {
                    fs.exists(newLPathDir, function (truth) {
                        exist = truth;
                        dcb(null);
                    });
                },
                function () {
                    if (exist) {
                        newLPathDir = oldDir + ('_' + (count++));
                    }
                    return exist;
                }, function () {
                    cb(null);
                });
        },
        function (cb) {
            newLPath = path.join(newLPathDir, path.basename(lPath).replace(/\.[^/.]+$/, ''));
            extfs.mkdirs(path.dirname(newLPath), function (err) {
                cb(err);
            });
        },
        function (cb) {
            FileSystem.move(lPath, newLPath, cb);
        },
        function (cb) {
            dfs.addFile(dfsPath, newLPath, cb);
        }],
        function (err, dfsPath2) {
            if (!err) {
                // remove the file in incoming dirname
                async.series([
                    function (cb) {
                        fs.unlink(newLPath, cb);
                    },
                    function (cb) {
                        fs.rmdir(newLPathDir, cb);
                    }], function () {
                    callback(err, dfsPath2);
                });
            } else {
                callback(err, dfsPath2);
            }
        });
};



/**
 * get index server
 * NOTE. This method can be called only of MASTER's
 * @function __getIndexServer
 * @param {string} sid - sid
 * @param {module:core/base-server.callback_err_serverid} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, id )
module.exports.__getIndexServer = function (sid, callback) {
    // check master server
    if (dibs.thisServer.type !== 'master') {
        callback(new DError('DFS015'), null);
        return;
    }

    if (dfs.indexServer === null) {
        dfs.indexServer = dibs.getServer(sid);
        callback(null, sid);
    } else {
        callback(null, dfs.indexServer.id);
    }
};


/**
 * register index
 * @function __registerIndex
 * @param {string} sid - sid
 * @param {string} index - index
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err )
module.exports.__connect = function (sid, index, callback) {
    var current = dibs.thisServer;

    // check index server
    if (current.id !== dfs.indexServer.id) {
        callback(new DError('DFS008'));
        return;
    }

    // register
    if (!dfs.masterIndex.registerServer(sid, index)) {
        callback(new DError('DFS016', {
            index: index,
            server: sid
        }));
    } else {
        callback(null);
    }
};


// callback( err )
module.exports.__disconnect = function (sid, callback) {
    var current = dibs.thisServer;

    // check index server
    if (current.id !== dfs.indexServer.id) {
        callback(new DError('DFS008'));
        return;
    }

    // unregister
    dfs.masterIndex.unregisterServer(sid, callback);
};


// create new single file index
// callback( err )
module.exports.__createNewFileIndex = function (sid, options, callback) {
    var masterIndex = dfs.masterIndex.getRawIndex();
    var count = 0;

    // if 'name' is not specified, use default name
    if (!options.name) {
        return callback(new DError('DFS019', {
            msg: 'File name is not specified!'
        }), null);
    }

    var dfsPath = [sid, (count).toString(), options.name].join('/');
    while (masterIndex[dfsPath] !== undefined) {
        dfsPath = [sid, (count++).toString(), options.name].join('/');
    }

    masterIndex[dfsPath] = {
        servers: [],
        size: -1,
        checksum: null
    };

    callback(null, dfsPath);
};


/**
 * @function __registerFileIndex
 * @param {string} sid - sid
 * @param {string} dfsPath - rPath
 * @param {module:core/dfs/dist-fs.masterinfo} fInfo - fInfo
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// register single file index
// callback( err )
module.exports.__registerFileIndex = function (sid, dfsPath, fInfo, callback) {
    var current = dibs.thisServer;

    // check index server
    if (current.id !== dfs.indexServer.id) {
        callback(new DError('DFS008'));
        return;
    }

    // register file index
    async.waterfall([
        function (cb) {
            dfs.masterIndex.registerFile(sid, dfsPath, fInfo, cb);
        }], function (err) {
        callback(err);
    });
};



/**
 * @function __getFileIndex
 * @param {string} dfsPath - rPath
 * @param {module:core/dfs/dist-fs.callback_err_info} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, fileInfo )
module.exports.__getFileIndex = function (sid, dfsPath, callback) {
    var current = dibs.thisServer;

    // check index server
    if (current.id !== dfs.indexServer.id) {
        callback(new DError('DFS008'), null);
        return;
    }

    // get file index
    async.waterfall([
        function (cb) {
            dfs.masterIndex.getFile(dfsPath, cb);
        }], function (err, index) {
        callback(err, index);
    });
};



/**
 * @function __removeFileIndex
 * @param {string} sid - sid
 * @param {string} dfsPath - rPath
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, fileInfo )
module.exports.__removeFileIndex = function (sid, dfsPath, callback) {
    var current = dibs.thisServer;

    // check index server
    if (current.id !== dfs.indexServer.id) {
        callback(new DError('DFS008'), null);
        return;
    }

    // remove file index
    async.waterfall([
        function (cb) {
            dfs.masterIndex.removeFile(sid, dfsPath, cb);
        }], function (err) {
        callback(err);
    });
};



/**
 * @function __aquireFileLock
 * @param {string} sid - sid
 * @param {string} dfsPath - rPath
 * @param {object} opt - opt
 * @param {module:core/dfs/dfs-lock.callback_err_lock} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, lock )
module.exports.__aquireFileLock = function (sid, dfsPath, opt, callback) {
    dfs.masterIndex.aquireFileLock(sid, dfsPath, opt, callback);
};


/**
 * @function __releaseFileLock
 * @param {module:core/dfs/dfs-lock.FileLock} lock - lock
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err )
module.exports.__releaseFileLock = function (lock, callback) {
    dfs.masterIndex.releaseFileLock(lock, callback);
};



/**
 * remote method for monitor
 * opt.type: "MASTER_INDEX", "LOCAL_INDEX", "MASTER_LOCK"
 * @function __monitorDFS
 * @param {object} opt - opt
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, indexTable )
module.exports.__monitorDFS = function (opt, callback) {
    dfs.monitor(opt, callback);
};




/**
 * @function __testCleanRepository
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

//
// remote method for test
//
// callback( err )
module.exports.__testCleanRepository = function (callback) {
    dfs.cleanRepository();
    callback(null);
};

/**
 * @function __testAddFileToServer
 * @param {string} dfsPath - rPath
 * @param {string} lPath - lPath
 * @param {string} sid - sid
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, dfsPath )
module.exports.__testAddFileToServer = function (lPath, sid, callback) {
    var server = dibs.getServer(sid);
    dfs.addFileToServer(null, lPath, server, callback);
};

/**
 * @function __testAddFile
 * @param {string} dfsPath - rPath
 * @param {string} lPath - lPath
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, dfsPath )
module.exports.__testAddFile = function (lPath, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    dfs.addFile(null, lPath, options, callback);
};

/**
 * @function __testExistsFile
 * @param {string} dfsPath - rPath
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, result )
module.exports.__testExistsFile = function (dfsPath, callback) {
    dfs.existsFile(dfsPath, callback);
};

/**
 * @function __testGetFile
 * @param {string} lPath - lPath
 * @param {string} dfsPath - rPath
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err )
module.exports.__testGetFile = function (lPath, dfsPath, callback) {
    dfs.getFile(lPath, dfsPath, callback);
};

/**
 * @function __testGetFileIndex
 * @param {string} dfsPath - rPath
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err, index )
module.exports.__testGetFileIndex = function (dfsPath, callback) {
    dfs.indexServer.__getFileIndex(null, dfsPath, callback);
};

/**
 * @function __testRemoveFile
 * @param {string} dfsPath - rPath
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err )
module.exports.__testRemoveFile = function (dfsPath, callback) {
    dfs.removeFile(dfsPath, callback);
};


/**
 * @function __testSetDFSMaxSize
 * @param {number} size - size
 * @param {module:lib/utils.callback_error} callback - callback
 * @memberOf module:core/dfs/dfs-remote
 */

// callback( err )
module.exports.__testSetDFSMaxSize = function (size, callback) {
    dfs.setMaxSize(size);
    callback(null);
};
