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

var fs = require('fs');
var extfs = require('fs-extra');
var async = require('async');
var path = require('path');
var _ = require('underscore');

var dibs = require('../../core/dibs');
var DError = require('../../core/exception.js');
var Utils = require('../../lib/utils.js');
var Snapshot = require('./snapshot.js');
var Feed = require('./feed-atom.js');


module.exports.create = createDistribution;
module.exports.load = loadDistributions;
module.exports.remove = removeDistribution;
module.exports.makeDistributionInfo = makeDistributionInfo;

module.exports.loadSnapshot = loadSnapshot;
module.exports.searchSnapshots = searchSnapshots;
module.exports.registerArtifacts = registerArtifacts;
module.exports.generateSnapshot = generateSnapshot;
module.exports.removeSnapshot = removeSnapshots;
module.exports.removePackages = removePackages;


var DIST_INFO_FILE = 'visualstudio-dist.idx';

function VisualStudioDistribution(name) {
    this.name = name;
    this.type = 'tizen-visualstudio';
    this.time = null;
    this.snapshots = [];
    this.latestSnapshot = null;
    this.path = null;
    this.options = {};
}

function makeDistributionInfo(repoPath, callback) {
    fs.writeFile(path.join(repoPath, DIST_INFO_FILE), '', callback);
}

function loadDistributions(repoPath, callback) {
    async.waterfall([
        function (cb) {
            loadDistributionInfo(repoPath, cb);
        },
        function (dists, cb) {
            async.map(dists, function (dist, cb1) {
                loadDistribution(dist.name, repoPath, function (err, dist2) {
                    if (err) {
                        cb1(err);
                    } else {
                        dist.snapshots = dist2.snapshots;
                        dist.latestSnapshot = dist2.latestSnapshot;
                        cb1(null, dist);
                    }
                });
            },
            function (err, results) {
                cb(err, results);
            });
        }
    ], callback);
}

function loadDistributionInfo(repoPath, callback) {
    loadDistributionInfoFile(path.join(repoPath, DIST_INFO_FILE), repoPath, callback);
}


function loadDistributionInfoFile(infoFilePath, repoPath, callback) {
    fs.readFile(infoFilePath, {
        encoding: 'utf8'
    },
        function (err, contents) {
            if (err) {
                return callback(err, null);
            } else {
                return callback(err, loadDistributionInfoString(contents, repoPath));
            }
        });
}

function loadDistributionInfoString(contents, repoPath) {
    var lines = contents.split('\n');
    var dists = [];
    var newDist = null;

    for (var i = 0; i < lines.length; i++) {
        var line = lines[i];
        var toks = line.split(/[: ]+/);
        if (toks[0] === 'name') {
            if (newDist !== null) {
                dists.push(newDist);
            }
            newDist = new VisualStudioDistribution(toks[1]);
            if (Utils.isURL(repoPath)) {
                newDist.path = repoPath + '/' + newDist.name;
            } else {
                newDist.path = path.join(repoPath, newDist.name);
            }
        } else if (toks[0] === 'time') {
            newDist.time = toks[1];
        } else if (toks[0] === 'id') {
            newDist.uid = toks[1];
        } else {
            // ignore
        }
    }
    if (newDist !== null) {
        dists.push(newDist);
    }

    return dists;
}

function loadDistribution(distName, repoPath, callback) {
    var dist = null;
    var distPath = path.join(repoPath, distName);

    async.series([
        function (cb) {
            fs.exists(distPath, function (exist) {
                if (exist) {
                    dist = new VisualStudioDistribution(distName);
                    return cb(null);
                } else {
                    return cb(new DError('VSREPO021', { distPath: distPath }));
                }
            });
        },
        function (cb) {
            searchSnapshots({ distName: distName, snapshotInfoOnly: true }, function (err, snapshots) {
                if (err) {
                    return cb(err);
                }

                dist.snapshots = snapshots;
                if (snapshots && snapshots.length > 0) {
                    var autoSnapshots = _.filter(snapshots, function (snapshot) {
                        return snapshot.attribute === 'auto';
                    });

                    var latestSnapshotName = autoSnapshots[autoSnapshots.length - 1].name;
                    searchSnapshots({ distName: distName, name: latestSnapshotName },
                        function (err1, results) {
                            dist.latestSnapshot = results[0];
                            for (var i = 0; i < dist.snapshots.length; i++) {
                                if (dist.snapshots[i].name === latestSnapshotName) {
                                    dist.snapshots[i] = results[0];
                                }
                            }
                            cb(err1);
                        }
                    );
                } else {
                    return cb(err);
                }
            });
        }
    ],
    function (err) {
        callback(err, dist);
    });
}

