/**
 * direct-sync.js
 * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Contact:
 * DongHee Yang <donghee.yang@samsung.com>
 * Sungmin Kim <sm.art.kim@samsung.com>
 * Jiil Hyoun <jiil.hyoun@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 extfs = require('fs-extra');
var async = require('async');

var dfs = require('../../plugins/dibs.dist-fs/dist-fs.js');
var Distribution = require('./distribution.js');
var Snapshot = require('./snapshot.js');
var RemoteRepo = require('./remote-repo.js');
var Utils = require('../../lib/utils.js');


module.exports.synchronize = synchronize;
module.exports.updateSyncOptions = updateSyncOptions;


function synchronize(dist, opts, parent, callback) {
    async.waterfall([
        // open remote
        function (cb) {
            var snapshotOpt = { snapshotName: null};
            if (opts.SYNC_SNAPSHOT) {
                snapshotOpt.snapshotName = opts.SYNC_SNAPSHOT;
            }

            if (opts.progress) {
                opts.progress('# Getting distribution info from remote...');
            }
            RemoteRepo.getRemoteDistribution(dist.syncURL, dist.syncDistName, snapshotOpt, cb);
        },
        // synchronize
        function (syncDist, cb) {
            synchronizeRemoteDistribution(dist, syncDist, opts, parent, cb);
        }
    ], function (err) {
        callback(err, dist);
    });
}


function synchronizeRemoteDistribution(dist, syncDist, opts, parent, callback) {
    var syncSnapshot = syncDist.latestSnapshot;
    var targetSnapshot = dist.latestSnapshot;

    if (opts.progress) {
        opts.progress('# Checking updated packages...');
    }

    // get latest snapshot of distribution
    var pkgs = Snapshot.getUpdatedPackages(syncSnapshot, targetSnapshot, opts);

    // skip synchronizing when no updated pkgs
    if (pkgs.length === 0) {
        if (opts.progress) {
            opts.progress('# Skipping sync distribution because of no updated packages');
        }
        callback(null);
        return;
    }

    var tempPath = null;
    async.waterfall([
        //  create temp dir
        function (cb) {
            Utils.genTemp(cb);
        },
        // download
        function (tempDir, cb) {
            tempPath = tempDir;
            if (opts.progress) {
                opts.progress('# Downloading updated packages...');
            }
            Distribution.downloadPackagesFromRemote(pkgs, syncSnapshot, tempPath, syncDist.path, opts.progress, cb);
        },
        // add
        function (lpaths, cb) {
            if (opts.progress) {
                opts.progress('# Preparing to register downloaded packages...');
            }
            addFiles(lpaths, cb);
        },
        // register
        function (rpaths, cb) {
            if (opts.progress) {
                opts.progress('# Registering updated packages...');
            }
            // NOTE. sync must consider compatibility
            Distribution.registerPackages(rpaths, dist, {
                noChangeLogCheck: true,
                sync: dist.options.SYNC_METHOD,
                forceSync: opts.SYNC_FORCE,
                syncPrefix: opts.SYNC_PREFIX,
                syncSnapshotName: syncSnapshot.name
            }, opts.progress, cb);
        }], function (err) {
        if (tempPath !== null) {
            extfs.remove(tempPath, function () {
                callback(err);
            });
        } else {
            callback(err);
        }
    });
}


function addFiles(lpaths, callback) {
    async.mapLimit(lpaths, 8,
        function (lpath, cb) {
            // NOTE. SYNC must be finished in 1 hour after adding file to DFS
            //      Otherwise, added files will be removed on DFS
            dfs.addFile(null, lpath, {
                lifetime: 60 * 60 * 1000
            }, function (err, dfsPath) {
                cb(err, dfsPath);
            });
        },
        function (err, rpaths) {
            callback(err, rpaths);
        });
}


function updateSyncOptions(dist, opts, parent, callback) {

    // write to config
    parent.setSyncToConfig(dist.name, 'direct', opts.SYNC_URL, opts.SYNC_DIST_NAME, opts.SYNC_PERIOD, opts.SYNC_PREFIX);

    // if auto sync, add
    if (opts.SYNC_URL && opts.SYNC_DIST_NAME && opts.SYNC_PERIOD > 0) {
        addSyncAction(dist.name, opts, parent);
    } else {
        removeSyncAction(dist.name, parent);
    }
    callback(null);
}


function addSyncAction(name, opts, parent) {
    parent.getServer().addSyncAction(name, function (cb) {
        parent.synchronizeDistribution(name, opts, function (err, dist) {
            cb(err);
        });
    }, null, opts.SYNC_PERIOD * 1000);
}


function removeSyncAction(name, parent) {
    parent.getServer().removeSyncAction(name);
}

