/**
 * pkg-build.js
 * Copyright (c) 2000 - 2015 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
**/

var optimist = require('optimist');
var os = require('os');
var fs = require('fs');
var url = require('url');
var Path = require('path');
var Async = require('async');

var Builder = require('../org.tizen.projects/builder.js');
var tizenUtil = require('../org.tizen.common/tizen_utils');
var Package = require('../org.tizen.common/package.js');
var utils = require('../../lib/utils');
var Tutils = require('../org.tizen.common/tizen_utils.js');
var RemoteRepo = require('../org.tizen.repository/remote-repo.js');
var Installer = require('../org.tizen.projects/installer.js');
var Monitor = require('../../lib/monitor.js');

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 ); }
};

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

// get host information
var host = tizenUtil.getTargetOS( os.platform(), os.arch() );

var message =
    'Usage: $0 -u <repository server url> [-o <os>] [-d <path>] [-t <package name>|<path>] [-c] [-h]\n\n' +
    'Basic Examples:\n' +
    '  Build                      $0 -u http://download.tizen.org/sdk/latest/tizen\n' +
    '  Build with local packages  $0 -u http://download.tizen.org/sdk/latest/tizen -d /local/pkg/path\n' +
    '                             (The version of package must be higher than repository\'s version)\n' +
    '  Build with toolchain       $0 -u http://download.tizen.org/sdk/latest/tizen -t mingw32-msys-1.0-dev\n' +
    '  Build with local toolchain $0 -u http://download.tizen.org/sdk/latest/tizen -t /local/path/toolchain_0.1_ubuntu32.zip\n' +
    '                             (The version of toolchain must be higher than repository\'s version)';

// option parse & set
var argv = optimist.usage(message)
    .alias({'u':'url','o':'os','d':'dependent-pkg-path','t':'toolchain-pkg-path','c':'clean','h':'help'})
    .string(['u','o'])
    .boolean(['c','h'])
    .describe('u','remote repository server url: http://download.tizen.org/sdk/latest/tizen')
    .describe('o','os information')
    .describe('d','local dependent package path')
    .describe('t','toolchain path. find the package in remote repository or local path')
    .describe('c','clean build option')
    .describe('h','display help')
    .default('o', host.os.toString())
    .default('t', Tutils.getOSCategory(host.os) === 'windows' ? 'mingw32-msys-1.0-dev':undefined)
    .demand(['u']).argv;

// get arguemnts
var serverURL = argv.u;
var osInformation = argv.o;
var localDependentPkgPath = argv.d;
var localToolchainPkgPath = argv.t;
var cleanOption = argv.c ? true : false;

// init variables
var repo = null;
var buildDepPkgs = null;
var distName;
var buildRoot = Path.join(utils.getHomePath(), "temp", "default");
var toolchainPath;
var remoteSnapshot = null;
var toolchainPkg = {};