function createDistribution(dname, opts, repoPath, callback) {
    var newObj = new VisualStudioDistribution(dname);
    newObj.time = Utils.generateTimeStamp();
    newObj.path = path.join(repoPath, newObj.name);

    async.series([
        function (cb) {
            fs.mkdir(path.join(repoPath, dname), cb);
        },
        function (cb) {
            var atom = new Feed('Visual-Studio Extension Repository (' + dname + ')');
            atom.save(path.join(repoPath, dname, 'atom.xml'), cb);
        },
        function (cb) {
            fs.mkdir(path.join(repoPath, dname, 'artifacts'), cb);
        },
        function (cb) {
            fs.mkdir(path.join(repoPath, dname, 'snapshots'), cb);
        },
        function (cb) {
            fs.mkdir(path.join(repoPath, dname, 'temp'), cb);
        },
        function (cb) {
            appendDistributionInfo(newObj, repoPath, cb);
        }
    ],
    function (err) {
        callback(err, newObj);
    });
}

function appendDistributionInfo(newDist, repoPath, callback) {
    async.waterfall([
        function (wcb) {
            loadDistributionInfo(repoPath, wcb);
        },
        function (dists, wcb) {
            var lines = [];
            for (var i = 0; i < dists.length; i++) {
                var dist = dists[i];
                lines.push('name : ' + dist.name);
                lines.push('time : ' + dist.time);
                if (dist.uid) {
                    lines.push('id : ' + dist.uid);
                }
                lines.push('');
            }

            lines.push('name : ' + newDist.name);
            lines.push('time : ' + newDist.time);

            fs.writeFile(path.join(repoPath, DIST_INFO_FILE), lines.join('\n'), function (err) {
                wcb(err);
            });
        }
    ],
    function (err) {
        callback(err);
    });
}

function removeDistribution(dname, opts, repoPath, callback) {
    async.waterfall([
        async.apply(removeDistributionInfo, dname, repoPath),
        async.apply(extfs.remove, path.join(repoPath, dname))
    ], callback);
}

function removeDistributionInfo(dname, repoPath, callback) {
    async.waterfall([
        function (wcb) {
            loadDistributionInfo(repoPath, wcb);
        },
        function (dists, wcb) {
            var filtered = dists.filter(function (e) {
                return e.name === dname;
            });
            if (filtered.length > 0) {
                dists.splice(dists.indexOf(filtered[0]), 1);
            }

            var lines = [];
            for (var i = 0; i < dists.length; i++) {
                var dist = dists[i];
                lines.push('name : ' + dist.name);
                lines.push('time : ' + dist.time);
                if (dist.uid) {
                    lines.push('id : ' + dist.uid);
                }
                lines.push('');
            }

            fs.writeFile(path.join(repoPath, DIST_INFO_FILE), lines.join('\n'), wcb);
        }
    ],
    function (err) {
        callback(err);
    });
}

function removePackages(names, dist, opts, callback) {
    Snapshot.deregisterPackages(names, dist, opts, function (err, newSnapshot) {
        if (!err) {
            addSnapshotToDist(newSnapshot, dist);
        }
        callback(err, newSnapshot);
    });
}

function loadSnapshot(name, dist, callback) {
    searchSnapshots({ name: name, distName: dist.name }, function (err, results) {
        if (err) {
            callback(err, null);
        } else {
            callback(null, results[0]);
        }
    });
}

function generateSnapshot(distribution, name, opts, callback) {
    Snapshot.generateSnapshot(distribution, name, opts, function (err, snapshot) {
        if (err) {
            return callback(err, null);
        } else {
            return callback(null, snapshot);
        }
    });
}

function searchSnapshots(conditions, callback) {
    if (conditions.snapshotInfoOnly) {
        Snapshot.searchSnapshotList(conditions, callback);
    } else {
        Snapshot.searchSnapshots(conditions, callback);
    }
}

function registerArtifacts(distribution, rpaths, opts, callback) {
    var progress = opts.progress;

    if (progress) {
        progress('## Registering package(s) into DB...');
    }

    Snapshot.registerArtifacts(distribution, rpaths, opts, function (err, snapshot) {
        if (err) {
            return callback(err, null);
        } else {
            addSnapshotToDist(snapshot, distribution);
            return callback(null, snapshot);
        }
    });
}

function addSnapshotToDist(newSnapshot, dist) {
    var updatedPackages = Snapshot.getUpdatedPackages(newSnapshot, dist.latestSnapshot);

    dibs.thisServer.emitEvent({
        event: 'TIZEN_REPO_SNAPSHOT_GENERATED',
        distributionName: dist.name,
        updatedPackages: updatedPackages
    });

    // only "auto" snapshot can be the latest
    if (newSnapshot.attribute === 'auto') {
        dist.latestSnapshot = newSnapshot;
    }
}

function removeSnapshots(nameList, dist, opts, callback) { //-> callback(err)
    Snapshot.remove(nameList, dist, opts, callback);
}
