var _ = require('lodash');
var Q = require('q');
var semver = require('semver');
var path = require('path');
var url = require('url');
var fs = require('./utils/fs');
var resolve = require('resolve');

var pkg = require('../package.json');

var Plugin = function(book, name) {
    this.book = book;
    this.name = name;
    this.packageInfos = {};
    this.infos = {};

    // Bind methods
    _.bindAll(this);

    _.each([
        'gitbook-plugin-'+name,
        'gitbook-'+name,
        name,
    ], function(_name) {
        // Load from the book
        if (this.load(_name, book.root)) return false;

        // Load from default plugins
        if (this.load(_name, __dirname)) return false;
    }, this);
};

// Type of plugins resources
Plugin.RESOURCES = ['js', 'css'];
Plugin.HOOKS = [
    'init', 'finish', 'finish:before', 'config', 'page', 'page:before'
];

// Load from a name
Plugin.prototype.load = function(name, baseDir) {
    try {
        var res = resolve.sync(name+'/package.json', { basedir: baseDir });

        this.baseDir = path.dirname(res);
        this.packageInfos = require(res);
        this.infos = require(resolve.sync(name, { basedir: baseDir }));
        this.name = name;

        return true;
    } catch (e) {
        this.packageInfos = {};
        this.infos = {};
        return false;
    }
};

Plugin.prototype.normalizeResource = function(resource) {
    // Parse the resource path
    var parsed = url.parse(resource);

    // This is a remote resource
    // so we will simply link to using it's URL
    if (parsed.protocol) {
        return {
            'url': resource
        };
    }

    // This will be copied over from disk
    // and shipped with the book's build
    return { 'path': this.name+'/'+resource };
};

// Return resources
Plugin.prototype._getResources = function(base) {
    base = base;
    var book = this.infos[base];

    // Compatibility with version 1.x.x
    if (base == 'website') book = book || this.infos.book;

    // Nothing specified, fallback to default
    if (!book) {
        return Q({});
    }

    // Dynamic function
    if(typeof book === 'function') {
        // Call giving it the context of our book
        return Q().then(book.bind(this.book));
    }

    // Plain data object
    return Q(_.cloneDeep(book));
};

// Normalize resources and return them
Plugin.prototype.getResources = function(base) {
    var that = this;

    return this._getResources(base)
    .then(function(resources) {

        _.each(Plugin.RESOURCES, function(resourceType) {
            resources[resourceType] = (resources[resourceType] || []).map(that.normalizeResource);
        });

        return resources;
    });
};

// Normalize filters and return them
Plugin.prototype.getFilters = function() {
    return this.infos.filters || {};
};

// Normalize blocks and return them
Plugin.prototype.getBlocks = function() {
    return this.infos.blocks || {};
};

// Test if it's a valid plugin
Plugin.prototype.isValid = function() {
    var that = this;
    var isValid = (
        this.packageInfos &&
        this.packageInfos.name &&
        this.packageInfos.engines &&
        this.packageInfos.engines.gitbook &&
        semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
    );

    // Valid hooks
    _.each(this.infos.hooks, function(hook, hookName) {
        if (_.contains(Plugin.HOOKS, hookName)) return;
        that.book.log.warn.ln('Hook ''+hookName+' 'used by plugin ''+that.packageInfos.name+'' has been removed or is deprecated');
    });

    return isValid;
};

// Resolve file path
Plugin.prototype.resolveFile = function(filename) {
    return path.resolve(this.baseDir, filename);
};

// Resolve file path
Plugin.prototype.callHook = function(name, data) {
    // Our book will be the context to apply
    var context = this.book;

    var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
    data = data || {};

    if (!hookFunc) return Q(data);

    this.book.log.debug.ln('call hook', name);
    if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln('hook ''+name+'' used by plugin ''+this.name+'' is deprecated, and will be removed in the coming versions');

    return Q()
    .then(function() {
        return hookFunc.apply(context, [data]);
    });
};

// Copy plugin assets fodler
Plugin.prototype.copyAssets = function(out, base) {
    var that = this;

    return this.getResources(base)
    .get('assets')
    .then(function(assets) {
        // Assets are undefined
        if(!assets) return false;

        return fs.copy(
            that.resolveFile(assets),
            out
        ).then(_.constant(true));
    }, _.constant(false));
};

module.exports = Plugin;

@@ -3438,17 +3438,17 @@
 n('Hook 
-'
+%22
 '+hookNa
@@ -3451,18 +3451,19 @@
 okName+'
+%22%22
  
-'
 used by 
@@ -3465,25 +3465,25 @@
 d by plugin 
-'
+%22
 '+that.packa
@@ -3496,17 +3496,17 @@
 s.name+'
-'
+%22
  has bee
@@ -4089,17 +4089,17 @@
 ook 
-'
+%22
 '+name+'
 ' us
@@ -4094,17 +4094,17 @@
 '+name+'
-'
+%22
  used by
@@ -4111,17 +4111,17 @@
  plugin 
-'
+%22
 '+this.n
@@ -4125,17 +4125,17 @@
 s.name+'
-'
+%22
  is depr

Fix js error