/**
 * reverse-build.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 JobDep = require('./job-dep.js');
var DError = require('../../core/exception');
var utils = require('../../lib/utils');

/**
 * Tizen reverse build module
 * @module models/tizen-project/reverse-build.js
 */

module.exports.check = function (job, monitor, callback) {
    var subJobIds = [];
    monitor.updateProgress('Reverse build start');

    async.waterfall([
        function (cb) {
            job.board.push({
                type: 'STAMP',
                time: utils.getTimeString(),
                name: 'reverse build start'
            });
            monitor.updateProgress('Getting all reverse build target...');
            getReverseBuildTargets(job, monitor, cb);
        },
        function (targets, cb) {
            // skip when no reverse target.
            if (targets.length <= 0) {
                return cb(null);
            }

            monitor.updateProgress('Creating reverse job(s)...');
            createReverseBuildJobs(job, targets, monitor, function (err1, result) {
                subJobIds = result;
                cb(err1);
            });
        },
        function (cb) {
            // skip when no reverse target.
            if (subJobIds.length <= 0) {
                return cb(null);
            }

            monitor.updateProgress('Waiting for all reverse job(s) finished...');
            waitForAllJobsFinished(subJobIds, monitor, cb);
        }
    ], function (err) {
        if (err) {
            monitor.updateProgress('Reverse build failed');
        } else {
            monitor.updateProgress('Reverse build succeeded');
        }
        job.board.push({
            type: 'STAMP',
            time: utils.getTimeString(),
            name: 'reverse build done'
        });
        callback(err, subJobIds);
    });
};


function getReverseBuildTargets(job, monitor, callback) {
    // get all reverse packages
    var reversePkgs = JobDep.getReverseBuildDepPkgs(job.options.packages, job.snapshot);
    monitor.updateProgress(' - reverse build target packages: [' + reversePkgs.map(function (e) {
        return e.name + '(' + e.os + ')';
    }).join(' ') + ']');

    // get projects from reverse packages
    getBuildTargetsFromReversePkgs(reversePkgs, job.snapshot, monitor, callback);
}


function getBuildTargetsFromReversePkgs(reversePkgs, snapshot, monitor, callback) {
    var results = [];
    _.each(reversePkgs, function (pkg) {
        if (pkg.options && pkg.options['__PROJECT_TYPE'] === 'Tizen-Source') {
            var pkgInfo = pkg.options;

            if (pkg.os && pkgInfo['__PROJECT_NAME'] &&
                pkgInfo['__GIT_COMMIT_ID'] && pkgInfo['__GIT_REPO']) {

                var tempObj = {
                    projectName: pkgInfo['__PROJECT_NAME'],
                    environmentName: pkg.os,
                    gitCommit: pkgInfo['__GIT_COMMIT_ID'],
                    gitRepo: pkgInfo['__GIT_REPO'],
                    gitBranch: pkgInfo['__GIT_BRANCH'],
                    useGitSubmodule: pkgInfo['__USE_GIT_SUBMODULES'] || false,
                    preBuild: pkgInfo['__PRE_BUILD'] || [],
                    postBuild: pkgInfo['__POST_BUILD'] || []
                };

                var flag = true;
                _.each(results, function (result) {
                    if (result.gitCommit === tempObj.gitCommit &&
                        result.environmentName === tempObj.environmentName) {
                        flag = false;
                    }
                });

                if (flag) {
                    results.push(tempObj);
                }
            }
        }
    });
    callback(null, results);
}


function createReverseBuildJobs(parentJob, targets, monitor, callback) {
    var depFiles = parentJob.resultFiles.join(',');

    monitor.updateProgress(' - requesting reverse job(s): ');
    async.mapLimit(targets, 10,
        function (target, cb) {
            monitor.updateProgress(' --' + target.projectName + '(' + target.environmentName + ')');
            dibs.rpc.jobmgr.addSubJob(parentJob.id, 'Tizen-Reverse', target.environmentName, {
                TARGET_OS: target.environmentName,
                UPLOAD: false,
                BUILD_ONLY: true,
                REVERSE_BUILD: true,
                DEP_FILES: depFiles,
                GIT_COMMIT: target.gitCommit,
                GIT_REPO: target.gitRepo,
                GIT_BRANCH: target.gitBranch,
                USE_GIT_SUBMODULES: target.useGitSubmodule,
                PROJECT_NAME: target.projectName,
                PRE_BUILD: target.preBuild,
                POST_BUILD: target.postBuild
            }, cb);
        },
        function (err, results) {
            callback(err, results);
        });
}


function waitForAllJobsFinished(jobIds, monitor, callback) {
    if (jobIds.length === 0) {
        callback(null); return;
    }

    async.each(jobIds,
        function (jobId, cb) {
            dibs.rpc.jobmgr.waitForJobStatus(jobId, 'FINISHED', function (err, status) {
                if (err) {
                    monitor.updateProgress('Reverse job \'' + jobId + '\' failed with \'' + status + '\'');
                    cb(new DError('TIZENJOB009', { jobId: jobId }, err));
                } else {
                    cb(null);
                }
            });
        },
        function (err) {
            if (err) {
                monitor.updateProgress('Canceling all reverse job(s)...');
                cancelUnfinishedJobs(jobIds, monitor, function (err2) {
                    callback(err);
                });
            } else {
                callback(null);
            }
        });
}


function cancelUnfinishedJobs(jobIds, monitor, callback) {
    if (jobIds.length === 0) {
        callback(null); return;
    }

    async.each(jobIds,
        function (jobId, cb) {
            monitor.updateProgress(' - sending cancel request for ... ' + jobId);
            dibs.rpc.jobmgr.cancelJob(jobId, function (err) {
                cb(null);
            });
        },
        function (err) {
            callback(null);
        }
    );
}
