/**
 * job-statistics.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 async = require('async');
var _ = require('underscore');

var dibs = require('../../core/dibs.js');
var utils = require('../../lib/utils.js');
var DError = require('../../core/exception');
var Job = require('../../plugins/dibs.model.common/job');


module.exports.create = create;


function JobStatistics(parent) {
    this.server = parent;
    var self = this;

    this.queryStatistics = function (opts, callback) {
        var queryCond = {};
        var err = null;

        // parsing querying options
        err = checkQueryingDate(opts, queryCond);
        if (err) {
            return callback(err, null);
        }

        if (opts.distName) {
            queryCond.distName = opts.distName;
        }
        if (opts.userEmail) {
            queryCond.user_email = opts.userEmail;
        }
        if (opts.projectName) {
            queryCond.project_name = opts.projectName;
        }
        //if ( opts.groupName ) { queryCond.groupName = opts.groupName; }
        //if ( opts.serverName ) { queryCond.serverName = opts.serverName; }

        // generate statistics result
        generateStatisticsForQueryingJobs(queryCond, callback);
    };


    function checkQueryingDate(opts, queryCond) {
        if (opts.dateFrom) {
            var startTime = null;
            if (_.isDate(opts.dateFrom) || typeof (opts.dateFrom) === 'string' || opts.dateFrom instanceof String) {
                startTime = new Date(opts.dateFrom);
            } else {
                return new DError('DATEMGR015', {
                    str: opts.dateFrom
                });
            }
            startTime.setHours(0);
            queryCond.start_time = utils.getTimeString(startTime);
        }

        if (opts.dateTo) {
            var endTime = null;
            if (_.isDate(opts.dateTo) || typeof (opts.dateTo) === 'string' || opts.dateTo instanceof String) {
                endTime = new Date(opts.dateTo);
            } else {
                return new DError('DATEMGR015', {
                    str: opts.dateTo
                });
            }
            endTime.setHours(23);
            endTime.setMinutes(59);
            endTime.setSeconds(59);
            queryCond.end_time = utils.getTimeString(endTime);
        }

        return null;
    }


    function generateStatisticsForQueryingJobs(queryCond, callback) {
        var result = null;
        var connection = null;

        async.waterfall([
            function (cb) {
                self.server.getDBHandle().getConnection(cb);
            },
            function (conn, cb) {
                connection = conn;
                Job.select(conn, queryCond, cb);
            },
            function (jobs, cb) {
                result = {
                    totalJobs: 0,
                    successJobs: 0,
                    successRate: 0,
                    failJobs: 0,
                    failRate: 0,
                    trends: {},
                    distributions: {},
                    users: {},
                    groups: {},
                    servers: {}
                };

                for (var i = 0; i < jobs.length; i++) {
                    var job = jobs[i];

                    // skip if parent job
                    if (job.childCount > 0) {
                        continue;
                    }

                    result.totalJobs++;
                    if (job.status === 'FINISHED') {
                        result.successJobs++;
                    } else {
                        result.failJobs++;
                    }

                    accumulateTrendData(job, result.trends);

                    accumulateDistributionData(job, result.distributions);
                    accumulateUserData(job, result.users);
                    accumulateGroupsData(job, result.groups);
                    accumulateServerData(job, result.servers);
                }

                result.successRate = Math.round(result.successJobs * 100 / result.totalJobs * 10) / 10;
                result.failRate = Math.round(result.failJobs * 100 / result.totalJobs * 10) / 10;

                cb(null, result);
            }
        ], function (err, result) {
            if (connection) {
                connection.release();
            }
            callback(err, result);
        });
    }


    function accumulateTrendData(job, trends) {

        // for trend
        var dateTime = new Date(job.startTime);
        var date = dateTime.format('YYYY-MM-DD');
        var week = new Date(dateTime.setDate(dateTime.getDate() - dateTime.getDay())).format('YYYY-MM-DD');
        var month = new Date(job.startTime).format('YYYY-MM');

        if (!trends.daily) {
            trends.daily = {
                totalJobs: {},
                successJobs: {},
                successRate: {},
                failJobs: {},
                failRate: {}
            };
            trends.weekly = {
                totalJobs: {},
                successJobs: {},
                successRate: {},
                failJobs: {},
                failRate: {}
            };
            trends.monthly = {
                totalJobs: {},
                successJobs: {},
                successRate: {},
                failJobs: {},
                failRate: {}
            };
        }

        // total jobs
        if (!trends.daily.totalJobs[date]) {
            trends.daily.totalJobs[date] = 0;
        }
        trends.daily.totalJobs[date]++;

        if (!trends.weekly.totalJobs[week]) {
            trends.weekly.totalJobs[week] = 0;
        }
        trends.weekly.totalJobs[week]++;

        if (!trends.monthly.totalJobs[month]) {
            trends.monthly.totalJobs[month] = 0;
        }
        trends.monthly.totalJobs[month]++;

        // success jobs
        if (job.status === 'FINISHED') {
            if (!trends.daily.successJobs[date]) {
                trends.daily.successJobs[date] = 0;
            }
            trends.daily.successJobs[date]++;

            if (!trends.weekly.successJobs[week]) {
                trends.weekly.successJobs[week] = 0;
            }
            trends.weekly.successJobs[week]++;

            if (!trends.monthly.successJobs[month]) {
                trends.monthly.successJobs[month] = 0;
            }
            trends.monthly.successJobs[month]++;
        } else {
            if (!trends.daily.failJobs[date]) {
                trends.daily.failJobs[date] = 0;
            }
            trends.daily.failJobs[date]++;

            if (!trends.weekly.failJobs[week]) {
                trends.weekly.failJobs[week] = 0;
            }
            trends.weekly.failJobs[week]++;

            if (!trends.monthly.failJobs[month]) {
                trends.monthly.failJobs[month] = 0;
            }
            trends.monthly.failJobs[month]++;
        }

        for (var date in trends.daily.totalJobs) {
            trends.daily.successRate[date] = Math.round(
                    trends.daily.successJobs[date] * 100 /
                    trends.daily.totalJobs[date] * 10) / 10;
            trends.daily.failRate[date] = Math.round(
                    trends.daily.failJobs[date] * 100 /
                    trends.daily.totalJobs[date] * 10) / 10;
        }
        for (var week in trends.weekly.totalJobs) {
            trends.weekly.successRate[week] = Math.round(
                    trends.weekly.successJobs[week] * 100 /
                    trends.weekly.totalJobs[week] * 10) / 10;
            trends.weekly.failRate[week] = Math.round(
                    trends.weekly.failJobs[week] * 100 /
                    trends.weekly.totalJobs[week] * 10) / 10;
        }
        for (var month in trends.monthly.totalJobs) {
            trends.monthly.successRate[month] = Math.round(
                    trends.monthly.successJobs[month] * 100 /
                    trends.monthly.totalJobs[month] * 10) / 10;
            trends.monthly.failRate[month] = Math.round(
                    trends.monthly.failJobs[month] * 100 /
                    trends.monthly.totalJobs[month] * 10) / 10;
        }
    }


    function accumulateDistributionData(job, distributions) {

        if (!distributions[job.distName]) {
            distributions[job.distName] = {
                totalJobs: 0,
                successJobs: 0,
                successRate: 0,
                failJobs: 0,
                failRate: 0,
                trends: {},
                projects: {}
            };
        }
        var result = distributions[job.distName];

        result.totalJobs++;
        if (job.status === 'FINISHED') {
            result.successJobs++;
        } else {
            result.failJobs++;
        }
        result.successRate = Math.round(result.successJobs * 100 / result.totalJobs * 10) / 10;
        result.failRate = Math.round(result.failJobs * 100 / result.totalJobs * 10) / 10;

        accumulateTrendData(job, result.trends);

        accumulateProjectData(job, result.projects);
    }


    function accumulateProjectData(job, projects) {

        if (!projects[job.projectName]) {
            projects[job.projectName] = {
                totalJobs: 0,
                successJobs: 0,
                successRate: 0,
                failJobs: 0,
                failRate: 0,
                totalExecTime: 0,
                avgExecTime: -1
            };
        }
        var result = projects[job.projectName];

        result.totalJobs++;
        if (job.status === 'FINISHED') {
            var initStartTimeObj = job.board.filter(function (e) {
                return e.name === 'INITIALIZING';
            })[0];
            var initEndTimeObj = job.board.filter(function (e) {
                return e.name === 'INITIALIZED';
            })[0];
            var execStartTimeObj = job.board.filter(function (e) {
                return e.name === 'WORKING';
            })[0];
            var execEndTimeObj = job.board.filter(function (e) {
                return e.name === 'FINISHED';
            })[0];
            if (initStartTimeObj && initEndTimeObj && execStartTimeObj && execEndTimeObj) {
                result.successJobs++;
                var initStartTime = new Date(initStartTimeObj.time);
                var initEndTime = new Date(initEndTimeObj.time);
                var execStartTime = new Date(execStartTimeObj.time);
                var execEndTime = new Date(execEndTimeObj.time);
                result.totalExecTime += ((initEndTime - initStartTime) +
                    (execEndTime - execStartTime));
            } else {
                result.failJobs++;
            }
        } else {
            result.failJobs++;
        }
        result.successRate = Math.round(result.successJobs * 100 / result.totalJobs * 10) / 10;
        result.failRate = Math.round(result.failJobs * 100 / result.totalJobs * 10) / 10;
        if (result.successJobs > 0) {
            result.avgExecTime = Math.round((result.totalExecTime / 1000 / result.successJobs) * 10) / 10;
        }
    }


    function accumulateUserData(job, users) {

        if (!users[job.userEmail]) {
            users[job.userEmail] = {
                totalJobs: 0,
                successJobs: 0,
                successRate: 0,
                failJobs: 0,
                failRate: 0,
                trends: {},
                projects: {}
            };
        }
        var result = users[job.userEmail];

        result.totalJobs++;
        if (job.status === 'FINISHED') {
            result.successJobs++;
        } else {
            result.failJobs++;
        }
        result.successRate = Math.round(result.successJobs * 100 / result.totalJobs * 10) / 10;
        result.failRate = Math.round(result.failJobs * 100 / result.totalJobs * 10) / 10;

        accumulateTrendData(job, result.trends);

        accumulateProjectData(job, result.projects);
    }


    function accumulateGroupsData(job, groups) {
        // skip when there no user group info
        if (!job.options || !job.options.USER_GROUPS) {
            return;
        }

        for (var i = 0; i < job.options.USER_GROUPS.length; i++) {
            accumulateSingleGroupData(job, job.options.USER_GROUPS[i], groups);
        }
    }


    function accumulateSingleGroupData(job, groupName, groups) {

        if (!groups[groupName]) {
            groups[groupName] = {
                totalJobs: 0,
                successJobs: 0,
                successRate: 0,
                failJobs: 0,
                failRate: 0,
                trends: {},
                projects: {}
            };
        }
        var result = groups[groupName];

        result.totalJobs++;
        if (job.status === 'FINISHED') {
            result.successJobs++;
        } else {
            result.failJobs++;
        }
        result.successRate = Math.round(result.successJobs * 100 / result.totalJobs * 10) / 10;
        result.failRate = Math.round(result.failJobs * 100 / result.totalJobs * 10) / 10;

        accumulateTrendData(job, result.trends);

        accumulateProjectData(job, result.projects);
    }


    function accumulateServerData(job, servers) {

        // skip when no server info
        if (!job.execServerId) {
            return;
        }

        if (!servers[job.execServerId]) {
            servers[job.execServerId] = {
                totalJobs: 0,
                successJobs: 0,
                successRate: 0,
                failJobs: 0,
                failRate: 0,
                trends: {},
                projects: {}
            };
        }
        var result = servers[job.execServerId];

        result.totalJobs++;
        if (job.status === 'FINISHED') {
            result.successJobs++;
        } else {
            result.failJobs++;
        }
        result.successRate = Math.round(result.successJobs * 100 / result.totalJobs * 10) / 10;
        result.failRate = Math.round(result.failJobs * 100 / result.totalJobs * 10) / 10;

        accumulateTrendData(job, result.trends);

        accumulateProjectData(job, result.projects);
    }
}


function create(parent) {
    parent.log.info('Creating job statics...');
    return new JobStatistics(parent);
}
