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

var utils = require('../../lib/utils.js');
var util = require('../org.tizen.ts.base.common/util.js');
var installer = require('../org.tizen.ts.base.common/package-installer.js');

var Process = require('../dibs.core/process.js');
var Zip = require('../dibs.core/zip.js');

module.exports.generateInstaller = generateInstaller;

var INSTALLER_FILE_NAME = 'tizen-sdk.zip';

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

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

    if (!baseRepoUrl) {
        baseRepoUrl = distPath;
    }

    var metaPkgList = [];
    if (options && options.metaPkgList) {
        if (_.isArray(options.metaPkgList)) {
            metaPkgList = options.metaPkgList;
        } else {
            metaPkgList = options.metaPkgList.split(',');
        }
    }

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

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

    var installerName = null;
    if (options && options.outputName) {
        installerName = options.outputName;
    } else {
        installerName = snapshot.name;
    }

    var workPath = path.join(process.cwd(), 'tmp', 'installer', snapshot.name);
    var hostOS = util.getTargetOS(os.platform(), os.arch()).os;

    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) {
            var targetPkgs = installer.getMetaPackagesToInstall(targetOS, snapshot, metaPkgList, excludePkgList, monitor);
            if (targetPkgs.length === 0) {
                return cb(new Error('No target meta packages'));
            }
            cb(null, targetPkgs);
        },
        function (targetPkgs, cb) {
            var isValid = _.every(targetPkgs, function (metaPkg) {
                var deps = util.package.getInstallDepsOfPackages([snapshot.osPackages[metaPkg.os][metaPkg.name]], metaPkg.os);
                var depMetas = _.filter(deps, function (dep) {
                    return snapshot.osPackages[dep.os][dep.name].attr === 'root';
                });

                if (deps.length === depMetas.length || depMetas.length === 0) {
                    return true;
                }
                monitor.updateProgress(' # - Meta package ' + metaPkg.name + ':' + metaPkg.os + ' has mixed package type in install dependent package list');
                return false;
            });

            if (isValid) {
                cb(null, targetPkgs);
            } else {
                var error = 'non-meta and meta packages cannot be put together in install-dependent.';
                cb(new Error(error));
            }
        },
        function (targetPkgs, cb) {
            // targetPkg from pkg.installDepList
            var targetPkgPathList = _.map(targetPkgs, function (pkg) {
                var pkgInfo = snapshot.osPackages[targetOS][pkg.name];
                if (utils.isURL(distPath)) {
                    return distPath + '/' + pkgInfo.path;
                } else {
                    return path.join(distPath, pkgInfo.path);
                }
            });
            cb(null, targetPkgs, targetPkgPathList);
        },
        function (targetPkgs, targetPkgPathList, cb) {
            if (!_.isEmpty(targetPkgPathList) && utils.isURL(targetPkgPathList[0])) {
                // TODO
                utils.genTemp(function (err1, tempDir) {
                    var pkgInfos = _.map(targetPkgs, function (pkg) {
                        return snapshot.osPackages[targetOS][pkg.name];
                    });
                    snapshot.downloadPackages(distPath, pkgInfos, tempDir, monitor, function (err1, results) {
                        cb(err1, results);
                    });
                });
            } else {
                cb(null, targetPkgPathList);
            }
        },
        function (targetPkgPathList, cb) {
            monitor.updateProgress(' # - Install META packages: ' + targetPkgPathList.map(function (e) {
                return path.basename(e).split('_')[0] + '(' + targetOS + ')';
            }).join(','));

            installer.installLocalPackages(targetPkgPathList, workPath, hostOS, snapshot, distPath, { skipMetaDep: true }, monitor, cb);
        },
        function (cb) {
            var imgInfo = 'Repository: ' + baseRepoUrl + os.EOL +
                'Distribution: ' + distName + os.EOL +
                'Installed-Snasphot: ' + snapshot.name + os.EOL;
            if (options && options.uid) {
                imgInfo += 'Distribution-ID: ' + options.uid;
            }
            monitor.updateProgress(' # - Create repository.info file');
            fs.writeFile(path.join(workPath, '.info', 'repository.info'), imgInfo, function (err) {
                if (err) {
                    monitor.updateProgress({ log: err, logType: 'error' });
                }
                cb(err);
            });
        },
        function (cb) {
            var sdkImagePath = path.join(workPath, INSTALLER_FILE_NAME);

            monitor.updateProgress(' # - Compress image file into \'' + sdkImagePath + '\'');
            Zip.compress(sdkImagePath, workPath, {}, cb);
        },
        function (cb) {
            Zip.getZipFileInformation(path.join(workPath, INSTALLER_FILE_NAME), function (err, info) {
                if (err) {
                    cb(err);
                } else {
                    var zipInfo =
                        'TOTAL_UNZIP_SIZE=' + info.uncompressedSize + os.EOL +
                        'TOTAL_UNZIP_FILE_COUNT=' + info.numberOfFiles + os.EOL;

                    fs.writeFile(path.join(workPath, 'zipfile.info'), zipInfo, cb);
                }
            });
        },
        function (cb) {
            // TODO: Is it required to get detailed information for installer?
            // generateInstallerInfo();
            cb(null);
        },
        function (cb) {
            monitor.updateProgress(' # - Create an installer for ' + targetOS);
            createInstaller(distPath, snapshot, targetOS, hostOS, workPath, installerName, { installerTitle: installerTitle }, monitor, cb);
        },
        function (cb) {
            // Clean tempWorkspace
            monitor.updateProgress(' # - Remove workspace \'' + workPath + '\'');
            utils.removePathIfExist(workPath, cb);
        }
    ],
    function (err) {
        callback(err);
    });
}


