/**
 * XadillaX created at 2016-05-12 16:10:11 With ♥
 *
 * Copyright (c) 2016 Souche.com, all rights
 * reserved.
 */
"use strict";

const fs = require("fs");
const path = require("path");

const _ = require("lodash");
const async = require("async");

class Plugin {
    constructor(akyuu) {
        this.akyuu = akyuu;
        this.basePluginConfig = akyuu.config.basePlugins || {};
        this.pluginConfig = akyuu.config.plugins || {};

        this.plugins = [];
        this.logger = akyuu.logger.get("plugin-loader");
    }

    loadSingle(key, config) {
        let plug;
        let name;
        // require from user's dir
        const userPluginsPath = `${this.akyuu.projectRoot}/plugins/${_.snakeCase(key)}`;
        if(fs.existsSync(`${userPluginsPath}.js`) || fs.existsSync(path.join(userPluginsPath, "index.js"))) {
            plug = require(userPluginsPath).create(this.akyuu, config);
            name = _.kebabCase(key);
        } else {
            try {
                // require from node_modules
                try {
                    name = `akyuu-${_.kebabCase(key)}`;
                    plug = require(name).create(this.akyuu, config);
                } catch(error) {
                    name = `@akyuu/${_.kebabCase(key)}`;
                    plug = require(name).create(this.akyuu, config);
                }
            } catch(error) {
                // see if it's built-in
                const builtInPath = `../built_in_plugin/${_.snakeCase(key)}`;
                if(fs.existsSync(path.join(__dirname, `${builtInPath}.js`))) {
                    plug = require(builtInPath).create(this.akyuu, config);
                    name = _.kebabCase(key);
                } else {
                    throw error;
                }
            }
        }
        // push plugins
        plug.name = plug.name || name;
        this.plugins.push(plug);

        this.logger.info(`Plugin \`${plug.name}\` loaded.`);
    }

    load() {
        let config = this.basePluginConfig;
        for(const key in config) {
            if(!config.hasOwnProperty(key)) continue;
            if(!config[key].enabled) continue;
            this.loadSingle(key, config[key]);
        }

        config = this.pluginConfig;
        for(const key in config) {
            if(!config.hasOwnProperty(key)) continue;
            if(!config[key].enabled) continue;
            this.loadSingle(key, config[key]);
        }
    }

    plug(pos, callback) {
        callback = callback || function() {};
        pos = pos || 0;

        const self = this;
        async.eachSeries(this.plugins, function(plugin, callback) {
            if(plugin.position !== pos) {
                return process.nextTick(callback);
            }

            const argsLength = plugin.plug.length;
            if(argsLength > 0) {
                plugin.plug(function(err) {
                    if(err) return callback(err);
                    self.logger.info(`${Plugin.INVERTED_POS[pos]} plugin \`${plugin.name}\` plugged.`);
                    callback();
                });
            } else {
                plugin.plug();
                self.logger.info(`${Plugin.INVERTED_POS[pos]} plugin \`${plugin.name}\` plugged.`);
                process.nextTick(callback);
            }
        }, function(err) {
            callback(err);
        });
    }
}

Plugin.POS = {
    BEFORE_SERVICE: 0,
    BEFORE_CONNECTION: 1,
    BEFORE_MODEL: 2,
    BEFORE_CONTROLLER: 3,

    AFTER_SERVICE: 100,
    AFTER_CONNECTION: 101,
    AFTER_MODEL: 102,
    AFTER_CONTROLLER: 103
};

Plugin.INVERTED_POS = _.invert(Plugin.POS);

module.exports = Plugin;

@@ -588,24 +588,25 @@
   let name;%0A
+%0A
         // r
@@ -1779,32 +1779,33 @@
     %7D%0A        %7D%0A
+%0A
         // push 
@@ -2002,32 +2002,61 @@
 sePluginConfig;%0A
+        let configList = %5B%5D;%0A
         for(cons
@@ -2191,40 +2191,101 @@
     
-this.loadSingle(key, config%5Bkey%5D
+configList.push(%7B%0A                key: key,%0A                config: config%5Bkey%5D%0A            %7D
 );%0A 
@@ -2458,32 +2458,627 @@
 bled) continue;%0A
+            configList.push(%7B%0A                key: key,%0A                config: config%5Bkey%5D%0A            %7D);%0A        %7D%0A%0A        // sort by weight & key%0A%0A        configList = configList.sort(function(a, b) %7B%0A            a.config.weight = a.config.weight %7C%7C 0;%0A            b.config.weight = b.config.weight %7C%7C 0;%0A            if(a.config.weight !== b.config.weight) %7B%0A                return b.config.weight - a.config.weight;%0A            %7D%0A            if(a.key %3E b.key) return 1;%0A            if(a.key %3C b.key) return -1;%0A            return 0;%0A        %7D);%0A%0A        for(const _config of configList) %7B%0A
             this
@@ -3093,24 +3093,35 @@
 gle(
+_config.
 key, 
+_
 config
-%5Bkey%5D
+.config
 );%0A 

add plugins load by weight