Async.waterfall([
    function(callback) {
        if (validateUrlFormat(serverURL) === false ) {
            optimist.showHelp();
            callback(new Error('Invalid URL Format!\n ex) http://download.tizen.org/sdk/latest/tizen'));
        } else {
            distName = Path.basename(serverURL);
            callback(null);
        }
    }, function(callback) {
        if (validateOsInfo(osInformation) === false ) {
            optimist.showHelp();
            callback(new Error('Invalid OS Format!\n ex) ubuntu-32'));
        } else {
            callback(null);
        }
    }, function(callback) {
        if (localDependentPkgPath && !fs.existsSync(localDependentPkgPath)) {
            optimist.showHelp();
            callback(new Error('Can not find ' + localDependentPkgPath));
        } else {
            callback(null);
        }
    }, function(callback) {
        if (localToolchainPkgPath) {
            console.log('Get toolchain information');
            if ('.' === Path.dirname(localToolchainPkgPath)){
                toolchainPkg.name = localToolchainPkgPath;
                toolchainPkg.path = null;
                customlog.info(' - toolchain name: ' + toolchainPkg.name);
                customlog.info(' - toolchain path: ' + serverURL + '/binary');
                callback(null);
            } else {
                fs.exists(localToolchainPkgPath, function(exists) {
                    if (exists) {
                        fs.lstat(localToolchainPkgPath, function(err, stats) {
                            if (!stats.isDirectory()) {
                                toolchainPkg.name = Path.basename(localToolchainPkgPath).split('_')[0];
                                toolchainPkg.path = Path.dirname(localToolchainPkgPath);
                                customlog.info(' - toolchain name: '+ toolchainPkg.name);
                                customlog.info(' - toolchain path: '+ toolchainPkg.path);
                                callback(null);
                            } else {
                                if (Tutils.getOSCategory(host.os) === 'windows') {
                                    toolchainPkg.name = 'mingw32-msys-1.0-dev';
                                    toolchainPkg.path = localToolchainPkgPath;
                                    customlog.info(' - toolchain name: '+ toolchainPkg.name);
                                    customlog.info(' - toolchain path: '+ toolchainPkg.path);
                                    callback(null);
                                } else {
                                    optimist.showHelp();
                                    callback(new Error('Please input toolchain package name'));
                                }
                            }
                        });
                    } else {
                        optimist.showHelp();
                        callback(new Error('Can not find ' + localToolchainPkgPath));
                    }
                });
            }
        } else {
            callback(null);
        }
    }, function(callback) {
        console.log('Get build dependencies');
        getBuildDependencies( process.cwd(), host.os, osInformation, callback);
    }, function(bDepPkgs, callback){
        console.log('Get repository');
        buildDepPkgs = bDepPkgs;
        repo = RemoteRepo.createRemoteRepo(Path.dirname(serverURL),
            {distName: distName, snapshotName:null});
        repo.open(null,callback);
    }, function(callback) {
        repo.searchSnapshots( {name:null, repoType:"tizen", distName:distName},
            function(err,snapshots) {
                if ( !err && snapshots.length > 0 ) {
                    remoteSnapshot = snapshots[0];
                }
                callback( err );
            });
    }, function(callback) {
        if (toolchainPkg.name) {
            toolchainPath = Path.join(buildRoot, 'toolchain', toolchainPkg.name);
            var toolchainPkgObjList;
            if (!toolchainPkg.path) {
                console.log('Install remote toolchain');
                toolchainPkgObjList = [{name: toolchainPkg.name, os: host.os}];

                Installer.installMultiplePackages(toolchainPkgObjList, toolchainPath, repo, distName, host,
                        { clean: cleanOption, localPkgs:[] }, monitor, callback );
            } else {
                console.log('Install local toolchain');
                toolchainPkgObjList = [{name: toolchainPkg.name, os: host.os}];
                var localPkgInfos = [];

                fs.readdir(toolchainPkg.path, function(err, files){
                    if (err) { return callback(err); }
                    Async.eachSeries(files, function(file, cb){
                        localPkgInfos.push(Path.join(toolchainPkg.path, file));
                        cb(null);
                    }, function(err){
                        if (err) { return callback(err); }
                        Installer.installMultiplePackages(toolchainPkgObjList, toolchainPath, repo, distName, host,
                                { clean: cleanOption, localPkgs:localPkgInfos }, monitor, callback );
                    });
                });
            }
        } else {
            callback(null);
        }
    }, function(callback){
        console.log('Install build dependent packages');
        if (localDependentPkgPath) {
            localDependentPkgPath = Path.resolve(localDependentPkgPath);
            fs.readdir( localDependentPkgPath, function(err, files){
                var localPkgInfos = [];
                if (err) {callback(err);}
                Async.eachSeries(files, function( file, cb ){
                    localPkgInfos.push( Path.join( localDependentPkgPath, file) );
                    cb( null );
                }, function(err){
                    if (err) { callback(err); }
                    Installer.installMultiplePackages(buildDepPkgs, buildRoot, repo, distName, host, { clean: cleanOption, localPkgs:localPkgInfos }, monitor, callback );

                });
            });
        } else {
            Installer.installMultiplePackages(buildDepPkgs, buildRoot, repo, distName, host,
                { clean: cleanOption, localPkgs:[] }, monitor, callback );
        }
    }, function(callback){
        // download archive packages
        getArchiveDependencies( process.cwd(), callback);
    }, function(sDepPkgs, callback){
        // download archive packages of dependent jobs
        sourceDepPkgs = sDepPkgs;
        downloadDependentArchivePackages( sourceDepPkgs, repo, buildRoot, callback );
    }, function(callback){
        if (toolchainPkg.name && toolchainPath) {
            console.log('Build with toolchain');
            // get toolchain information
            var toolchainInfoPath = Path.join(toolchainPath, 'dibs_toolchain.json');
            if (fs.existsSync(toolchainInfoPath) && fs.lstatSync(toolchainInfoPath).isFile()) {
                var toolchainInfo = createJson(toolchainInfoPath);
                if (toolchainInfo) {
                    // set toolchain information
                    var info = {
                        PACKAGE: toolchainPkg.name,
                        BIN_PATH: toolchainInfo.BIN_PATH || null,
                        CMD_PATH: toolchainInfo.CMD_PATH,
                        TOOLCHAIN_PATH: toolchainPath
                    };
                    Builder.build(process.cwd(), buildRoot, host, {
                        TARGET_OS: osInformation,
                        TOOLCHAIN: info }, monitor,callback);
                } else {
                    return callback(new Error('Cannot read ' + toolchainInfoPath + ' file.'));
                }
            } else {
                return callback(new Error('Cannot found ' + toolchainInfoPath + ' file.'));
            }
        } else {
            console.log('Build without toolchain');
            Builder.build(process.cwd(), buildRoot, host, {
                TARGET_OS: osInformation }, monitor,callback);
        }
    }
    ], function(err) {
        if (err) {
            customlog.error( err.message );
            console.log( 'build fail.');
            process.exit(-1);
        } else {
            console.log( 'build success.');
        }
    });


