/**
 * project-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 async = require('async');
var _ = require('underscore');

var dibs = require('../../core/dibs');
var DError = require('../../core/exception.js');
var SyncDistProject = require('./sync-dist-project.js');

var SYNC_PRJ_NAME = 'dist-sync';
var SYNC_PRJ_TYPE = 'Tizen-Sync-Dist';

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


function synchronize(dist, opts, parent, callback) {
    // check sync option first
    if (!dist.options.SYNC_METHOD || !dist.options.SYNC_URL ||
        !dist.options.SYNC_DIST_NAME) {

        return callback(new DError('TASYNC003'), null);
    }

    dibs.log.info('Synchronizing using binary project...' + dist.name);
    async.waterfall([
        // get distribution owner
        function (cb) {
            getDistributionInfo(dist.name, cb);
        },
        // add sync job
        function (distInfo, cb) {
            var newOpts = _.clone(opts);
            newOpts.SYNC_METHOD = dist.options.SYNC_METHOD;
            newOpts.SYNC_URL = dist.options.SYNC_URL;
            newOpts.SYNC_DIST_NAME = dist.options.SYNC_DIST_NAME;

            addSyncAction(dist.name, newOpts, 'sync-manager@user', parent);
            parent.getServer().waitForNextScheduledAction(dist.name, cb);
        }], function (err) {
        callback(err, dist);
    });
}


function getDistributionInfo(distName, callback) {
    dibs.rpc.datamgr.searchDistributions({
        type: 'tizen',
        name: distName
    }, function (err, dists) {
        if (err) {
            callback(err, null);
        } else if (dists.length === 0) {
            callback(new Error('No such a distribution! ' + distName), null);
        } else {
            callback(null, dists[0]);
        }
    });
}


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

    dibs.log.info('Updating option of binary project synchronization...' + dist.name);
    // reset sync action if exists
    if (!opts.SYNC_URL || !opts.SYNC_DIST_NAME) {
        removeSyncAction(dist.name, parent);

        // write to config
        parent.setSyncToConfig(dist.name, 'project', null, null, -1);

        callback(null);
        return;
    }

    async.waterfall([
        //  wait for data-manager ready
        function (cb) {
            waitForDataManagerReady(cb);
        },
        // add tizen-dist-sync if needed
        function (cb) {
            addSyncProjectIfNeeded(dist.name, opts, cb);
        },
        // get distribution owner
        function (cb) {
            getDistributionInfo(dist.name, cb);
        },
        function (distInfo, cb) {
            // if auto sync, add
            if (opts.SYNC_URL && opts.SYNC_DIST_NAME && opts.SYNC_PERIOD > 0) {
                addSyncAction(dist.name, opts, 'sync-manager@user', parent);
            } else {
                removeSyncAction(dist.name, parent);
            }
            cb(null);
        }], function (err) {
        if (!err) {
            // write to config
            var period = opts.SYNC_PERIOD ? opts.SYNC_PERIOD : -1;
            parent.setSyncToConfig(dist.name, 'project',
                opts.SYNC_URL, opts.SYNC_DIST_NAME, period);
        }
        callback(err);
    });
}


function waitForDataManagerReady(callback) {
    var servers = dibs.getServersByType('datamgr');
    if (servers.length > 0) {
        if (servers[0].status === 'RUNNING') {
            callback(null);
        } else {
            dibs.log.info('Waiting for data-manager server ready...');
            setTimeout(function () {
                waitForDataManagerReady(callback);
            }, 1000);
        }
    } else {
        callback(new DError('TASYNC002'));
    }
}


function addSyncProjectIfNeeded(distName, opts, callback) {
    dibs.rpc.datamgr.searchProjects({
        name: SYNC_PRJ_NAME,
        distName: distName
    }, function (err, prjs) {
        if (err) {
            callback(err); return;
        }

        if (prjs.length === 0) {
            var syncOptions = {
                SYNC_METHOD: opts.SYNC_METHOD,
                SYNC_URL: opts.SYNC_URL,
                SYNC_DIST_NAME: opts.SYNC_DIST_NAME
            };
            if (opts.SYNC_PERIOD) {
                syncOptions.SYNC_PERIOD = opts.SYNC_PERIOD;
            }

            dibs.rpc.datamgr.addProject(SYNC_PRJ_NAME, SYNC_PRJ_TYPE, syncOptions,
                ['ubuntu-32'], [], [], distName,
                function (err) {
                    callback(err);
                });
        } else {
            var prj = prjs[0];
            prj.options.SYNC_METHOD = opts.SYNC_METHOD;
            prj.options.SYNC_URL = opts.SYNC_URL;
            prj.options.SYNC_DIST_NAME = opts.SYNC_DIST_NAME;
            if (opts.SYNC_PERIOD) {
                prj.options.SYNC_PERIOD = opts.SYNC_PERIOD;
            }

            dibs.rpc.datamgr.updateProject(prj, function (err) {
                callback(err);
            });
        }
    });
}


function addSyncAction(distName, opts, distOwner, parent) {
    parent.getServer().addSyncAction(distName, function (callback) {
        async.waterfall([
            function (cb) {
                SyncDistProject.checkUpdatedBinaryProjectPackages(
                    distName, opts.SYNC_URL, opts.SYNC_DIST_NAME, {
                        updateProgress: function () {}
                    }, cb);
            },
            function (pkgs, currDist, syncDist, cb) {
                if (pkgs.length === 0) {
                    return cb(null);
                }

                async.waterfall([
                    function (cb1) {
                        dibs.rpc.jobmgr.addJob(distOwner, distName,
                            SYNC_PRJ_NAME, 'ubuntu-32', {
                                UPLOAD: true
                            }, cb1);
                    },
                    function (jobId, cb1) {
                        dibs.rpc.jobmgr.waitForJobStatus(jobId, 'FINISHED', cb1);
                    }], cb);
            }], function (err) {
            callback(err);
        });
    }, null, opts.SYNC_PERIOD * 1000);
}


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

