/**
 * ts-source-project.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
**/

'use strict';

var async = require('async');
var extfs = require('fs-extra');
var fs = require('fs');
var os = require('os');
var path = require('path');
var util = require('../org.tizen.ts.base.common/util.js');
var _ = require('underscore');

var builder = require('./builder.js');
var git = require('../dibs.scm.git/git.js');
var installer = require('../org.tizen.ts.base.common/package-installer.js');
var Monitor = require('../../lib/monitor.js');

module.exports = TsSourceJob;

function TsSourceJob(options) {
    this.name = options.name;
    this.type = 'Tizen-Source';
    this.os = options.os;
    this.hostOs = options.hostOs;
    this.version = options.version;
    this.srcPath = options.srcPath;
    this.packages = options.packages;
    this.pkgInfo = options.pkgInfo;
    this.log = options.log || console;
    this.buildRoot = options.buildRoot || path.join(os.tmpdir(), 'ts-cli', this.name);
}

TsSourceJob.prototype.build = build;

function build(repo, opts, callback) {
    var self = this;

    self.log.info('Start Build - ' + self.name);
    var monitor = new Monitor({
        onProgress: function (info, cb) {
            if (info.logType) {
                self.log[info.logType](info.log);
            }
            cb(null);
        }
    });

    var dist = repo.distributions[repo.distName];
    if (!dist) {
        return callback(new Error('# - Failed to get \'' + repo.distName + '\''));
    }

    async.waterfall([
        function (cb) {
            if (opts.cloneSource) {
                self.log.info('Copy source to workspace ' + path.join(self.buildRoot, 'source'));
                cloneSource(self, opts, cb);
            } else {
                return cb(null);
            }
        },
        function (cb) {
            installArchiveDependencyPackages(self, repo, dist, monitor, opts, cb);
        },
        function (cb) {
            installBuildDependencyPackages(self, repo, dist, monitor, opts, cb);
        }, function (cb) {
            self.log.info('Build without toolchain');
            builder.build(self, {
                TARGET_OS: self.os
            }, monitor, cb);
        },
        function (packageFileList, cb) {
            if (opts.push) {
                self.log.info('Push packages...');
                dist.registerPackages(packageFileList, {force: opts.force}, monitor, function (err) {
                    if (err) {
                        self.log.error('Push packages failed...');
                    } else {
                        self.log.info('Push packages success...');
                    }
                    cb(err);
                });
            } else {
                cb(null);
            }
        }
    ], function (err) {
        if (err) {
            self.log.error('build fail.');
            self.log.error(err);
        } else {
            self.log.info(self.name + ' build success.');
        }
        callback(err);
    });
}

function cloneSource(job, opts, callback) {
    git.clone(job.srcPath, {targetDir: path.join(job.buildRoot, 'source')}, function (err, srcPath) {
        job.orgSrcPath = job.srcPath;
        job.srcPath = srcPath;
        callback(err);
    });
}

function getBuildDependencies(manifest, targetOS) {
    var result = [];
    _.each(manifest, function (pkg) {
        if (pkg.os !== targetOS) {
            return;
        }

        _.each(pkg.buildDepList, function (dep) {
            var depOS = (dep.os === undefined) ? targetOS : dep.os;
            var filtered = result.filter(function (depPkg) {
                return depPkg.name === dep.packageName && depPkg.os === targetOS;
            });
            if (_.findWhere(result, {name: dep.packageName, os: depOS})) {
                return;
            } else {
                result.push({name: dep.packageName, os: depOS});
            }
        });
    });
    return result;
}

function installArchiveDependencyPackages(job, repo, dist, monitor, opts, callback) {
    job.log.info('Check archive dependent packages');
    var depPkgs = util.package.getSourceDepsOfPackages(job.pkgInfo.packages);
    if (_.isEmpty(depPkgs)) {
        return callback(null);
    }

    var snapshot = dist.latestSnapshot;
    if (!snapshot) {
        return callback('Failed install dependency packages => Not found snapshot');
    }

    getArchiveDependencyPackageFile(job, depPkgs, repo, dist, snapshot, monitor, callback);
}

function getArchiveDependencyPackageFile(job, pkgs, repo, dist, snapshot, monitor, callback) {
    var depPkgPathList = [];
    var depPkgList = [];
    for (var i = 0; i < pkgs.length; i++) {
        var pkg = pkgs[i];
        if (!snapshot || !snapshot.archivePackages) {
            return callback('Not found build dependent package in snapshot ' + pkg.name);
        }
        job.log.info('  ===> ' + pkg.name);
        depPkgPathList.push(path.join(dist.path, 'source', pkg.name));
        depPkgList.push(pkg.name);
    }

    if (repo.isRemoteRepo) {
        var downloadPath = path.join(os.tmpdir(), 'ts-cli', '.download');
        snapshot.downloadPackages(dist.path, depPkgList, downloadPath, monitor, callback);
    } else {
        async.each(depPkgPathList, function (pkgPath, cb) {
            extfs.copy(pkgPath, path.join(job.buildRoot, path.basename(pkgPath)), cb);
        }, function (err) {
            callback(err);
        });
    }
}

function installBuildDependencyPackages(job, repo, dist, monitor, opts, callback) {
    job.log.info('Check build dependent packages');
    var buildDepPkgs = util.package.getBuildDepsOfPackages(job.pkgInfo.packages, job.os);
    if (_.isEmpty(buildDepPkgs)) {
        return callback(null);
    }

    var snapshot = dist.latestSnapshot;
    if (!snapshot) {
        return callback('Failed install dependency packages => Not found snapshot');
    }

    async.waterfall([
        function (cb) {
            job.log.info('Get path of build-dependent packages');
            getBuildDependencyPackageFilePath(job, buildDepPkgs, repo, dist, snapshot, monitor, cb);
        },
        function (buildDepPkgPathList, cb) {
            job.log.info('Install build-dependent packages');
            installer.installLocalPackages(buildDepPkgPathList, job.buildRoot, job.os, snapshot, dist.path,
                { clean: opts.clean, localPkgs: [] }, monitor, cb);
        }
    ], callback);
}

function getBuildDependencyPackageFilePath(job, pkgs, repo, dist, snapshot, monitor, callback) {
    var buildDepPkgPathList = [];
    var buildDepPkgList = [];
    for (var i = 0; i < pkgs.length; i++) {
        var pkg = pkgs[i];
        if (!snapshot.osPackages || !snapshot.osPackages[pkg.os] || !snapshot.osPackages[pkg.os][pkg.name]) {
            monitor.updateProgress({
                log: 'Not found build dependent package in snapshot ' + pkg.name + '(' + pkg.os + ')',
                logType: 'error'
            });
            return callback('Not found build dependent package in snapshot ' + pkg.name + '(' + pkg.os + ')');
        }
        job.log.info('  ===> ' + pkg.name + '(' + pkg.os + ')');
        buildDepPkgPathList.push(path.join(dist.path, snapshot.osPackages[pkg.os][pkg.name].path));
        buildDepPkgList.push(snapshot.osPackages[pkg.os][pkg.name]);
    }

    if (repo.isRemoteRepo) {
        var downloadPath = path.join(os.tmpdir(), 'ts-cli', '.download');
        snapshot.downloadPackages(dist.path, buildDepPkgList, downloadPath, monitor, callback);
    } else {
        callback(null, buildDepPkgPathList);
    }
}
