/**
 * release-image.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 path = require('path');
var url = require('url');
var _ = require('underscore');

var utils = require('../../lib/utils.js');
var util = require('../org.tizen.ts.base.common/util.js');
var Zip = require('../dibs.core/zip.js');

module.exports.generateImage = generateImage;

function generateImage(distName, distPath, baseRepoUrl, snapshot, os, options, monitor, callback) {
    var targetSnapshot = _.clone(snapshot);

    if (!targetSnapshot.osPackages) {
        return callback(new Error(targetSnapshot.name + ' does not include packages'));
    }

    var metaPkgList = [];
    var installTypePkgNameList = [];
    var imgPkgList = [];

    var excludePkgList = null;
    if (options && options.excludePkgList) {
        if (_.isArray(options.excludePkgList)) {
            excludePkgList = options.excludePkgList;
        } else {
            excludePkgList = options.excludePkgList.split(',');
        }
    }

    var userInputUID = null;
    if (options && options.userInputUID) {
        userInputUID = options.userInputUID;
    }

    var skipIntegrityCheck = false;
    if (options && options.skipIntegrityCheck) {
        skipIntegrityCheck = true;
    }

    var imageName = snapshot.name;
    if (options && options.outputName) {
        imageName = options.outputName;
    }
    var workPath = path.join(process.cwd(), 'tmp', 'image', imageName);
    var imageFileName = imageName + '_' + os + '.zip';

    async.waterfall([
        function (cb) {
            // intialize temporary workspace
            monitor.updateProgress(' # - Remove if temporary workspace exists ' + workPath);
            utils.removePathIfExist(workPath, cb);
        },
        function (cb) {
            monitor.updateProgress(' # - Create temporary workspace');
            extfs.mkdirp(workPath, function (err) {
                cb(err);
            });
        },
        function (cb) {
            _.each(targetSnapshot.osPackages[os], function (pkg) {
                if (pkg.attr === 'root' || pkg.attr === 'mandatory') {
                    metaPkgList.push(pkg);
                } else if (pkg.attr === 'install') {
                    installTypePkgNameList.push(pkg);
                }
            });

            monitor.updateProgress(' # - Meta-package list');
            monitor.updateProgress('   - ' + _.pluck(_.values(metaPkgList), 'name'));
            if (installTypePkgNameList && !_.isEmpty(installTypePkgNameList)) {
                monitor.updateProgress(' # - Install-type package list');
                monitor.updateProgress('   - ' + _.pluck(_.values(installTypePkgNameList), 'name'));
            }

            cb(null);
        },
        function (cb) {
            if (excludePkgList && !_.isEmpty(excludePkgList)) {
                validateExcludeList(metaPkgList, excludePkgList, cb);
            } else {
                monitor.updateProgress(' # - Ignore checking exclude meta list.');
                cb(null);
            }
        },
        function (cb) {
            if (excludePkgList && !_.isEmpty(excludePkgList)) {
                var excludeMetapackageList = [];
                monitor.updateProgress(' # - Excluding those meta packages');
                monitor.updateProgress(' # - ' + _.values(excludePkgList));

                var tempList = _.filter(metaPkgList, function (metaPkg) {
                    return (_.indexOf(excludePkgList, metaPkg.name) !== -1);
                });

                targetSnapshot.getAllInstallDependentPackages(tempList, {}, monitor, function (err, results) {
                    if (err) {
                        monitor.updateProgress({ log: err, logType: 'error' });
                        cb(err);
                    } else {
                        _.each(results, function (pkg) {
                            if (pkg.attr === 'root') {
                                excludeMetapackageList.push(pkg);
                            }
                        });
                        cb(null, _.difference(metaPkgList, _.union(tempList, excludeMetapackageList)));
                    }
                });
            } else {
                imgPkgList = _.union(metaPkgList, installTypePkgNameList);
                cb(null, metaPkgList);
            }
        },
        function (results, cb) {
            // get install dependency pkg list
            if (excludePkgList && !_.isEmpty(excludePkgList)) {
                var tempList = _.filter(metaPkgList, function (metaPkg) {
                    return (_.indexOf(excludePkgList, metaPkg.name) !== -1);
                });
                targetSnapshot.getAllInstallDependentPackagesWithExcludeList(results, tempList, monitor, cb);
            } else {
                monitor.updateProgress(' # - Get all install-dependent packages from snapshot');
                var opts = {};
                if (skipIntegrityCheck) {
                    opts.skipIntegrityCheck = skipIntegrityCheck;
                }
                targetSnapshot.getAllInstallDependentPackages(results, opts, monitor, cb);
            }
        },
        function (downloadPkgs, cb) {
            monitor.updateProgress(' # - Generating temporary directory for download');

            imgPkgList = _.union(imgPkgList, downloadPkgs, installTypePkgNameList);
            utils.genTemp(function (err, tempDir) {
                cb(err, downloadPkgs, tempDir);
            });
        },
        function (downloadPkgs, tempDir, cb) {
            // download packages
            monitor.updateProgress(' # - Downloading packages from \'' + distPath + '\'');
            targetSnapshot.downloadPackages(distPath, downloadPkgs, tempDir, monitor, function (err, downloadPath) {
                cb(err, downloadPath);
            });
        },
        function (downloadPath, cb) {
            // move download packages into workspace
            monitor.updateProgress(' # - Copying ' + downloadPath.length + ' packages into workspace');

            async.eachLimit(downloadPath, 4, function (pkgPath, cb1) {
                var pkgName = path.basename(pkgPath);
                monitor.updateProgress('  ## - Copying ' + pkgName);
                extfs.copy(pkgPath, path.join(workPath, 'binary', pkgName), function (err1) {
                    cb1(err1);
                });
            },
            function (err) {
                cb(err);
            });
        },
        function (cb) {
            // write image.info, pkg_list_xxx, os_info files
            monitor.updateProgress(' # - Writing image info files');

            var imgInfoContents =
                'origin : ' + url.resolve(baseRepoUrl, './') + '\n' +
                'distribution : ' + distName;

            if (userInputUID) {
                imgInfoContents += '\ndistribution_id : ' + userInputUID;
            }

            saveImageInfoFiles(workPath, os, imgInfoContents, {
                imgPkgList: imgPkgList,
                excludePkgList: excludePkgList
            }, cb);
        },
        function (cb) {
            // compress imag into zip file
            var imageFilePath = path.join(process.cwd(), imageFileName);

            monitor.updateProgress(' # - Compress image file into \'' + imageFilePath + '\'');
            Zip.compress(imageFilePath, workPath, {}, cb);
        },
        function (cb) {
            // Clean tempWorkspace
            monitor.updateProgress(' # - Remove workspace \'' + workPath + '\'');
            utils.removePathIfExist(workPath, cb);
        }
    ],
    function (err) {
        callback(err);
    });
}


function validateExcludeList(metaPackageList, excludePackageList, callback) {
    async.eachSeries(excludePackageList, function (pkg, cb) {
        var exists = _.where(metaPackageList, { name: pkg });
        if (!_.isEmpty(exists)) {
            cb(null);
        } else {
            cb(new Error(pkg + ' is not in repository. (case-sensitive).'));
        }
    }, function (err) {
        callback(err);
    });
}


function saveImageInfoFiles(workspacePath, os, imgInfoContents, options, callback) {
    var excludePkgList = null;
    if (options && options.excludePkgList) {
        excludePkgList = options.excludePkgList;
    }

    var imgPkgList = null;
    if (options && options.imgPkgList) {
        imgPkgList = options.imgPkgList;
    }

    async.waterfall([
        function (cb) {
            fs.writeFile(path.join(workspacePath, 'image.info'), imgInfoContents, cb);
        },
        function (cb) {
            fs.writeFile(path.join(workspacePath, 'os_info'), os, cb);
        },
        function (cb) {
            if (excludePkgList && !_.isEmpty(excludePkgList)) {
                _.each(imgPkgList, function (pkg) {
                    pkg.installDepList = _.reject(pkg.installDepList, function (depPkg) {
                        return _.contains(excludePkgList, depPkg.packageName);
                    });
                });
                fs.writeFile(path.join(workspacePath, 'pkg_list_' + os), util.package.pkgListString(imgPkgList), cb);
            } else {
                fs.writeFile(path.join(workspacePath, 'pkg_list_' + os), util.package.pkgListString(imgPkgList), cb);
            }
        }
    ],
    function (err) {
        callback(err);
    });
}
