/**
 * job.js
 * Copyright (c) 2000 - 2015 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 async = require('async');
var _ = require('underscore');

var dibs = require('../../../core/dibs.js');
var expressio = require('express.io');
var jobBuilder = require('../../dibs.model.sqlbuilder/job.js');
var session = require('./session');
var DError = require('../../../core/exception.js');

var router = new expressio.Router();

module.exports.router = router;


router.get('/jobs', session.checkSession, function (req, res) {
    var request = req.query;
    var error = null;

    var init = request.init;
    if (init) {
        // 'init' option is used when initializng grid
        return res.send([]);
    }

    // extract key values to parse. it might be variable
    var params = _.keys(request);

    var sort = null;
    var order = 'id';
    var orderType = 'ASC';
    var offset = 0;
    var count = 25;
    var limit = null;

    var options = {
        id: request.id,
        distribution_name: request.distName,
        project_name: request.projectName,
        project_type: request.projectType,
        environment_name: request.envName,
        status: request.status,
        statusInfo: request.statusInfo,
        parentId: request.parentId,
        parent: request.parent,
        user_email: request.userEmail,
        user_name: request.userName,
        children: request.children,
        start_time: request.startTime,
        end_time: request.endTime,
        snapshot_name: request.snapshot,
        start_snapshot: request.startSnapshot,
        end_snapshot: request.endSnapshot
    };

    var jobResults = null;

    var conn = null;

    var childCondition = req.query['$child'];
    if (childCondition) {
        options['$child'] = JSON.parse(childCondition.toString());
    }

    _.each(params, function (key) {
        /*
        * ex) param: ['sort', '-id)'];
        */
        var param = key.split('(');
        if (param[0] === 'sort') {
            sort = param[1].split(')')[0];

            if (sort[0] === '+') {
                orderType = 'ASC';
            } else if (sort[0] === '-') {
                orderType = 'DESC';
            } else {
                error = new DError('WEB001', { arguments: sort });
                dibs.log.error(error.message);
                return res.status(503).send(JSON.stringify(error.message));
            }
            order = sort.substr(1, sort.length - 1);
        } else if (param[0] === 'limit') {
            limit = param[1].split(')')[0];

            var limitToks = limit.split(',');
            if (limitToks.length === 2) {
                offset = parseInt(limitToks[1], 10);
                count = parseInt(limitToks[0], 10);
            } else {
                offset = 0;
                count = parseInt(limitToks[0], 10);
            }
        }
    });

    if (req.headers.range) {
        var syntax = new RegExp('^items[ ]*=[ ]*[0-9]+-[0-9]+$');
        var range = req.headers.range;
        if (!syntax.test(range)) {
            error = new DError('WEB001', { arguments: range });
            dibs.log.error(error.message);
            return res.status(503).send(JSON.stringify(error.message));
        }
        var arg = req.headers.range.split('=');
        var data = arg[1].split('-');
        offset = data[0];
        count = data[1] - data[0] + 1;
    }

    options.order = orderType;
    options.limit = limit;
    options.offset = offset;
    options.count = count;

    async.waterfall([
        function (cb) {
            // dbConn: mysql connection pool
            // getConnection: acquire connection from pool
            dibs.thisServer.dbConn.getConnection(function (err1, connection) {
                if (err1) {
                    dibs.log.error(err1);
                }

                conn = connection;
                cb(err1);
            });
        },
        function (cb) {
            generateJobData(conn, options, function (err1, results) {
                if (err1) {
                    dibs.log.error(err1);
                }
                jobResults = results;
                cb(err1);
            });
        },
        function (cb) {
            if (req.headers.range || limit) {
                options.parent_id = 'NULL';
                jobBuilder.selectJobCount(conn, options, function (err1, count) {
                    if (err1) {
                        dibs.log.error(err1);
                    } else {
                        res.header('Content-Range', 'items ' + offset + '-' +
                            (parseInt(offset, 10) + (jobResults.length - 1)) + '/' + count);
                    }
                    cb(err1);
                });
            } else {
                cb(null);
            }
        },
        function (cb) {
            // update job info
            async.map(jobResults, function (job, cb1) {
                jobBuilder.selectJobInfo(conn, job.id, function (err1, jobInfo) {
                    if (!err1) {
                        job.options = jobInfo;
                    }
                    cb1(err1, job);
                });
            },
            function (err, results) {
                jobResults = results;
                cb(err);
            });
        }
    ],
    function (err) {
        if (err) {
            dibs.log.error(err);
        }
        dibs.thisServer.dbConn.releaseConnection(conn);

        res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
        res.header('Pragma', 'no-cache');
        res.header('Expires', 0);

        res.send(jobResults);
    });
});


