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

var dfs = require('../dibs.dist-fs/dist-fs.js');
var Package = require('../dibs.core.server/package.js');
var Utils = require('../../lib/utils.js');
var DError = require('../../core/exception.js');


module.exports.load = loadSnapshots;
module.exports.save = saveSnapshot;
module.exports.create = createSnapshot;
module.exports.remove = removeSnapshot;

module.exports.appendPackages = appendPackagesToSnapshot;
module.exports.removePackages = removePackagesFromSnapshot;
module.exports.prepareToDownload = prepareToDownload;


function DIBSSnapshot(name) {
    var self = this;
    this.name = name;
    this.type = 'auto';
    this.time = null;
    this.path = null;
    this.origin = null;
    this.packages = {};
}


function loadSnapshots(distPath, callback) {
    var snapshotInfoPath = path.join(distPath, 'snapshots.idx');
    if (!fs.existsSync(snapshotInfoPath)) {
        callback(new DError('TIZENTAREPO010'), null);
        return;
    }

    // load snapshot info
    var snapshots = loadSnapshotInfoFromFile(snapshotInfoPath);

    async.mapLimit(snapshots, 10, function (snapshot, cb) {
        var snapshotFilePath = path.join(distPath, snapshot.path);
        loadSnapshot(snapshotFilePath, function (err, s) {
            if (!err) {
                snapshot.packages = s.packages;
            }

            cb(err, snapshot);
        });
    }, function (err, results) {
        callback(err, results);
    });
}


function loadSnapshotInfoFromFile(snapshotInfoPath) {
    return extfs.readJsonSync(snapshotInfoPath);
}


function loadSnapshot(snapshotFilePath, callback) {
    // check
    if (!fs.existsSync(snapshotFilePath)) {
        callback(new DError('TIZENTAREPO011'), null);
        return;
    }

    // create
    var newObj = new DIBSSnapshot('Empty');

    async.series([
        // load os packages
        function (cb) {
            loadPackages(snapshotFilePath, function (err, packages) {
                if (!err) {
                    newObj.packages = packages;
                }
                cb(err);
            });
        }
    ], function (err) {
        callback(err, newObj);
    });
}


function loadPackages(snapshotFilePath, callback) {
    callback(null, extfs.readJsonSync(snapshotFilePath).packages);
}


function createSnapshot(sname, src) {
    var timestamp = Utils.generateTimeStamp(true);
    var newObj = null;

    if (sname === null) {
        newObj = new DIBSSnapshot(timestamp);
        newObj.type = 'auto';
    } else {
        newObj = new DIBSSnapshot(sname);
        newObj.type = 'manual';
        newObj.origin = src.name;
    }
    newObj.time = Utils.generateTimeStamp();

    if (src) {
        var pkgs = src.packages;

        newObj.packages = {};
        for (var pname in pkgs) {
            var pkg = pkgs[pname];
            newObj.packages[pname] = pkg;
        }
    }

    return newObj;
}


function saveSnapshot(snapshot, distPath, callback) {
    // create directory
    var snapshotFilePath = path.join(distPath, 'snapshots', snapshot.name + '.idx');

    extfs.outputJsonSync(snapshotFilePath, snapshot);

    // update path
    snapshot.path = '/snapshots/' + snapshot.name + '.idx';

    // add snapshot
    var snapshotInfoPath = path.join(distPath, 'snapshots.idx');
    var snapshots = loadSnapshotInfoFromFile(snapshotInfoPath);
    snapshots.push(snapshot);

    // save snapshot info
    saveSnapshotInfo(snapshots, snapshotInfoPath);

    callback(null);
}


function saveSnapshotInfo(snapshots, snapshotInfoPath) {

    extfs.outputJsonSync(snapshotInfoPath, snapshots.map(function (e) {
        if (e.origin) {
            return {
                name: e.name,
                type: e.type,
                time: e.time,
                path: e.path,
                origin: e.origin
            };
        } else {
            return {
                name: e.name,
                type: e.type,
                time: e.time,
                path: e.path
            };
        }
    }));
}


function removeSnapshot(sname, distPath, callback) {
    // remove from snapshots
    var snapshotInfoPath = path.join(distPath, 'snapshots.idx');
    var snapshots = loadSnapshotInfoFromFile(snapshotInfoPath).filter(function (e) {
        return e.name !== sname;
    });

    // save snapshot info
    saveSnapshotInfo(snapshots, snapshotInfoPath);

    callback(null);
}


function appendPackagesToSnapshot(fileInfos, oldSnapshot, force, callback) {
    // create
    var newSnapshot = createSnapshot(null, oldSnapshot);

    // update
    for (var dfsPath in fileInfos) {
        var pkg = fileInfos[dfsPath];

        // check version
        if (!force && oldSnapshot) {
            oldPkg = oldSnapshot.packages[pkg.name];

            if (oldPkg) {
                callback(new DError('TIZENTAREPO013', {
                    new: pkg.name
                }));
                return;
            }
        }

        newSnapshot.packages[pkg.name] = pkg;
    }

    // validate
    validateSnapshot(newSnapshot, function (err) {
        if (!err) {
            callback(err, newSnapshot);
        } else {
            callback(err, null);
        }
    });
}


function removePackagesFromSnapshot(names, oldSnapshot, opts, callback) {

    // create
    var newSnapshot = createSnapshot(null, oldSnapshot);

    // update
    for (var i = 0; i < names.length; i++) {
        var pname = names[i];

        var pkg = newSnapshot.packages[pname];
        if (pkg) {
            delete (newSnapshot.packages)[pname];
        } else {
            callback(new DError('TIZENTAREPO014', {
                pkg: pname
            }), null);
            return;
        }
    }

    // validate
    validateSnapshot(newSnapshot, function (err) {
        if (!err) {
            callback(err, newSnapshot);
        } else {
            callback(err, null);
        }
    });
}


// TODO: must check install dependency
function validateSnapshot(snapshot, callback) {
    var errMsgs = [];

    if (errMsgs.length > 0) {
        callback(new DError('TIZENTAREPO016', {
            snapshot: snapshot.name,
            errMsg: errMsgs.join('\n')
        }));
    } else {
        callback(null);
    }
}


function prepareToDownload(name, snapshot, distPath, os, callback) {

    async.waterfall([
        function (cb) {
            var pkg = snapshot.packages[name];
            if (!pkg) {
                cb(new DError('TIZENTAREPO018', {
                    pkg: name
                }), null, null);
            } else {
                if (Utils.isURL(distPath)) {
                    cb(null, distPath + '/' + pkg.path, pkg);
                } else {
                    cb(null, path.join(distPath, pkg.path), pkg);
                }
            }
        },
        function (lpath, pkg, cb) {
            if (snapshot.remote) {
                cb(null, lpath);
            } else {
                var opts = {};
                if (pkg.path && pkg.size && pkg.checksum) {
                    opts.name = path.basename(pkg.path);
                    opts.size = pkg.size;
                    opts.checksum = pkg.checksum;
                }

                // NOTE. DOWNLOAD must be finished in 1 hour after adding this file to DFS
                //      Otherwise, added files will be removed on DFS
                opts.lifetime = 60 * 60 * 1000;
                dfs.addFile(null, lpath, opts, function (err, dfsPath2) {
                    cb(err, dfsPath2);
                });
            }
        }], function (err, dfsPath) {
        callback(err, dfsPath);
    });

}
