/**
 * distribution.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 dibs = require('../../../core/dibs.js');
var utils = require('../../../lib/utils.js');
var path = require('path');
var async = require('async');
var expressio = require('express.io');
var session = require('./session');
var DError = require('../../../core/exception.js');
var _ = require('underscore');

var router = new expressio.Router();

module.exports.router = router;


router.get('/distributions', session.checkSession, function (req, res) {
    var privilege = req.query.privilege;
    var queryPrivilege = req.query.queryPrivilege;
    var distStatus = req.query.status;
    var listOnly = req.query.listOnly || 'false';
    var condition = {
        email: req.session.email
    };

    // set status option
    if (privilege) {
        condition.privilege = privilege;
    }
    if (queryPrivilege) {
        condition.queryPrivilege = queryPrivilege;
    }
    if (distStatus) {
        condition.status = distStatus;
    }

    dibs.rpc.datamgr.searchDistributions(condition, function (err, distributions) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            if (listOnly === 'true') {
                for (var i in distributions) {
                    distributions[i].projects = {};
                }
            }
            res.send(distributions);
        }
    });
});


router.get('/distributions/:distName', session.checkSession, function (req, res) {
    var distName = req.param('distName');
    var email = req.session.email;
    var condition = {
        name: distName
    };

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'READ', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        },
        function (cb) {
            dibs.rpc.datamgr.searchDistributions(condition, cb);
        }], function (err, dists) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send(dists[0]);
        }
    });
});

router.post('/distributions', session.checkSession, function (req, res) {
    var groupName = req.body.groupName;
    var distName = req.body.name;
    var distType = req.body.type;
    var distStatus = req.body.status;
    var distAccess = req.body.access;
    var distSync = req.body.sync;
    var distDesc = req.body.description;
    var distOptions = req.body.options;
    var email = req.session.email;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'CREATE', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        },
        function (cb) {
            dibs.rpc.datamgr.addDistribution(groupName, distName,
                distType, distStatus, distAccess, distSync,
                distDesc, distOptions, cb);
        }], function (err) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send();
        }
    });
});


router.put('/distributions/:distName', session.checkSession, function (req, res) {
    var distName = req.param('distName');
    var action = req.query.action;
    var email = req.session.email;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'UPDATE', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        }], function (err) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            if (action) {
                synchronizeDistribution(distName, req, res);
            } else {
                updateDistribution(distName, req, res);
            }
        }
    });
});


function updateDistribution(distName, req, res) {
    var distGroupName = req.body.groupName;
    var distStatus = req.body.status;
    var distAccess = req.body.access;
    var distSync = req.body.sync;
    var distDesc = req.body.description;
    var distOptions = req.body.options;

    dibs.log.info('Received a request for distribution update...' + distName);
    dibs.rpc.datamgr.searchDistributions({
        name: distName
    }, function (err, results) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            var dist = results[0];
            dist.groupName = distGroupName || dist.groupName;
            dist.status = distStatus || dist.status;
            dist.access = distAccess || dist.access;
            dist.sync = distSync || dist.sync;
            if (dist.sync === 'NULL') {
                dist.sync = null;
            }
            dist.description = distDesc || dist.description;
            dist.options = distOptions || dist.options;

            dibs.rpc.datamgr.updateDistribution(dist, function (err, dist) {
                if (err) {
                    dibs.log.error(JSON.stringify(err));
                    res.status(503).send(JSON.stringify(err.message));
                } else {
                    res.send(dist);
                }
            });
        }
    });
}


function synchronizeDistribution(distName, req, res) {
    var dist = null;

    dibs.log.info('Received a request for distribution synchronization...' + distName);
    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.searchDistributions({
                name: distName
            }, cb);
        },
        function (dists, cb) {
            if (dists.length <= 0) {
                cb(new Error('Distribution not found! :' + distName)); return;
            }
            dist = dists[0];
            dibs.rpc.repo.synchronizeDistribution(distName, {
                repoType: dist.type,
                ResponseAtStart: true
            }, cb);
        }
    ], function (err) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send(dist);
        }
    });
}


router.delete('/distributions/:distName', session.checkSession, function (req, res) {
    var distName = req.param('distName');
    var email = req.session.email;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'DELETE', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        },
        function (cb) {
            dibs.rpc.datamgr.searchDistributions({
                name: distName
            }, function (err, results) {
                if (!err && results[0]) {
                    cb(null, results[0]);
                } else {
                    dibs.log.error(err);
                    cb(new Error('Fail to find the distribution!'), null);
                }
            });
        },
        function (dist, cb) {
            dibs.rpc.datamgr.removeDistribution(dist.name, cb);
        }], function (err) {
        if (err) {
            dibs.log.error(err);
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send();
        }
    });
});

// sortby = name / time
// order = asc / desc
// count = window size
// offset = offset from 0
// repoType = repository type
router.get('/distributions/:distName/snapshots', session.checkSession, function (req, res) {
    var distName = req.param('distName');
    var email = req.session.email;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'READ', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        }], function (err) {
        if (err) {
            dibs.log.error(err);
            res.status(503).send(JSON.stringify(err.message));
        } else {
            getSnapshotList(req, res);
        }
    });
});


function getSnapshotList(req, res) {
    var distName = req.param('distName');
    var sortby = req.query.sortby;
    var order = req.query.order;
    var repoType = req.query.repoType;
    var latest = req.query.latest;
    var remote = req.query.remote;
    var remoteDistName = req.query.remoteDistName;

    dibs.rpc.repo.searchSnapshots({
        distName: distName,
        latest: latest,
        repoType: repoType,
        snapshotInfoOnly: true,
        addChangeLog: true,
        remote: remote,
        remoteDistName: remoteDistName
    }, function (err, snapshots) {
        if (err) {
            dibs.log.error(err);
            res.status(503).send(JSON.stringify(err.message));
        } else {
            var list = [];
            // TO-BE: must be changed to plug-in model
            if (sortby === 'name') {
                list = _.sortBy(_.map(snapshots, function (snap) {
                    var output = {
                        name: snap.name,
                        time: snap.time,
                        // for tizen/visualstudio module
                        type: snap.attribute || snap.type,
                        changeLog: snap.options.changes || snap.changes
                    };
                    if (snap.origin) {
                        output.origin = snap.origin;
                    }
                    return output;
                }), function (snap) {
                    return snap.name;
                });
            } else {
                list = _.map(_.sortBy(snapshots, function (snapshot) {
                    return snapshot.time;
                }), function (snap) {
                    var output = {
                        name: snap.name,
                        time: snap.time,
                        // for tizen/visualstudio module
                        type: snap.attribute || snap.type,
                        changeLog: snap.options.changes || snap.changes
                    };
                    if (snap.origin) {
                        output.origin = snap.origin;
                    }
                    return output;
                });
            }
            if (order === 'desc') {
                list = list.reverse();
            }
            res.send(list);
        }
    });
}


router.get('/distributions/:distName/snapshots/:snapshotName', session.checkSession, function (req, res) {
    var distName = req.param('distName');
    var email = req.session.email;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'READ', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        }], function (err) {
        if (err) {
            dibs.log.error(err);
            res.status(503).send(JSON.stringify(err.message));
        } else {
            getSnapshotInfo(req, res);
        }
    });
});


function getSnapshotInfo(req, res) {
    var options = req.query;
    options.distName = req.param('distName');
    options.name = req.param('snapshotName');

    dibs.rpc.repo.searchSnapshots(options, function (err, snapshots) {
        if (err) {
            dibs.log.warn(err);
            res.status(503).send(JSON.stringify(err.message));
        } else if (!snapshots || snapshots.length === 0) {
            var error = new Error('Snapshot not found!');
            res.status(503).send(JSON.stringify(error));
        } else {
            res.send(snapshots[0]);
        }
    });
}


router.post('/distributions/:distName/snapshots', session.checkSession, function (req, res) {
    var email = req.session.email;
    var snapshotName = req.body.name;
    var distName = req.param('distName');
    var options = req.body.options;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'UPDATE', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        },
        function (cb) {
            dibs.rpc.datamgr.searchDistributions({
                name: distName
            }, cb);
        },
        function (dists, cb) {
            if (dists.length < 1) {
                return cb(new Error('Distribution not found!'));
            }

            dibs.rpc.repo.generateSnapshot(snapshotName, {
                distName: distName,
                repoType: dists[0].type,
                refSnapshot: options.refSnapshot,
                snapshotType: 'manual',
                userEmail: email
            }, cb);
        }
    ], function (err, snap) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send();
        }
    });
});


router.delete('/distributions/:distName/snapshots/:snapshotName', session.checkSession, function (req, res) {
    var email = req.session.email;
    var snapshotName = req.param('snapshotName');
    var distName = req.param('distName');
    var options = req.body.options;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'UPDATE', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        },
        function (cb) {
            dibs.rpc.datamgr.searchDistributions({
                name: distName
            }, cb);
        },
        function (dists, cb) {
            if (dists.length < 1) {
                return cb(new Error('Distribution not found!'));
            }

            dibs.rpc.repo.removeSnapshot([snapshotName], {
                distName: distName,
                repoType: dists[0].type
            }, cb);
        }
    ], function (err, snap) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send();
        }
    });
});


router.delete('/distributions/:distName/rmPackages', session.checkSession, function (req, res) {
    var email = req.session.email;
    var distName = req.param('distName');
    var options = req.body.options;
    var packages = req.body.pkgs;

    async.waterfall([
        function (cb) {
            dibs.rpc.datamgr.checkPrivilege('distribution', 'UPDATE', {
                distName: distName
            }, email, function (err, allowed) {
                if (allowed) {
                    cb(null);
                } else {
                    cb(new DError('SESSION006'));
                }
            });
        },
        function (cb) {
            dibs.rpc.datamgr.searchDistributions({
                name: distName
            }, cb);
        },
        function (dists, cb) {
            if (dists.length < 1) {
                return cb(new Error('Distribution not found!'));
            }

            dibs.rpc.repo.removePackages(packages, {
                distName: distName,
                repoType: dists[0].type,
                userEmail: email
            }, cb);
        }
    ], function (err) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send();
        }
    });
});
