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

var DError = require('./exception');
var dibs = new CoreFramework();
module.exports = dibs;

var MASTER_SERVER_TYPE = 'master';
/**
 * @module core/dibs
 */

/**
 * @constructor
 * @memberOf module:core/dibs
 */
function CoreFramework() {
    var self = this;
    /** HASH (extension point name -> extensionPoint:{@link module:core/plugin/extension-point.ExtensionPoint}) */
    var exp = null;
    /** HASH (extension name -> extension:{@link module:core/plugin/extension.Extension}) */
    var exts = null;
    /** HASH (ServerType:string -> server:{@link module:core/base-server.BaseServer}) */
    var servers = {};
    /** HASH (ServerType:string -> server:{@link module:core/base-server.BaseServer}) */
    this.serverTypes = {};
    /** HASH (projectType:string -> server:object <loaded extension object>) */
    this.repositoryTypes = {};
    this.projectTypes = {};
    this.version = '0.0.0';

    /**
     * @method createServer
     * @param {string} sid - server id
     * @param {string} stype - server type
     * @returns {module:core/base-server.BaseServer}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.createServer = function (sid, stype) {
        var serverType = stype;
        var newServer = null;

        if (this.serverTypes[serverType] !== undefined) {
            newServer = this.serverTypes[serverType].createServer(sid);
        } else {
            throw new DError('ERR001', {
                stype: serverType
            });
        }

        return newServer;
    };

    /**
     * add server to servers hash
     * @method addServer
     * @param {module:core/base-server.BaseServer}
     * @returns {undefined}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.addServer = function (server) {
        var exist = false;
        var serverType = server.type;

        if (servers[serverType] === undefined) {
            servers[serverType] = [];
        }

        for (var i = 0; i < servers[serverType].length; i++) {
            if (servers[serverType][i].id === server.id) {
                exist = true;
            }
        }

        if (!exist) {
            servers[serverType].push(server);
        }
    };

    /**
     * remove server to servers hash
     * @method removeServer
     * @param {module:core/base-server.BaseServer}
     * @returns {undefined}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.removeServer = function (server) {
        var exist = false;
        var serverType = server.type;

        if (servers[serverType] === undefined) {
            servers[serverType] = [];
        }

        var newList = _.filter(servers[server.type], function (svr) {
            return svr.id !== server.id;
        });

        servers[server.type] = newList;
    };

    /**
     * get server from servers hash
     * @method getServer
     * @param {string} sid - server id
     * @returns {module:core/base-server.BaseServer}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.getServer = function (sid) {
        for (var type in servers) {
            for (var i = 0; i < servers[type].length; i++) {
                var server = servers[type][i];
                if (server.id === sid) {
                    return server;
                }
            }
        }
        return null;
    };

    /**
     * modify server from servers hash
     * @method getServer
     * @param {string} sid - server id
     * @param {string} serverData - server
     * @returns {module:core/base-server.BaseServer}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.modifyServer = function (sid, serverData) {
        for (var type in servers) {
            for (var i = 0; i < servers[type].length; i++) {
                var server = servers[type][i];
                if (server.id === sid) {
                    servers[type][i] = serverData;
                    return true;
                }
            }
        }
        return false;
    };

    /**
     * get server from servers hash using environment condition
     * @method getServersByEnvironment
     * @param {environment} environment - environment name
     * @returns {module:core/base-server.BaseServer}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.getServersByEnvironment = function (environmentId) {
        var i;
        var j;
        var results = [];
        var serverList = self.getAllServers();

        for (i = 0; i < serverList.length; i++) {
            var server = serverList[i];
            var envs = server.environments;

            if (envs) {
                for (j = 0; j < envs.length; j++) {
                    if (envs[j].id === environmentId) {
                        var s = server;
                        results.push(s);
                        break;
                    }
                }
            }
        }
        return results;
    };

    /**
     * get server from servers hash using server type
     * @method getServerByType
     * @param {string} stype - server Type
     * @returns {module:core/base-server.BaseServer}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.getServersByType = function (type) {
        var serverList = servers[type];
        var result = [];
        if (serverList !== undefined) {
            result = serverList;
        }
        return result;
    };

    /**
     * @method getAllServers
     * @returns {Array.<string>} list of server type
     * @memberOf module:core/dibs.CoreFramework
     */
    this.getAllServers = function () {
        var list = [];
        for (var type in servers) {
            for (var i = 0; i < servers[type].length; i++) {
                list.push(servers[type][i]);
            }
        }
        return list;
    };

    /**
     * @method getMasterServer
     * @returns {module:core/base-server.BaseServer} first matched running Master server
     * @memberOf module:core/dibs.CoreFramework
     */
    this.getMasterServer = function () {
        var master = self.getServersByType(MASTER_SERVER_TYPE);

        if (master !== null && master.length > 0) {
            return master[0];
        } else {
            return null;
        }
    };


    /**
     * @method existsServerType
     * @param {string} stype - server type
     * @returns {boolean}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.existsServerType = function (stype) {
        return (this.serverTypes[stype] !== undefined);
    };


    /**
     * @method initialize
     * @returns {undefined}
     * @memberOf module:core/dibs.CoreFramework
     */
    this.initialize = function () {
        // read version
        this.version = extfs.readJsonSync(path.join(__dirname, '..', 'package.json')).version;

        // skip if already initialized
        if (this.plugin !== undefined) {
            return;
        }

        // initialize plugin manager
        this.plugin = require('./plugin/plugin-manager.js');

        // load plugins
        var init = require('./plugin/init.js');
        init.loadPlugins();

        // Add base server
        self.BaseServer = require('./base-server');

        // Register server types
        exts = self.plugin.getExtensions('dibs.base.server');
        for (var i = 0; i < exts.length; i++) {
            var ext = exts[i];
            var stype = ext.serverType;
            this.serverTypes[stype] = ext.module;
        }

        // Register distribution types
        exts = self.plugin.getExtensions('dibs.repository.type');
        for (var j = 0; j < exts.length; j++) {
            var extType = exts[j];
            var type = extType.type;
            this.repositoryTypes[type] = extType;
        }

        // Register project types
        exts = self.plugin.getExtensions('dibs.base.projectType');
        for (var j = 0; j < exts.length; j++) {
            var extPtype = exts[j];
            var ptype = extPtype.projectType;
            this.projectTypes[ptype] = extPtype;
        }


        // apply DIBS base extensions
        self.rpc = require('./rpc');
        self.config = require('./config');
        self.log = require('./log.js');
    };
}


//
// HERE DEFINES BASIC EXCEPTION CONTROL OF SERVERS
//
process.setMaxListeners(0);

process.on('exit', function () {});
process.on('SIGINT', function () {
    console.log('\n');
    console.log('WARN: Shutting down from SIGINT (Crtl-C)');
    handleProcessExit();
});

process.on('uncaughtException', function (err) {
    if (err instanceof DError) {
        if (dibs.thisServer && dibs.thisServer.log) {
            dibs.thisServer.log.error(err.message);
        }
        console.error(err.message);
    } else {
        if (dibs.thisServer && dibs.thisServer.log) {
            dibs.thisServer.log.error(err.stack);
        }
        console.error(err.stack);
    }
    console.error('WARN: uncaughtException be generated');
});


function handleProcessExit() {
    if (dibs.thisServer &&
        (dibs.thisServer.status === 'INITIALIZED' ||
        dibs.thisServer.status === 'RUNNING')) {

        dibs.thisServer.terminate({}, function (err) {
            if (err !== null) {
                console.error(err);
                console.error(err.stack);
                process.exit(1);
            }
        });
    } else {
        process.exit(1);
    }
}
