/**
 * fs-distribution-api.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 fs = require('fs');
var extfs = require('fs-extra');
var path = require('path');
var _ = require('underscore');

var DError = require('../../../core/exception.js');
var util = require('../../org.tizen.ts.base.common/util.js');

module.exports.saveSnapshot = saveSnapshot;
module.exports.loadSnapshots = loadSnapshots;
module.exports.loadSnapshotInfoFromString = loadSnapshotInfoFromString;
module.exports.loadPackages = loadPackages;
module.exports.loadChangeLog = loadSnapshotChangeLog;
module.exports.loadOsInfo = loadOsInfo;
module.exports.copyFilesToRepository = copyFilesToRepository;

var SNAPSHOT_INFO_FILE = 'snapshot.info';

function saveSnapshot(distPath, snapshot, monitor, callback) {
    var snapshotPath = path.join(distPath, 'snapshots', snapshot.name);
    var changeLogPath = path.join(distPath, 'changes');

    async.waterfall([
        function (cb) {
            // create directory
            extfs.mkdirs(snapshotPath, function (err) {
                cb(err);
            });
        },
        function (cb) {
            // save archive packages
            saveArchivePackages(snapshotPath, snapshot.archivePackages, monitor, cb);
        },
        function (cb) {
            // save os info
            var osList = Object.keys(snapshot.osPackages);
            saveOsInfo(snapshotPath, osList, cb);
        },
        function (cb) {
            // save os packages
            saveOsPackages(snapshotPath, snapshot.osPackages, cb);
        },
        function (cb) {
            // save change log
            saveChangeLogs(changeLogPath, snapshot.name, snapshot.changes, monitor, cb);
        },
        function (cb) {
            loadSnapshots(distPath, cb);
        },
        function (snapshots, cb) {
            var insertIdx = null;
            if (snapshot.type === 'manual') {
                var originSnapshots = snapshots.filter(function (e) {
                    return e.name === snapshot.origin;
                });
                if (originSnapshots.length >= 1) {
                    insertIdx = snapshots.indexOf(originSnapshots[0]) + 1;
                } else {
                    insertIdx = snapshots.length;
                }
            } else {
                insertIdx = snapshots.length;
            }
            snapshots.splice(insertIdx, 0, snapshot);

            // write snapshots object array into snapshot.info file format
            saveSnapshotInfo(distPath, snapshots, function (err) {
                cb(err, snapshots);
            });
        },
        function (snapshots, cb) {
            if (snapshot.type !== 'temp' && snapshots.indexOf(snapshot) === snapshots.length - 1) {
                monitor.updateProgress(' # - Copying snapshot files into distribution directory.');
                copySnapshotFilesToRoot(distPath, snapshot, cb);
            } else {
                return cb(null);
            }
        }
    ],
    function (err) {
        callback(err);
    });
}

function saveSnapshotInfo(distPath, snapshots, callback) {
    var snapshotInfoPath = path.join(distPath, SNAPSHOT_INFO_FILE);

    var snapshotInfos = [];

    _.each(snapshots, function (snapshot) {
        snapshotInfos.push('name : ' + snapshot.name);
        snapshotInfos.push('time : ' + snapshot.time);
        snapshotInfos.push('type : ' + snapshot.type);
        if (snapshot.origin) {
            snapshotInfos.push('origin: ' + snapshot.origin);
        }
        snapshotInfos.push('path : ' + snapshot.path);
        snapshotInfos.push('');
    });

    fs.writeFile(snapshotInfoPath, snapshotInfos.join('\n'), callback);
}


function saveOsInfo(snapshotPath, osList, callback) {
    fs.writeFile(path.join(snapshotPath, 'os_info'), osList.join('\n'),
        function (err) {
            callback(err);
        });
}


function saveOsPackages(snapshotPath, osPackages, callback) {
    var osPkgList = _.map(osPackages, function (packages, os) {
        return [os, util.package.pkgListString(packages)];
    });

    async.each(osPkgList, function (osPkg, cb) {
        var os = osPkg[0];
        var contents = osPkg[1];
        fs.writeFile(path.join(snapshotPath, 'pkg_list_' + os), contents, cb);
    }, function (err) {
        callback(err);
    });
}


function saveArchivePackages(snapshotPath, archivePackages, monitor, callback) {
    fs.writeFile(path.join(snapshotPath, 'archive_pkg_list'), archivePackages.join('\n'),
        function (err) {
            if (err) {
                monitor.updateProgress(err);
            }
            callback(err);
        });
}


function saveChangeLogs(changeLogPath, snapshotName, changes, monitor, callback) {
    fs.writeFile(path.join(changeLogPath, snapshotName + '.log'), changes,
        function (err) {
            if (err) {
                monitor.updateProgress(err);
            }
            callback(err);
        });
}


function copySnapshotFilesToRoot(distPath, snapshot, callback) {
    var snapshotDirPath = path.join(distPath, snapshot.path);

    async.series([
        // copy os_info
        function (cb) {
            extfs.copy(path.join(snapshotDirPath, 'os_info'), path.join(distPath, 'os_info'), cb);
        },
        // copy archive_pkg_list
        function (cb) {
            fs.exists(path.join(snapshotDirPath, 'archive_pkg_list'), function (exists) {
                if (!exists) {
                    return cb(null);
                }
                extfs.copy(path.join(snapshotDirPath, 'archive_pkg_list'), path.join(distPath, 'archive_pkg_list'), cb);
            });
        },
        // copy pkg_list_*
        function (cb) {
            var osList = Object.keys(snapshot.osPackages);
            async.eachSeries(osList, function (os, cb1) {
                fs.exists(path.join(snapshotDirPath, 'pkg_list_' + os), function (exists) {
                    if (!exists) {
                        return cb1(null);
                    }
                    extfs.copy(path.join(snapshotDirPath, 'pkg_list_' + os), path.join(distPath, 'pkg_list_' + os), cb1);
                });
            }, cb);
        }], function (err) {
        callback(err);
    });
}


function copyFilesToRepository(distPath, updatedPkgs, options, monitor, callback) {
    var copied = [];

    async.eachSeries(Object.keys(updatedPkgs), function (pkgPath, cb) {
        var pkg = updatedPkgs[pkgPath];

        if (pkg && options.isTemp) {
            copyTempFileToRepository(pkgPath, distPath, options,
                function (err, file) {
                    if (!err) {
                        copied.push(file);
                    }
                    cb(err);
                });
        } else if (!_.isString(pkg)) {
            // binary package
            monitor.updateProgress(' # - Copying \'' + pkg.name + '\' binary package into repositoroy.');
            copyBinaryPackageFilesToRepository(pkgPath, pkg, distPath, options,
                function (err, files) {
                    if (!err) {
                        copied = copied.concat(files);
                    }
                    cb(err);
                });
        } else {
            // archive package
            monitor.updateProgress(' # - Copying \'' + path.basename(pkgPath) + '\' source package into repositoroy.');
            copyArchivePackageFileToRepository(pkgPath, distPath, options,
                function (err, file) {
                    if (!err) {
                        copied.push(file);
                    }
                    cb(err);
                });

        }
    },
    function (err) {
        if (err) {
            return callback(new DError('TREPO037', err), copied);
        } else {
            return callback(null, copied);
        }
    });
}


function copyTempFileToRepository(pkgPath, distPath, opts, callback) {
    var targetPath = path.join(distPath, 'temp', 'jobs', opts.tempJobId.toString(), path.basename(pkgPath));

    extfs.copy(pkgPath, targetPath, function (err) {
        callback(err, targetPath);
    });
}


function copyBinaryPackageFilesToRepository(pkgPath, pkg, distPath, options, callback) {

    if (options.uploadCompat) {
        var copied = [];
        async.eachSeries(pkg.osList, function (os, cb) {
            copyOsBinaryPackageFileToRepository(pkgPath, pkg, os, distPath, options,
                function (err, filePath) {
                    if (!err) {
                        copied.push(filePath);
                    }
                    cb(err);
                });
        }, function (err) {
            callback(err, copied);
        });
    } else {
        copyOsBinaryPackageFileToRepository(pkgPath, pkg, pkg.os, distPath, options,
            function (err, filePath) {
                callback(err, [filePath]);
            });
    }
}


function copyOsBinaryPackageFileToRepository(pkgPath, pkg, os, distPath, opts, callback) {
    var targetPath = path.join(distPath, 'binary', (pkg.name + '_' + pkg.version + '_' + os + '.zip'));

    extfs.copy(pkgPath, targetPath, function (err) {
        callback(err, targetPath);
    });
}

function copyArchivePackageFileToRepository(pkgPath, distPath, opts, callback) {
    var targetPath = path.join(distPath, 'source', path.basename(pkgPath));

    extfs.copy(pkgPath, targetPath, function (err) {
        callback(err, targetPath);
    });
}


function loadSnapshots(distPath, callback) {
    var snapshotInfoPath = path.join(distPath, SNAPSHOT_INFO_FILE);
    loadSnapshotInfo(snapshotInfoPath, function (err, results) {
        callback(err, results);
    });
}

function loadSnapshotInfo(snapshotInfoPath, callback) {

    async.waterfall([
        function (cb) {
            fs.readFile(snapshotInfoPath, { encoding: 'utf8' }, function (err, data) {
                if (err) {
                    // error message: new DError('TREPO010')
                    return cb(err, null);
                } else {
                    return cb(err, data);
                }
            });
        },
        function (data, cb) {
            var snapshots = loadSnapshotInfoFromString(data);
            cb(null, snapshots);
        }
    ],
    function (err, snapshots) {
        callback(err, snapshots);
    });
}

function loadSnapshotInfoFromString(contents) {
    var lines = contents.split('\n');

    var snapshots = [];
    var newSnapshot = {};

    _.each(lines, function (line) {
        var toks = line.split(/[: ]+/);

        if (toks[0] === 'name') {
            if (!_.isEmpty(newSnapshot)) {
                snapshots.push(newSnapshot);
                newSnapshot = {};
            }
            newSnapshot.name = toks[1];
        } else if (toks[0] === 'time') {
            newSnapshot.time = toks[1];
        } else if (toks[0] === 'type') {
            newSnapshot.type = toks[1];
        } else if (toks[0] === 'path') {
            newSnapshot.path = toks[1];
        } else if (toks[0] === 'origin') {
            newSnapshot.origin = toks[1];
        } else {
            // ignore
        }
    });

    if (!_.isEmpty(newSnapshot)) {
        snapshots.push(newSnapshot);
    }

    return snapshots;
}


function loadPackages(snapshotPath, callback) {
    var results = {};

    async.waterfall([
        function (cb) {
            loadArchivePackages(path.join(snapshotPath, 'archive_pkg_list'), function (err, archives) {
                results.archivePackages = archives;
                cb(err);
            });
        },
        function (cb) {
            loadOsInfo(path.join(snapshotPath, 'os_info'), cb);
        },
        function (osList, cb) {
            loadOsPackages(snapshotPath, osList, function (err, osPackages) {
                results.osPackages = osPackages;
                cb(err);
            });
        }
    ],
    function (err) {
        callback(err, results);
    });
}


function loadSnapshotChangeLog(distPath, snapshotName, callback) {
    var changeLogPath = path.join(distPath, 'changes', snapshotName + '.log');
    fs.readFile(changeLogPath, {
        encoding: 'utf8'
    }, function (err, changelog) {
        if (err) {
            return callback(err, null);
        } else {
            return callback(err, changelog);
        }
    });
}


function loadOsPackages(snapshotPath, osList, callback) {
    var newOsPackages = {};

    async.each(osList, function (os, cb) {
        var pkgListPath = path.join(snapshotPath, 'pkg_list_' + os);
        newOsPackages[os] = {};

        async.waterfall([
            function (cb) {
                fs.stat(pkgListPath, cb);
            },
            function (stats, cb) {
                if (stats.size > 0) {
                    util.package.getPkgListFromFile(pkgListPath, cb);
                } else {
                    cb(null, []);
                }
            },
            function (pkgList, cb) {
                _.each(pkgList, function (pkg) {
                    pkg.os = os;
                    pkg.path = '/binary/' + util.package.getPackageFileNameFromInfo(pkg.name, pkg.version, pkg.os);
                    newOsPackages[os][pkg.name] = pkg;
                });
                cb(null);
            }
        ],
        function (err) {
            cb(err);
        });
    },
    function (err) {
        callback(err, newOsPackages);
    });
}


function loadArchivePackages(archivePkgPath, callback) {
    fs.readFile(archivePkgPath, {
        encoding: 'utf8'
    }, function (err, contents) {
        if (err) {
            callback(err, []);
        } else {
            var archives = contents.split('\n').filter(function (e) {
                return (e !== '');
            });
            callback(err, archives);
        }
    });
}


function loadOsInfo(osInfoPath, callback) {
    fs.readFile(osInfoPath, {
        encoding: 'utf8'
    }, function (err, contents) {
        if (err) {
            callback(err, []);
        } else {
            var osList = contents.split('\n').filter(function (e) {
                return (e !== '');
            });
            callback(err, osList);
        }
    });
}