function createInstaller(distPath, snapshot, targetOS, hostOS, workPath, installerName, opts, monitor, callback) {
    // check workspace directory
    var workspace = process.cwd();
    if (opts.workspace) {
        workspace = opts.workspace;
    }

    var INST_GEN_SCRIPT = 'buildInstaller';

    // get host information
    var platform = os.platform();

    // get installDir for installer-base
    var installerBaseDir = path.join(workspace, 'installer-base');
    var installerBinPath = path.join(installerBaseDir, 'installer', (opts.isCLI ? 'cli' : 'ui'));

    var scriptPath = null;
    if (platform === 'win32') {
        scriptPath = path.join(installerBinPath, INST_GEN_SCRIPT + '.BAT');
    } else {
        // linux, macos
        scriptPath = path.join(installerBinPath, INST_GEN_SCRIPT);
    }

    async.waterfall([
        function (cb) {
            if (!snapshot || !snapshot.osPackages || !snapshot.osPackages[targetOS]) {
                cb(new Error('snapshot or snapshot.osPackages does not exist'));
            } else {
                var installerPkgInfo = snapshot.osPackages[targetOS]['installer'];
                if (!installerPkgInfo) {
                    cb(new Error('\'installer\' for ' + targetOS + ' does not exist in snapshot'));
                } else {
                    var installPkgPath = null;
                    if (utils.isURL(distPath)) {
                        // TODO: need to improve
                        utils.genTemp(function (err, tempDir) {
                            snapshot.downloadPackages(distPath, [installerPkgInfo], tempDir, monitor, function (err1, results) {
                                cb(err, results);
                            });
                        });
                    } else {
                        installPkgPath = path.join(distPath, installerPkgInfo.path);
                        cb(null, [installPkgPath]);
                    }
                }
            }
        },
        function (installPkgPath, cb) {
            monitor.updateProgress(' # - Install \'installer\' package...');
            installer.installLocalPackages(installPkgPath, installerBaseDir, hostOS, snapshot, distPath, {}, monitor, cb);
        },
        function (cb) {
            monitor.updateProgress(' # - move image file...');
            extfs.copy(path.join(workPath, INSTALLER_FILE_NAME),
                path.join(installerBinPath, INSTALLER_FILE_NAME), cb);
        },
        function (cb) {
            monitor.updateProgress(' # - move image file info...');
            extfs.copy(path.join(workPath, 'zipfile.info'),
                path.join(installerBinPath, 'zipfile.info'), cb);
        },
        function (cb) {
            monitor.updateProgress(' # - Check if installer generation script exists or not. ' + scriptPath);
            fs.access(scriptPath, fs.F_OK, cb);
        },
        function (cb) {
            monitor.updateProgress(' # - Add execution permission to script files');
            fs.chmod(scriptPath, '777', cb);
        },
        function (cb) {
            monitor.updateProgress(' # - Execute installer generation script.');

            var env = _.clone(process.env);
            if (opts.releaseName) {
                env.RELEASE_NAME = opts.releaseName;
            }

            if (opts.installerTitle) {
                env.INSTALLER_TITLE = opts.installerTitle;
            }

            Process.create(scriptPath,
                [],
                {
                    cwd: installerBinPath,
                    env: env
                },
                {
                    onStdout: function (line) {
                        monitor.updateProgress('   ' + line);
                    },
                    onStderr: function (line) {
                        monitor.updateProgress('   ' + line);
                    },
                    onExit: function (code) {
                        if (code !== 0) {
                            var error = new Error('Executing process exited with code ' + code);
                            cb(error);
                        } else {
                            cb(null);
                        }
                    }
                });
        },
        function (cb) {
            monitor.updateProgress(' # - Moving result files to workspace directory... ');

            var fileExtension = '';
            if (platform === 'win32') {
                fileExtension = '.exe';
            } else if (platform === 'darwin') {
                fileExtension = '.dmg';
            } else {
                fileExtension = '.bin';
            }

            var imageFileName = 'installer' + fileExtension;
            var srcFilePath = path.join(installerBinPath, imageFileName);
            fs.access(srcFilePath, fs.F_OK, function (err1) {
                if (err1) {
                    monitor.updateProgress({ log: srcFilePath + ' does not exist', logType: 'error' });
                    cb(err1);
                } else {
                    var targetFilePath = path.join(workspace, installerName + '_' + targetOS + fileExtension);
                    if (opts.isCLI) {
                        targetFilePath = path.join(workspace, 'CLI-' + installerName + '_' + targetOS + fileExtension);
                    }
                    extfs.copy(srcFilePath, targetFilePath, cb);
                }
            });
        }
    ],
    function (err) {
        callback(err);
    });
}
