/**
 * pkg-cli.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 fs = require('fs');
var optimist = require('optimist');
var os = require('os');
var path = require('path');
var url = require('url');
var _ = require('underscore');

var dibs = require('../../core/dibs.js');
var Monitor = require('../../lib/monitor.js');
var utils = require('../../lib/utils.js');
var ImageGenerator = require('../org.tizen.common/image-generator.js');
var Package = require('../org.tizen.common/package.js');
var TizenUtil = require('../org.tizen.common/tizen_utils.js');
var Installer = require('../org.tizen.projects/installer.js');
var Distribution = require('../org.tizen.repository/distribution.js');
var Snapshot = require('../org.tizen.repository/snapshot.js');
var RemoteRepo = require('../org.tizen.repository/remote-repo.js');
var IGen = require('../org.tizen.repository.advanced/installer-generator.js');

dibs.initialize();

function stylize(str, style) {
    var styles = {
        grey: [90, 39], red: [31, 39], green: [32, 39], yellow: [33, 39]
    };
    return '\033[' + styles[style][0] + 'm' + str + '\033[' + styles[style][1] + 'm';
}

['grey', 'yellow', 'red', 'green'].forEach(function (style) {
    String.prototype.__defineGetter__(style, function () {
        return stylize(this, style);
    });
});

// custom setting
var customlog = {
    info: function (str) { console.log(str.toString('utf-8').green); },
    error: function (str) { console.log(str.toString('utf-8').red); },
    warn: function (str) { console.log(str.toString('utf-8').yellow); }
};

// get host information
var host = TizenUtil.getTargetOS(os.platform(), os.arch());
var usageMessage =
    'This tool is command line interface for control packages\n\n' +
    'Usage: $0 <SUBCOMMAND> [OPTS]\n\n' +
    'Subcommands:\n' +
    '  install            Install a package from a repository or a local package\n' +
    '  uninstall          Uninstall a package from install directory\n' +
    '  show-pkg           show package information from install directory\n' +
    '  list-pkg           list packages from install directory\n' +
    '  dep-graph          Show build|install dependency packages\n' +
    '  install-dep-list   Show all install dependent packages\n' +
    '  full-build-step    Show build step about full-build\n' +
    '  make-img           Make SDK-Image.\n' +
    '  validate-meta      validate meta list.\n\n' +
    'Subcommand usage:\n' +
    '  install             pkg-cli install -P <package name> [-o <os>] -u <package server url> -l <install directory> [-s <snapshot>]\n' +
    '  uninstall           pkg-cli uninstall -P <package name> -l <install directory>\n' +
    '  show-pkg            pkg-cli show-pkg -P <package name> -l <install directory>\n' +
    '  list-pkg            pkg-cli list-pkg -l <install directory>\n' +
    '  dep-graph           pkg-cli dep-graph -u <package server url> [-o <os>] [-d <dependency type>]\n' +
    '  install-dep-list    pkg-cli install-dep-list -u <package server url> -o <os> -m <include meta list>\n' +
    '  full-build-step     pkg-cli full-build-step -u <package server url> [-o <os>]\n' +
    '  make-img            pkg-cli make-img -u <package server url> -s <snapshot> [-o <os>] [-i <image info>] [-x <exclude meta list>] [-q <distribution unique identifier>]\n' +
    '  validate-meta       pkg-cli validate-mata -u <package server url> [-s <snapshot>] [-o <os>] [-m <include meta list>] [-x <exclude meta list>]\n';

var argv = optimist.usage(usageMessage)
    .alias({u: 'url', s: 'snapshot', o: 'os', i: 'info', x: 'exclude', h: 'help', l: 'location'})
    .describe('u', 'remote package server url: http://127.0.0.1/dibs/unstable')
    .describe('s', 'snapshot name: 0425142832828356')
    .describe('o', 'os information: ubuntu-32/ubuntu-64/windows-32/windows-64/macos-64/all')
    .describe('i', 'sdk image information : http://download.tizen.org/sdk/packages/official')
    .describe('x', 'exclude meta-package list : MOBILE-3.0,IVI-1.0')
    .describe('q', 'distribution unique identifier : aa02179c-7c64-11e5-8bcf-feff819cdc9f')
    .describe('d', 'dependency type ex)install / build / all')
    .describe('h', 'display help')
    .describe('P', 'package name')
    .describe('l', 'SDK install location')
    .describe('m', 'include meta list')
    .default('d', 'all')
    .string(['s', 'o', 'x', 'P', 'l', 'q'])
    .argv;

var subCmds = argv._;

if (argv.h) {
    optimist.showHelp();
    process.exit(0);
}

// init variables
var serverURL = argv.u;
var osInformation = argv.o;
var snapshotInfo = argv.s;
var imageInformation = argv.i;
var packageName = argv.P;
var installLocation = argv.l;
var curDir = process.cwd();
var includeInformation = [];
var excludeInformation = [];
var distributionUID = argv.q;

if (argv.m) { includeInformation = argv.m.split(',');}
if (argv.x) { excludeInformation = argv.x.split(',');}

var distributionName;
var osList = [];

// validate sub command
if (!subCmds || subCmds.length === 0) {
    console.error('Sub-Command must be specified!');
    process.exit(-1);
}

switch (subCmds[0]) {
case 'install':
    handle_install_command(packageName, serverURL, installLocation, snapshotInfo);
    break;
case 'uninstall':
    handle_uninstall_command(packageName, installLocation);
    break;
case 'show-pkg':
    handle_show_pkg_command(packageName, installLocation);
    break;
case 'list-pkg':
    handle_list_pkg_command(installLocation);
    break;
case 'make-img':
    handle_make_img_command();
    break;
case 'dep-graph':
    handle_dep_graph_command();
    break;
case 'install-dep-list':
    handle_install_dep_command();
    break;
case 'full-build-step':
    handle_full_build_step_command();
    break;
case 'validate-meta':
    handle_validate_meta_command();
    break;
default:
    console.error('Invalid sub-command!: \'' + subCmds + '\'');
    process.exit(-1);
}

function handle_install_command(pkgName, serverURL, installLocation, snapshotName) {
    // check arguments
    if (!pkgName || pkgName.length === 0) {
        console.error('PackageName MUST be specified!');
        process.exit(-1);
    }
    if (!serverURL || serverURL.length === 0) {
        console.error('Repository URL MUST be specified!');
        process.exit(-1);
    }
    if (!installLocation || installLocation.length === 0) {
        console.error('Install location MUST be specified!');
        process.exit(-1);
    }
    if (!snapshotName || snapshotName.length === 0) {
        console.log('Use the latest snapshot');
        snapshotName = null;
    }

    var options = {};
    var repoInfo = getRepositoryInfo(serverURL);
    repoInfo.snapshotName = snapshotName;

    var monitor = new Monitor({
        onProgress: function (info, cb) {
            if (info.logType) {
                customlog[info.logType](info.log);
            }
            cb(null);
        }
    });

    installLocation = path.resolve(installLocation);

    async.waterfall([
        // check if pkgName is a file or not
        function (cb) {
            fs.readFile(pkgName, function (err) {
                if (!err) {
                    Package.getPkgInfoFromPkgFile(pkgName, function (err, pkgInfo) {
                        options.localPkgs = [pkgName];
                        if (err) {
                            console.error('Invalid Package File! Please use corret file');
                            process.exit(-1);
                        } else {
                            pkgName = pkgInfo.name;
                        }
                    });
                }
                cb(null);
            });
        },
        // validate & init osInformation
        function (cb) {
            if (osInformation) {
                validateOsInfo(osInformation, function (err, rst) {
                    if (!rst) {
                        optimist.showHelp();
                        cb(new Error('Invalid OS Format!\n ex)ubuntu-32'));
                    } else {
                        cb(null);
                    }
                });
            } else {
                cb(null);
            }
        },
        // create remote repo
        function (cb) {
            var repo = RemoteRepo.createRemoteRepo(repoInfo.baseURL,
                {distName: repoInfo.distName, snapshotName: repoInfo.snapshotName});
            repo.open(null, function (err) {
                cb(err, repo);
            });
        },
        // validate snapshot
        function (repo, cb) {
            if (repoInfo.snapshotName) {
                repo.searchSnapshots({name: repoInfo.snapshotName, repoType: 'tizen', distName: repoInfo.distName}, function (err, snaps) {
                    if (err) {
                        console.error(err);
                    } else if (snaps.length === 0) {
                        err = new Error('snapshot ' + repoInfo.snapshotName + ' is invalid!!');
                    }
                    cb(err, repo);
                });
            } else {
                cb(null, repo);
            }
        },

        // install package
        function (repo, cb) {
            var targetOS = osInformation ? osInformation : TizenUtil.getTargetOS(os.platform(), os.arch()).os;
            Installer.installSinglePackage(pkgName, targetOS, installLocation,
                repo, repoInfo.distName, host, options, monitor, cb);
        }
    ],
    function (err) {
        if (err) {
            console.error('Package installation failure!');
            console.error(err);
            process.exit(-1);
        } else {
            console.log('Package installation success!');
            process.exit(0);
        }
    });
}

function handle_uninstall_command(pkgName, installLocation) {
    // check arguments
    if (!pkgName || pkgName.length === 0) {
        console.error('Package Name MUST be specified!');
        process.exit(-1);
    }

    if (!installLocation || installLocation.length === 0) {
        console.error('Install location MUST be specified!');
        process.exit(-1);
    }

    var monitor = new Monitor({
        onProgress: function (info, cb) {
            if (info.logType) {
                customlog[info.logType](info.log);
            }
            cb(null);
        }
    });

    installLocation = path.resolve(installLocation);

    Installer.uninstallSinglePackage(pkgName, installLocation, monitor, function (err) {
        if (err) {
            console.error('Package uninstall failed!');
            console.error(err);
            process.exit(-1);
        } else {
            console.log('Package uninstall succeeded!');
            process.exit(0);
        }
    });
}

function handle_show_pkg_command(pkgName, installLocation) {
    // check arguments
    if (!pkgName || pkgName.length === 0) {
        console.error('Package Name MUST be specified!');
        process.exit(-1);
    }

    if (!installLocation || installLocation.length === 0) {
        console.error('Install location MUST be specified!');
        process.exit(-1);
    }

    var monitor = new Monitor({
        onProgress: function (info, cb) {
            if (info.logType) {
                customlog[info.logType](info.log);
            }
            cb(null);
        }
    });

    installLocation = path.resolve(installLocation);

    Installer.showPackage(pkgName, installLocation, monitor, function (err, packageInfo) {
        if (err) {
            console.error('Failed to find \'' + pkgName + '\' from ' + installLocation);
            console.error(err);
            process.exit(-1);
        } else {
            console.info(packageInfo);
            process.exit(0);
        }
    });


}

function handle_list_pkg_command(installLocation) {
    // check arguments
    if (!installLocation || installLocation.length === 0) {
        console.error('Install location MUST be specified!');
        process.exit(-1);
    }

    var monitor = new Monitor({
        onProgress: function (info, cb) {
            if (info.logType) {
                customlog[info.logType](info.log);
            }
            cb(null);
        }
    });

    installLocation = path.resolve(installLocation);

    Installer.listPackages(installLocation, monitor, function (err, packageInfoList) {
        if (err) {
            console.error('Failed to find any package from ' + installLocation);
            console.error(err);
            process.exit(-1);
        } else {
            console.info(packageInfoList);
            process.exit(0);
        }
    });
}

function handle_validate_meta_command() {
    // check arguments
    if (includeInformation.length === 0) {
        console.error('include Meta list MUST be specified!');
        process.exit(-1);
    }
    if (!serverURL || serverURL.length === 0) {
        console.error('Repository URL MUST be specified!');
        process.exit(-1);
    }

    // var options = {};
    var repoInfo = getRepositoryInfo(serverURL);
    var monitor = new Monitor({
        onProgress: function (info, cb) {
            cb(null);
        }
    });

    var snapshotName = (snapshotInfo) ? snapshotInfo : repoInfo.snapshotName;
    async.waterfall([
        // validate & init osInformation
        function (cb) {
            if (osInformation) {
                validateOsInfo(osInformation, function (err, rst) {
                    if (!rst) {
                        optimist.showHelp();
                        cb(new Error('Invalid OS Format!\n ex)ubuntu-32'));
                    } else if (osInformation === 'all') {
                        optimist.showHelp();
                        cb(new Error('Invalid OS Format!\n ex)ubuntu-32'));
                    } else {
                        cb(null);
                    }
                });
            } else {
                osInformation = host.os;
                cb(null);
            }
        },
        // create remote repo
        function (cb) {
            var repo = RemoteRepo.createRemoteRepo(repoInfo.baseURL,
                {distName: repoInfo.distName, snapshotName: snapshotName});
            repo.open(null, function (err) {
                cb(err, repo);
            });
        },
        function (repo, cb) {
            repo.searchSnapshots({
                name: snapshotName,
                repoType: 'tizen',
                distName: repoInfo.distName
            }, cb);
        },
        // get meta package list
        function (snapshots, cb) {
            var metaList = IGen.getMetaPackagesToInstall(osInformation, snapshots[0], includeInformation, excludeInformation, monitor);
            _.each(metaList, function (m) {console.log(m.name);});
            cb(null);
        }
    ],
    function (err) {
        if (err) {
            console.error(err);
            process.exit(-1);
        } else {
            console.log('extract valid meta list succeeded!');
            process.exit(0);
        }
    });
}

function getRepositoryInfo(serverURL) {
    var result = {};

    var sIndex = serverURL.indexOf('/snapshots/');
    if (sIndex !== -1) {
        result.baseURL = path.dirname(serverURL.substring(0, sIndex));
        result.distName = path.basename(serverURL.substring(0, sIndex));
        result.snapshotName = path.basename(serverURL);
    } else {
        result.baseURL = path.dirname(serverURL);
        result.distName = path.basename(serverURL);
        result.snapshotName = null;
    }

    return result;
}

function handle_make_img_command() {
    console.log('Start creating SDK images');
    async.waterfall([
        function (cb) {
            // validate url format
            console.log('# - Validate server URL... ' + serverURL);
            validateUrlFormat(serverURL, function (err, rst) {
                if (!rst) {
                    optimist.showHelp();
                    cb(new Error('Invalid URL Format!\n ex)http://download.tizen.org/sdk/latest/tizen'));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            // validate & init osInformation
            console.log('# - Validate OS information... ');
            validateOsInfo(osInformation, function (err, rst) {
                if (!rst) {
                    optimist.showHelp();
                    cb(new Error('Invalid OS Format!\n ex)ubuntu-32'));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            // resolve server url
            resolveURL(serverURL, cb);
        },
        function (cb) {
            async.eachSeries(osList, function (os, ecb) {
                console.log('# - Generate SDK Image: ' + ('TIZEN-SDK-IMG_' + snapshotInfo + '_' + os + '.zip') + ' in ' + curDir);
                ImageGenerator.makeImage({os: os,
                                        workspace: curDir,
                                        imgName: 'TIZEN-SDK-IMG_' + snapshotInfo + '_' + os + '.zip',
                                        imgInfo: imageInformation,
                                        pServerURL: serverURL,
                                        distributionName: distributionName,
                                        snapshotName: snapshotInfo,
                                        excludeList: excludeInformation,
                                        uid: distributionUID
                                        }, ecb);
            },
            function (err) {
                cb(err);
            });
        }
    ],
    function (err) {
        if (err) {
            console.error('Failed to create SDK images');
            customlog.error(err);
        } else {
            console.log('Finish creating SDK images successfully');
        }
    });
}


function handle_dep_graph_command() {
    async.waterfall([
        function (cb) {
            // validate & init osInformation
            validateOsInfo(osInformation, function (err, rst) {
                if (!rst) {
                    optimist.showHelp();
                    cb(new Error('Invalid OS Format!\n ex)ubuntu-32'));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            async.each(osList, function (os, ecb) {
                genDepGraph(argv.u, os, argv.d, ecb);
            }, cb);
        }
    ],
    function (err) {
        if (err) {
            customlog.error(err);
        }
    });
}

function handle_install_dep_command() {
    if (!serverURL || serverURL.length === 0) {
        optimist.showHelp();
        console.error('Repository URL MUST be specified!');
        process.exit(-1);
    }

    if (!includeInformation || includeInformation.length === 0) {
        optimist.showHelp();
        console.error('Meta package list MUST be specified!');
        process.exit(-1);
    }

    if (!osInformation) {
        optimist.showHelp();
        console.error('OS information MUST be specified!');
        process.exit(-1);
    }

    var repoInfo = getRepositoryInfo(serverURL);
    repoInfo.snapshotName = null;

    async.waterfall([
        function (cb) {
            var repo = RemoteRepo.createRemoteRepo(repoInfo.baseURL, {
                distName: repoInfo.distName,
                snapshotName: repoInfo.snapshotName});
            repo.open(null, function (err) {
                cb(err, repo);
            });
        }, function (repo, cb) {
            repo.searchSnapshots({name: repoInfo.snapshotName, repoType: 'tizen', distName: repoInfo.distName}, function (err, snaps) {
                if (err) {
                    cb(err);
                } else if (snaps.length === 0) {
                    cb(new Error('snapshot ' + repoInfo.snapshotName + ' is invalid!!'));
                } else {
                    getAllInstallDependentPackages(includeInformation, snaps[0], cb);
                }
            });
        }, function (results, cb) {
            if (results.length !== 0) {
                // sorting
                results.sort(function (a, b) {
                    if (a.name > b.name) {
                        return 1;
                    }
                    if (a.name < b.name) {
                        return -1;
                    }
                    return 0;
                });
                console.log('<<Package List>>');
                _.each(results, function (result) {
                    console.log(result.name + ' [' + result.os + ']');
                });
            }
            cb(null);
        }
    ], function (err) {
        if (err) {
            customlog.error(err);
        }
    });
}

function getAllInstallDependentPackages(inputList, inputSnapshot, callback) {
    var outputList = [];
    var inputPackageList = [];

    _.each(inputList, function (input) {
        _.each(inputSnapshot.osPackages[osInformation], function (item) {
            if (item.name === input) {
                inputPackageList.push(item);
            }
        });
    });

    async.eachSeries(inputPackageList, function (pkgObj, cb) {
        Snapshot.getInstallDependentPackages([pkgObj], inputSnapshot, {
            installOrder: false
        }, function (err, rst) {
            outputList = _.union(outputList, rst, [pkgObj]);
            cb(err);
        });
    }, function (err) {
        if (err) {
            callback(err);
        } else {
            callback(err, outputList);
        }
    });
}

function handle_full_build_step_command() {
    async.waterfall([
        function (cb) {
            // validate & init osInformation
            validateOsInfo(osInformation, function (err, rst) {
                if (!rst) {
                    optimist.showHelp();
                    return cb(new Error('Invalid OS Format!\n ex)ubuntu-32'));
                } else {
                    return cb(null);
                }
            });
        },
        function (cb) {
            async.each(osList, function (os, ecb) {
                getFullBuildStep(argv.u, os, ecb);
            }, cb);
        }
    ], function (err) {
        if (err) {
            customlog.error(err);
        }
    });
}

// private
function validateUrlFormat(input, callback) {
    var result = url.parse(input);
    callback(null, (result.protocol === 'http:') || (result.protocol === 'https:') || (result.protocol === 'ftp:'));
}


function validateOsInfo(input, callback) {
    if (input === 'all') {
        console.log('# - Use all os... ' + TizenUtil.getOsList('Tizen-Source'));

        TizenUtil.getOsList('Tizen-Source').forEach(function (osInfo) {
            osList.push(osInfo);
        });
        callback(null, true);
    } else if (input) {
        console.log('# - Use given os info... ' + input);

        var osFlag = false;
        TizenUtil.getOsList('Tizen-Source').forEach(function (osInfo) {
            if (osInfo === input) {
                osFlag = true;
            }
        });

        if (osFlag) {
            osList.push(input);
            callback(null, true);
        } else {
            callback(null, false);
        }
    } else {
        console.log('# - Use host-os info... ' + host.os);

        osList.push(host.os);
        callback(null, true);
    }
}

function resolveURL(input, callback) {
    if (input.slice(-1) === '/') {
        serverURL = input.substring(0, input.length - 1);
    } else {
        serverURL = input;
    }

    var urlInfo = url.parse(serverURL);
    var pathInfo = urlInfo.path.split('/');

    // get snapshot info if exists
    if (urlInfo.path.indexOf('snapshots') > 0) {
        snapshotInfo = pathInfo[ pathInfo.length - 1 ];
        distributionName = pathInfo[ pathInfo.length - 3 ];
        serverURL = url.resolve(serverURL, '../');
        callback(null);
    } else {
        distributionName = pathInfo[ pathInfo.length - 1 ];
        Distribution.loadRemote(url.resolve(serverURL, './'), {distName: distributionName, snapshotName: null}, function (err, rst) {
            if (err) {
                callback(err);
            } else {
                snapshotInfo = rst[0].latestSnapshot.name;
                callback(err);
            }
        });
    }
}

function indent(string) {
    var idRule = /[a-zA-z0-9_]/g;
    return 'G' + string.match(idRule).join('');
}

function genDepGraph(url, os, type, callback) {
    async.waterfall([
        function (wcb) {
            utils.getTextFromUrl(url + '/pkg_list_' + os, wcb);
        }, Package.getPkgListFromString,
        function (pkgList, wcb) {
            var orphanPkgs = '';
            var connectedPkgs = {};
            var string = 'digraph ' + argv.d + '_dependency {\nranksep=1.5;\n';
            _.each(pkgList, function (pkg) {
                if (type === 'install' || type === 'all') {
                    _.each(pkg.installDepList, function (dep) {
                        connectedPkgs[pkg.name] = true;
                        connectedPkgs[dep.packageName] = true;
                        var weight = '';
                        if (dep.os && dep.os !== os) {
                            weight = ',label=\'' + dep.os + '\',fontcolor=red,style=bold';
                        }
                        if (pkg.attr === 'root') {
                            string += indent(pkg.name) + ' -> ' + indent(dep.packageName) + ' [color=green' + weight + '];\n';
                        } else {
                            string += indent(pkg.name) + ' -> ' + indent(dep.packageName) + ' [color=red' + weight + '];\n';
                        }
                    });
                }

                if (type === 'build' || type === 'all') {
                    _.each(pkg.buildDepList, function (dep) {
                        connectedPkgs[pkg.name] = true;
                        connectedPkgs[dep.packageName] = true;
                        var weight = '';
                        if (dep.os && dep.os !== os) {
                            weight = ',label=\'' + dep.os + '\',fontcolor=blue,style=bold';
                        }
                        string += indent(pkg.name) + ' -> ' + indent(dep.packageName) + ' [color=blue' + weight + '];\n';
                        string += indent(dep.packageName) + ' [label=\'' + dep.packageName + '\'];\n';
                    });
                }
            });
            _.each(pkgList, function (pkg) {
                if (connectedPkgs[pkg.name]) {
                    if (pkg.attr === 'root') {
                        string += indent(pkg.name) + ' [label=\'' + pkg.name + '\',shape=box];\n';
                    } else {
                        string += indent(pkg.name) + ' [label=\'' + pkg.name + '\',shape=ellipse];\n';
                    }
                } else {
                    orphanPkgs += pkg.name + '\n';
                }
            });
            string += '}';
            console.log('dot -Tpng -o ' + type + '_dep_' + os + '.png ' + type + '_dep_' + os + '.dot');
            async.waterfall([
                function (wcb1) {
                    fs.writeFile(path.join(curDir, type + '_dep_' + os + '.dot'), string, wcb1);
                },
                function (wcb1) {
                    fs.writeFile(path.join(curDir, type + '_dep_Orphan_Packages' + os + '.txt'), orphanPkgs, wcb1);
                }
            ], wcb);
        }], callback);
}

function getFullBuildStep(url, os, callback) {
    var depList = {};
    var prjList = {};
    async.waterfall([
        function (cb) {
            console.log('* Getting Pacakge Information');
            async.eachSeries(TizenUtil.getOsList('Tizen-Source'), function (osInfo, cb1) {
                async.waterfall([
                    function (cb2) {
                        process.stdout.write(' - Target: ' + url + '/pkg_list_' + osInfo + ' ... ');
                        utils.getTextFromUrl(url + '/pkg_list_' + osInfo, cb2);
                    }, function (result, cb2) {
                        Package.getPkgListFromString(result, cb2);
                    }, function (pkgList, cb2) {
                        if (osInfo !== os) {
                            _.each(pkgList, function (pkg) {
                                if (!_.has(depList, pkg.name)) {
                                    depList[pkg.name] = [];

                                    var info = {};
                                    info.projectName = pkg.source || pkg.name;
                                    if (pkg.attr && pkg.attr === 'binary') {
                                        info.projectType = 'binary';
                                    } else if (pkg.attr && pkg.attr === 'root') {
                                        info.attr = 'meta-pacakge';
                                    } else {
                                        info.projectType = 'unknown';
                                    }
                                    prjList[pkg.name] = info;
                                }
                            });
                        } else {
                            _.each(pkgList, function (pkg) {
                                var item1 = [];
                                var item2 = [];
                                _.each(pkg.buildDepList, function (dep) {
                                    item1.push(dep.packageName);
                                });
                                _.each(pkg.installDepList, function (dep) {
                                    item2.push(dep.packageName);
                                });
                                depList[pkg.name] = _.union(item1, item2);

                                var info = {};
                                info.projectName = pkg.source || pkg.name;
                                if (pkg.attr && pkg.attr === 'binary') {
                                    info.projectType = 'binary';
                                } else if (pkg.attr && pkg.attr === 'root') {
                                    info.attr = 'meta-package';
                                } else {
                                    info.projectType = 'unknown';
                                }
                                prjList[pkg.name] = info;
                            });
                        }
                        console.log('done');
                        cb2(null);
                    }
                ], cb1);
            }, cb);
        }, function (cb) {
            var i = 1;
            var metaProject = [];

            // get meta-package projects
            _.each(depList, function (item, key) {
                if (prjList[key].attr === 'meta-package') {
                    metaProject.push(prjList[key].projectName);
                }
            });
            metaProject = _.uniq(metaProject);

            console.log('* Full-build Step');
            while (Object.keys(depList).length > 0) {
                var candidatePackage = [];
                // finding treminal package
                _.each(depList, function (item, key) {
                    if (item.length === 0) {
                        candidatePackage.push(key);
                        delete depList[key];
                    }
                });

                var output = [];
                _.each(candidatePackage.sort(), function (item) {
                    // exclude meta package
                    if (_.indexOf(metaProject, prjList[item].projectName) > -1) {
                        return;
                    }

                    if (prjList[item].projectType === 'binary') {
                        output.push('   ' + item + '  (binary)');
                    } else {
                        output.push('   ' + item + '  (source:' + prjList[item].projectName + ')');
                    }
                });

                if (output.length > 0) {
                    console.log(' - Step.' + i++ + 'package (binary | source: $GIT_SOURCE_NAME)');
                    console.log(output);
                }

                // remove package in dependent list
                _.each(candidatePackage, function (item) {
                    _.each(depList, function (item1, key) {
                        var idx = item1.indexOf(item);
                        if (idx > -1) {
                            depList[key].splice(idx, 1);
                        }
                    });
                });
            }
            console.log(' - Step.' + i);
            console.log('   meta project: ' + metaProject);
            cb(null);
        }
    ], callback);
}

