var _ = require('lodash')
    , fs = require('fs')
    , novaform = require('./novaform')
    , novastl = require('./novastl')
    , NovaError = require('./nova-error')
    , path = require('path')
    , program = require('./program')
    , ProjectRef = require('./project-ref')
    , semver = require('semver')
    , util = require('util');

Project.searchPaths = [process.cwd()];

function Project(name) {
    if (!(this instanceof Project)) {
        return new Project(name);
    }

    this.name = name;

    for (var i = 0; i < Project.searchPaths.length; ++i) {
        var searchPath = Project.searchPaths[i];
        var filepath = path.join(searchPath, this.name);
        if (fs.existsSync(filepath) || fs.existsSync(filepath + '.js')) {
            this.filepath = filepath;
            break;
        }
    }

    if (!this.filepath) {
        throw new Error(`Could not find project ${name} in ${Project.searchPaths}`);
    }

    try {
        this.module = require(this.filepath);
    } catch (e) {
        // Someday a line number might be useful :( https://bugs.chromium.org/p/v8/issues/detail?id=2589
        throw new Error(`Failed to require project ${name} - ${e}`);
    }
}

Project.prototype.load = function(lib) {
    try {
        this.def = this.module({
            params: program.params,
            lib: lib,
            form: novaform,
            stl: novastl,
        });
    } catch (e) {
        throw new Error(`Failed to load project ${this.name}: ${e.message}`);
    }

    var requiredNovaVersion = this.def.novaVersion;
    var actualNovaVersion = program.getVersion();

    if (!requiredNovaVersion) {
        throw new NovaError(
'\
You seem to be using an old nova version that is incompatible with your definition file.\
\nEither downgrade nova to a crappier version or redo your definition file.\
'       );
    }

    if (!semver.satisfies(actualNovaVersion, requiredNovaVersion)) {
        throw new NovaError('Nova binary incompatible with nova project: '
            + this.name
            + `. Required version: ${requiredNovaVersion}, nova version: ${actualNovaVersion}`);
    }
};

Project.prototype.getComponents = function() {
    return _.map(this.def.components, 'name') || [];
};

Project.prototype.getArtifacts = function() {
    return _.map(this.def.artifacts, 'name') || [];
};

Project.makeFullRef = function(projectName, componentRef) {
    var ref = ProjectRef.parse(componentRef);
    return ref.subname ? componentRef : util.format('%s/%s', projectName, ref.name);
};

Project.prototype.getDependencies = function(comopnentName) {
    var componentDef = this.findComponent(comopnentName);
    var makeRefs = _.bind(Project.makeFullRef, Project, this.name);
    var componentDependencies = _.get(componentDef, 'dependencies.components', []);
    var artifactDependencies = _.get(componentDef, 'dependencies.artifacts', []);
    return {
        components: _.map(componentDependencies, makeRefs),
        artifacts: _.map(artifactDependencies, makeRefs),
    };
};

Project.prototype.findComponent = function(name) {
    return _.find(this.def.components, { name : name });
};

Project.prototype.findArtifact = function(name) {
    return _.find(this.def.artifacts, { name : name });
};

module.exports = Project;

@@ -2124,16 +2124,1196 @@
 );%0A    %7D
+%0A%0A    _.forEach(this.def.components, component =%3E %7B%0A        if (!component.name) %7B%0A            throw new NovaError('Found component without a name. WHY YOU NO NAME COMPONENT!!??');%0A        %7D%0A%0A        if (component.dependencies) %7B%0A            var dependencies = component.dependencies;%0A            if (!_.isObject(dependencies) %7C%7C _.isArray(dependencies)) %7B%0A                throw new NovaError(%60Expected '$%7Bcomponent.name%7D' dependencies to be an object.%60)%0A            %7D%0A%0A            if (dependencies.components && !_.isArray(dependencies.components)) %7B%0A                throw new NovaError(%60Expected '$%7Bcomponent.name%7D' dependencies components to be an array.%60)%0A            %7D%0A%0A            if (dependencies.artifacts && !_.isArray(dependencies.artifacts)) %7B%0A                throw new NovaError(%60Expected '$%7Bcomponent.name%7D' dependencies artifacts to be an array.%60)%0A            %7D%0A%0A            var extraKeys = _.difference(_.keys(dependencies), %5B'artifacts', 'components'%5D);%0A            if (extraKeys.length !== 0) %7B%0A                throw new NovaError(%60Found unexpected nonsense inside '$%7Bcomponent.name%7D' dependencies object: $%7BextraKeys.join(',')%7D%60);%0A            %7D%0A        %7D%0A    %7D);
 %0A%7D;%0A%0APro

Add some nova definition file checks