router.get('/jobs/:id', session.checkSession, function (req, res) {
    var jobId = req.param('id');
    var options = {
        id: jobId,
        children: true
    };

    if (jobId === undefined) {
        res.status(503).send('job id is undefined!');
        return;
    }
    var jobObj = {};
    var conn = null;

    async.waterfall([
        function (cb) {
            dibs.thisServer.dbConn.getConnection(function (err1, connection) {
                if (err1) {
                    dibs.log.error(err1);
                }

                conn = connection;
                cb(err1);
            });
        },
        function (cb) {
            generateJobData(conn, options, function (err1, results) {
                if (err1) {
                    dibs.log.error(err1);
                }
                jobObj = results[0];
                cb(err1);
            });
        },
        function (cb) {
            // update job info
            jobBuilder.selectJobInfo(conn, jobId, function (err1, jobInfo) {
                if (err1) {
                    dibs.log.error(err1);
                }
                jobObj.options = jobInfo;
                cb(err1);
            });
        },
        function (cb) {
            // update job board
            jobBuilder.selectJobBoard(conn, jobId, function (err1, jobBoard) {
                if (err1) {
                    dibs.log.error(err1);
                }
                jobObj.board = jobBoard;
                cb(err1);
            });
        }
    ],
    function (err) {
        dibs.thisServer.dbConn.releaseConnection(conn);

        if (err) {
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send(JSON.stringify(jobObj));
        }
    });
});


// dummy
router.put('/jobs/:id', session.checkSession, function (req, res) {
    res.send();
});


router.post('/jobs', session.checkSession, function (req, res) {
    var body = req.body;
    var projectName = body.projectName;
    var distName = body.distName;
    var userEmail = body.userEmail;
    var envName = body.envName;
    var options = body.options;
    if (options === undefined) {
        options = {};
    } else {
        if (envName === undefined && body.options.environments !== undefined) {
            envName = body.options.environments[0];
            delete body.options.environments;
        }
    }

    if (projectName === undefined || projectName === null) {
        dibs.log.error('Project name is not specified!');
        res.status(503).send(JSON.stringify('Project name is not specified!'));
        return;
    }

    dibs.rpc.jobmgr.addJob(userEmail, distName, projectName, envName, options, function (err, jobId) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send();
        }
    });
});


router.delete('/jobs', session.checkSession, function (req, res) {
    var jobId = req.body.id;

    if (!jobId) {
        dibs.log.error('Job ID is not specified!');
        res.status(503).send(JSON.stringify('Job ID is not specified!'));
        return;
    }

    dibs.rpc.jobmgr.cancelJob(jobId, function (err, jobId) {
        if (err) {
            dibs.log.error(JSON.stringify(err));
            res.status(503).send(JSON.stringify(err.message));
        } else {
            res.send();
        }
    });
});


function createJobObject(res) {
    var job = {};

    job.id = res.id;
    job.userEmail = res.user_email;
    job.distName = res.distribution_name;
    job.distType = res.distribution_type;
    job.projectName = res.project_name;
    job.projectType = res.project_type;
    job.environmentName = res.environment_name;
    job.parentId = res.parent_id;
    job.type = res.type;
    job.status = res.status;
    job.statusInfo = res.status_info;
    job.startTime = res.start_time;
    job.endTime = res.end_time;
    job.snapshotName = res.snapshot_name;
    job.approvalId = res.approval_id;
    job.userName = res.name;
    job.options = null;

    return job;
}


function addSubJobInfo(conn, jobObjs, callback) {
    async.map(jobObjs, function (jobObj, cb) {
        var job = createJobObject(jobObj);

        job.board = null;
        job.children = false;
        job.childCount = 0;

        if (job.parentId === null) {
            jobBuilder.selectJobCount(conn, { parent_id: job.id }, function (err1, count) {
                if (count && count > 0) {
                    job.childCount = count;
                    job.children = true;
                    job['$ref'] = 'node' + job.id;
                } else {
                    job.hasChildren = false;
                }
                cb(err1, job);
            });
        } else {
            job.hasChildren = false;
            cb(null, job);
        }
    },
    function (err, results) {
        callback(err, results);
    });
}


function generateJobData(conn, options, callback) {
    var isQueryParentJobs = true;
    var clonedOpts = _.clone(options);

    if (options.children || options.parent || options.project_name) {
        isQueryParentJobs = false;
    }

    var jobObjs = null;

    async.waterfall([
        function (cb) {
            if (isQueryParentJobs) {
                clonedOpts.parent_id = 'NULL';
            }

            // query jobs using given options from client requests.
            jobBuilder.selectJob(conn, clonedOpts, function (err1, results) {
                if (err1) {
                    dibs.log.error(err1);
                }
                jobObjs = results;
                cb(err1);
            });
        },
        function (cb) {
            if (clonedOpts.project_name) {
                var jobIds = [];
                _.each(jobObjs, function (job) {
                    if (job.parent_id) {
                        jobIds.push(job.parent_id);
                    } else {
                        // in case of single job
                        jobIds.push(job.id);
                    }
                });

                jobBuilder.selectJob(conn, { id: _.unique(jobIds) }, function (err1, results) {
                    if (err1) {
                        dibs.log.error(err1);
                    }

                    jobObjs = results;
                    cb(err1);
                });
            } else {
                cb(null);
            }
        },
        function (cb) {
            addSubJobInfo(conn, jobObjs, cb);
        }
    ],
    function (err, results) {
        callback(err, results);
    });
}
