
var path = require('path'),
    _    = require('lodash'),
    Promise = require('bluebird'),
    AppProxy = require('./proxy'),
    config = require('../config'),
    AppSandbox = require('./sandbox'),
    AppDependencies = require('./dependencies'),
    AppPermissions = require('./permissions'),
    loader;

// Get the full path to an app by name
function getAppAbsolutePath(name) {
    return path.join(config.paths.appPath, name);
}

// Get a relative path to the given apps root, defaults
// to be relative to __dirname
function getAppRelativePath(name, relativeTo) {
    relativeTo = relativeTo || __dirname;

    return path.relative(relativeTo, getAppAbsolutePath(name));
}

// Load apps through a psuedo sandbox
function loadApp(appPath) {
    var sandbox = new AppSandbox();

    return sandbox.loadApp(appPath);
}

function getAppByName(name, permissions) {
    // Grab the app class to instantiate
    var AppClass = loadApp(getAppRelativePath(name)),
        appProxy = new AppProxy({
            name: name,
            permissions: permissions
        }),
        app;

    // Check for an actual class, otherwise just use whatever was returned
    if (_.isFunction(AppClass)) {
        app = new AppClass(appProxy);
    } else {
        app = AppClass;
    }

    return {
        app: app,
        proxy: appProxy
    };
}

// The loader is responsible for loading apps
loader = {
    // Load a app and return the instantiated app
    installAppByName: function (name) {
        // Install the apps dependendencies first
        var appPath = getAppAbsolutePath(name),
            deps = new AppDependencies(appPath);

        return deps.install()
            .then(function () {
                // Load app permissions
                var perms = new AppPermissions(appPath);

                return perms.read().catch(function (err) {
                    // Provide a helpful error about which app
                    return Promise.reject(new Error('Error loading app named ' + name + '; problem reading permissions: ' + err.message));
                });
            })
            .then(function (appPerms) {
                var appInfo = getAppByName(name, appPerms),
                    app = appInfo.app,
                    appProxy = appInfo.proxy;

                // Check for an install() method on the app.
                if (!_.isFunction(app.install)) {
                    return Promise.reject(new Error('Error loading app named ' + name + '; no install() method defined.'));
                }

                // Run the app.install() method
                // Wrapping the install() with a when because it's possible
                // to not return a promise from it.
                return Promise.resolve(app.install(appProxy)).return(app);
            });
    },

    // Activate a app and return it
    activateAppByName: function (name) {
        var perms = new AppPermissions(getAppAbsolutePath(name));

        return perms.read().then(function (appPerms) {
            var appInfo = getAppByName(name, appPerms),
                app = appInfo.app,
                appProxy = appInfo.proxy;

            // Check for an activate() method on the app.
            if (!_.isFunction(app.activate)) {
                return Promise.reject(new Error('Error loading app named ' + name + '; no activate() method defined.'));
            }

            // Wrapping the activate() with a when because it's possible
            // to not return a promise from it.
            return Promise.resolve(app.activate(appProxy)).return(app);
        });
    }
};

module.exports = loader;
