/**
 * zip.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
**/

/**
 * common zip module use child process
 */

var spawn = require('child_process').spawn;
var _ = require('underscore');
var os = require('os');
var fs = require('fs');
var extfs = require('fs-extra');
var path = require('path');
var async = require('async');
var AdmZip = require('adm-zip');
var Process = require('./process.js');


module.exports.compress = compress;
module.exports.uncompress = uncompress;
module.exports.unzipAFile = unzipAFile;
module.exports.unzipAFileString = unzipAFileString;
module.exports.getZipFileInformation = getZipFileInformation;


function compress(targetPath, srcPath, options, callback) {
    if (typeof options === 'function') {
        callback = options; options = {};
    }

    if (callback) {
        compressAsync(targetPath, srcPath, options, callback);
    } else {
        if (!fs.existsSync(srcPath)) {
            return null;
        } else if (fs.existsSync(targetPath)) {
            return null;
        } else {
            var targetDir = path.dirname(targetPath);
            if (!fs.existsSync(targetDir)) {
                extfs.mkdirpSync(targetDir);
            }
            return zip(targetPath, srcPath, options);
        }
    }
}


function compressAsync(targetPath, srcPath, options, callback) {
    var targetDir = path.dirname(targetPath);
    async.waterfall([
        //check input
        function (cb) {
            fs.exists(srcPath, function (exist) {
                if (!exist) {
                    cb(new Error('Source directory(' + srcPath + ') does not exists!'));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            fs.exists(targetPath, function (exist) {
                if (exist) {
                    cb(new Error('Target zip file(' + targetPath + ') already exists!'));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            fs.exists(targetDir, function (exist) {
                if (exist) {
                    cb(null);
                } else {
                    extfs.mkdirp(targetDir, function (err) {
                        cb(err);
                    });
                }
            });
        },
        function (cb) {
            zip(targetPath, srcPath, options, cb);
        }
    ], callback);
}


function uncompress(srcZipPath, targetDir, options, callback) {
    if (typeof options === 'function') {
        callback = options; options = {};
    }
    async.waterfall([
        //check input
        function (cb) {
            fs.exists(srcZipPath, function (exist) {
                if (!exist) {
                    cb(new Error('Source zip file(' + srcZipPath + ') does not exists!'));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            fs.exists(targetDir, function (exist) {
                if (exist) {
                    cb(null);
                } else {
                    extfs.mkdirp(targetDir, function (err) {
                        cb(err);
                    });
                }
            });
        },
        function (cb) {
            unzip(srcZipPath, targetDir, options, cb);
        }
    ], callback);
}


function execute(bin, options, envs, callback) {
    var message = '';
    var string = '';
    var process;
    process = spawn(bin, options, envs);
    process.stderr.on('data', function (data) {
        message = message + data;
    });
    process.stdout.on('data', function (data) {
        string = string + data;
    });
    process.on('close', function (code) {
        if (code === 0) {
            callback(null, string);
        } else {
            callback(new Error(message));
        }
    });
    process.on('uncaughtException', function (err) {
        callback(err);
    });

}


// NOTE. callback is optional
function zip(targetPath, sourceDir, options, callback) {
    var cmdArgs = [];
    var cmd = '';
    if (os.platform() === 'win32') {
        cmd = '7z';
        cmdArgs.push('a');
        cmdArgs.push('-tzip');
        cmdArgs.push(targetPath);
        cmdArgs.push('.');
        if (options.excludeList && options.excludeList.length !== 0) {
            _.each(options.excludeList, function (el) {
                cmdArgs.push('-xr!' + el);
            });
        }
    } else {
        cmd = 'zip';
        cmdArgs.push('-rqy');
        cmdArgs.push(targetPath);
        cmdArgs.push('.');
        if (options.excludeList && options.excludeList.length !== 0) {
            cmdArgs.push('-x');
            _.each(options.excludeList, function (el) {
                cmdArgs.push(el);
            });
        }
    }

    var newOptions = _.clone(options);
    newOptions.onExit = function (code) {
        if (options.onExit) {
            options.onExit(code);
        }
        if (callback) {
            if (code !== 0) {
                var error = new Error('zip process exited with code ' + code);
                return callback(error);
            } else {
                return callback(null);
            }
        }
    };

    return Process.create(cmd, cmdArgs, {
        cwd: sourceDir,
        env: process.env
    }, newOptions);
}


function unzip(sourcePath, targetDir, options, callback) {
    var cmdArgs = [];
    var cmd = '';

    if (os.platform() === 'win32') {
        cmd = '7z';
        cmdArgs = ['x', '-o' + targetDir, sourcePath];
        if (options.targetFile) {
            cmdArgs.push(options.targetFile);
        }
    } else {
        cmd = 'unzip';
        cmdArgs = ['-o', sourcePath];
        if (options.targetFile) {
            cmdArgs.push(options.targetFile);
        }
        cmdArgs.push('-d');
        cmdArgs.push(targetDir);
    }

    var newOptions = _.clone(options);
    newOptions.onExit = function (code) {
        if (options.onExit) {
            options.onExit(code);
        }
        callback(null);
    };

    return Process.create(cmd, cmdArgs, {
        cwd: process.cwd(),
        env: process.env
    }, newOptions);
}


function unzipAFile(sourcePath, targetDir, fileName, callback) {
    if (os.platform() === 'win32') {
        execute('7z', ['x', '-o' + targetDir, sourcePath, fileName], {
            cwd: process.cwd(),
            env: process.env
        }, function (err) {
            callback(err);
        });
    } else {
        execute('unzip', ['-u', sourcePath, fileName, '-d', targetDir], {
            cwd: process.cwd(),
            env: process.env
        }, function (err) {
            callback(err);
        });
    }
}


function unzipAFileString(sourcePath, fileName, callback) {
    if (os.platform() === 'win32') {
        execute('7z', ['x', sourcePath, fileName, '-so'], {
            cwd: process.cwd(),
            env: process.env
        }, callback);
    } else {
        execute('unzip', ['-p', sourcePath, fileName], {
            cwd: process.cwd(),
            env: process.env
        }, callback);
    }
}


function getZipFileInformation(zipFilePath, callback) {
    var entries;
    var files = [];
    var numberOfFiles = 0;
    var uncompressedSize = 0;

    async.waterfall([
        function (cb) {
            try {
                var zipFile = new AdmZip(zipFilePath);
                entries = zipFile.getEntries();
                cb(null);
            } catch (e) {
                cb(e);
            }
        },
        function (cb) {
            async.each(entries, function (entry, cb1) {
                numberOfFiles++;
                uncompressedSize += entry.header.size;
                files.push(entry.entryName);
                cb1(null);
            }, cb);
        }
    ], function (err) {
        if (err) {
            callback(err);
        } else {
            callback(err, {
                numberOfFiles: numberOfFiles,
                uncompressedSize: uncompressedSize,
                entry: files
            });
        }
    });
}
