/**
 * An apeman project.
 * @constructor apeman.Project
 * @param {object} config - Project configuration.
 * @author Taka Okunishi
 *
 */
var path = require('path'),
    fs = require('fs');

function Project(config) {
    var s = this;
    if (!config) return;
    if (typeof config === 'string') {
        config = require(config);
    }
    s.validateConfig(config);

    var prototypeProject = Project.loadPrototypeProject(config.prototype);

    s.loadConfig(config, prototypeProject);

    s.prototypeProject = prototypeProject;

}

Project.loadPrototypeProject = function (projectName) {
    return projectName && require('./load_project')(projectName);
};

Project.prototype.validateConfig = function (config) {
    for (var key in config) {
        if (config.hasOwnProperty(key)) {
            var valid = ~Project.accessors.indexOf(key);
            if (!valid) {
                throw new Error('Invalid key: ' + key);
            }
        }
    }
};

function fallbackCopy(src, dest) {
    for (var key in src) {
        if (src.hasOwnProperty(key)) {
            if (!dest.hasOwnProperty(key)) {
                dest[key] = src[key];
            }
        }
    }
    return dest;
}

/**
 * Load project configuration.
 * @function apeman.Project.prototype.loadConfig
 * @param {object} config - Project configuration.
 * @param {object} prototypeProject - Prototype object.
 */
Project.prototype.loadConfig = function (config, prototypeProject) {
    var s = this;
    Object.keys(config).forEach(function (key) {
        Object.defineProperty(s, key, {
            enumerable: true,
            configurable: true,
            get: function () {
                var s = this,
                    cacheKey = '___cached___' + key;
                var cache = s[cacheKey];
                if (cache) {
                    return cache;
                }
                var value = config[key],
                    prototypeValue = prototypeProject && prototypeProject[key];
                if (prototypeValue) {
                    if (typeof(prototypeValue) === 'object') {
                        value = fallbackCopy(prototypeValue, value || {});
                    }
                }
                s[cacheKey] = value;
                return value;
            }
        })
    });
};

/**
 * Look up a file in each project directory.
 * @function apeman.Project.prototype.lookup
 * @param {string} filename - File name to lookup.
 * @returns {string} - File name.
 */
Project.prototype.lookup = function (filename) {
    var s = this,
        local = s.resolve(filename);
    var found = local && Project._exists(local);

    if (found) {
        return local;
    }
    var prototypeProject = s.prototypeProject;
    return prototypeProject && prototypeProject.lookup(filename);
};

/**
 * Look up a file in each project directory and require it.
 * @function apeman.Project.prototype.lookupRequire
 * @param {string} filename - File name to require.
 * @returns {string} - File name.
 */
Project.prototype.lookupRequire = function (filename) {
    var s = this, resolved = null;
    try {
        resolved = require.resolve(s.lookup(filename));
    } catch (e) {
        var prototypeProject = s.prototypeProject;
        return prototypeProject && prototypeProject.lookupRequire(filename);
    }
    return resolved && require(resolved) || null;
};

/**
 * Resolve a file in each project directory.
 * @function apeman.Project.prototype.resolve
 * @param {string} filename - File name to lookup.
 * @returns {string} - File name.
 */
Project.prototype.resolve = function (filename) {
    var s = this;
    return path.resolve(s.basedir, filename);
};


Project._exists = function (filename) {
    try {
        return !!require.resolve(filename);
    } catch (e) {
        return false;
    }
};

Project.accessors = [
    'basedir',
    'meta',
    'tasks',
    'prototype'
];

module.exports = Project;