function validateUrlFormat ( input ) {
    var result = url.parse( input );
    return ( ( result.protocol == "http:" ) || ( result.protocol == "https:" ) || (result.protocol == "ftp:") );
}

function validateOsInfo ( input ) {
    var osFlag = false;
    tizenUtil.getOsList("Tizen-Source").forEach(function (osInfo) {
       if (osInfo == input) osFlag = true;
    });
    if (osFlag) return true;
    else return false;
}

function getBuildDependencies( srcDir, hostOS, targetOS , callback ) {
    var pkgFile = Path.join(srcDir, "package", "pkginfo.manifest");
    Package.getPkgListFromFile(pkgFile, function(err, pkgs) {
        var result = [];
        if (!err) {
            for( var i = 0; i < pkgs.length; i++ ) {
                var pkg = pkgs[i];
                if ( pkg.os !== targetOS ) { continue; }

                if ( pkg.buildDepList.length > 0 ) {
                    for( var j = 0; j < pkg.buildDepList.length; j++ ) {
                        var dep = pkg.buildDepList[j];
                        var depOS = (dep.os === undefined) ? targetOS : dep.os;
                        var filtered = result.filter( function(depPkg) {
                                return depPkg.name === dep.packageName && depPkg.os === targetOS;
                            });
                        if ( filtered.length === 0 ) {
                            result.push({name:dep.packageName, os:depOS});
                        }
                    }
                }
            }
        }
        callback( err, result );
    });
}


function getArchiveDependencies( srcDir,  callback ) {
    var pkgFile = Path.join(srcDir, "package", "pkginfo.manifest");
    Package.getPkgListFromFile(pkgFile, function(err, pkgs) {
        var result = [];
        if ( !err ) {
            result = Package.getSourceDepsOfPackages( pkgs );
        }
        callback( err, result );
    });
}


function downloadDependentArchivePackages( archivePkgs, repo, downloadDir, callback ){
    Async.each(archivePkgs, function(archive,ecb){
        // download archive package
        repo.downloadPackage(archive.name,{
            repoType:"tizen",
            distName: distName,
            snapshotName: remoteSnapshot.name,
            targetDir:downloadDir
        },function(err){ecb(err);});
    }, function(err){callback(err);});
}

function createJson(jsonPath) {
    var j = require(jsonPath);
    // verify format
    if (j.CMD_PATH === undefined || typeof j.CMD_PATH !== 'string') {
        return null;
    }
    return j;
}
