/**
 * Create a index.js for the given directory.
 * Exports files in directory with camelized name.
 * @example
 *
 *     //This is an index file generate by ci worker.
 *     exports.fooBar = require('./foo_bar.js');
 *
 * @function ci.worker.generateIndex
 * @param {object} config - Configuration object.
 * @param {string} config.dir - Directory to create index.
 * @param {boolean}[config.capitalize=false] - Exports with capitalize name or not.
 * @param {string} [config.pattern='*'] - Pattern to collect modules within the directory.
 * @param {string} [config.dest=config.dir+'/index.js'] - Index file path to generate.
 * @param {function} callback - Callback when done.
 * @author Taka Okunishi
 *
 */

var fs = require('fs'),
    path = require('path'),
    string = require('../../lib/string'),
    file = require('../../lib/file'),
    object = require('../../lib/object'),
    camelize = string.camelize,
    debug = require('./_debug'),
    matchesGlob = file.matchesGlob,
    writeReadonlyFile = file.writeReadonlyFile,
    fallbackCopy = object.fallbackCopy,
    copy = object.copy,
    apeman = require('apeman'),
    _loadLocalTmpl = require('./_load_local_tmpl');


exports = module.exports = function (config, callback) {

    var dirname = config.dir,
        pattern = config.pattern || '*',
        capitalize = config.capitalize || false,
        dest = path.resolve(config.dest || path.join(dirname, 'index.js'));

    exports._modulesData(dirname, pattern, capitalize, function (err, modulesData) {
        if (err) {
            callback(err);
            return;
        }
        var exportsNames = modulesData && modulesData.map(function (data) {
            return data.exportsName;
        }) || [];
        exports._prototypeData(dirname, exportsNames, function (err, prototypeData) {
            if (err) {
                callback(err);
                return;
            }
            var docData = exports._docData(config.doc);
            exports._loadIndexJsTmpl(function (err, tmpl) {
                if (err) {
                    callback(err);
                    return;
                }
                var modules = modulesData && modulesData.map(function (data, i) {
                    data.hasNext = i < modulesData.length - 1;
                    return data;
                });
                var data = exports._renderData({
                        prototype: prototypeData,
                        doc: docData,
                        modules: modules,
                        hasModules: !!modules && modules.length
                    }),
                    content = tmpl(data);
                writeReadonlyFile(dest, content, function (err) {
                    if (!err) {
                        debug.didCreateFile(dest);
                    }
                    callback();
                });
            });
        });
    });
};

exports._renderData = function (data) {
    var result = {};
    result.meta = apeman.currentProject.meta;
    copy(data, result);
    return result;
};

exports._docData = function (data) {
    if (!data) return null;
    return {
        overview: data.overview,
        namespace: data.namespace
    }
};

/**
 * Get module data.
 * @param {string} dirname - Name of directory which contains all modules.
 * @param {string} pattern - Model file name pattern.
 * @param {string[]} capitalize - Module names to be exported with capitalized names.
 * @param {function} callback - Callback when done.
 * @private
 */
exports._modulesData = function (dirname, pattern, capitalize, callback) {
    /**
     * Should be capitalized or not
     * @param name - Name to judge.
     * @returns {boolean} Capitalized or not
     * @private
     */
    function shouldCapitalize(name) {
        if (typeof(capitalize) === 'string') {
            capitalize = [capitalize];
        }
        if (Array.isArray(capitalize)) {
            if (~capitalize.indexOf(name)) {
                return true;
            } else {
                for (var i = 0; i < capitalize.length; i++) {
                    var hit = matchesGlob(name, capitalize[i]);
                    if (hit) return true;
                }
            }
            return false;
        } else {
            return !!capitalize;
        }
    }

    fs.exists(dirname, function (exists) {
        if (!exists) {
            callback(null);
            return;
        }

        fs.readdir(dirname, function (err, filenames) {
            if (err) {
                callback(err);
                return;
            }
            var data = filenames
                .filter(function (filename) {
                    return matchesGlob(filename, pattern);
                })
                .filter(function (filename) {
                    return filename != 'index.js';
                })
                .filter(function (filename) {
                    return !filename.match(/^\./);
                })
                .filter(function (filename) {
                    try {
                        require.resolve(path.resolve(dirname, filename));
                        return true;
                    } catch (e) {
                        return false;
                    }
                })
                .map(function (filename) {
                    var name = path.basename(filename, path.extname(filename));
                    return {
                        exportsName: camelize(name.replace(/\-/g, '_'), shouldCapitalize(name)),
                        requireName: './' + name
                    };
                });

            callback(null, data);
        });
    });
};

/**
 * Get the prototype module data.
 * @param {string} dirname - Directory name to look up.
 * @param {string[]|boolean} overridden - Overridden or not.
 * @param {function} callback - Callback when done.
 * @protected
 * @ignore
 */
exports._prototypeData = function (dirname, overridden, callback) {
    var currentProject = apeman.currentProject,
        prototypeProject = currentProject.prototypeProject;
    if (!prototypeProject) {
        callback(null);
        return;
    }
    var lookupPath = path.relative(currentProject.basedir, path.resolve(dirname)),
        prototypePath = prototypeProject.resolve(lookupPath);
    try {
        var prototype = require(prototypePath);
    } catch (e) {
        callback(null);
        return;
    }
    var data = prototype && {
        lookupPath: lookupPath,
        modules: Object.keys(prototype)
            .filter(function (name) {
                if (!overridden) return true;
                return overridden.indexOf(name) === -1;
            })
            .map(function (name) {
                return {exportsName: name}
            }).map(function (data, i) {
                data.hasNext = i < Object.keys(prototype)
                    .filter(function (name) {
                        if (!overridden) return true;
                        return overridden.indexOf(name) === -1;
                    })
                    .map(function (name) {
                        return {exportsName: name}
                    }).length - 1;
                return data;
            })
    };
    callback(null, data);
};

/**
 * Load the index js template.
 * @param {function} callback - Callback when done.
 * @protected
 * @ignore
 */
exports._loadIndexJsTmpl = function (callback) {
    _loadLocalTmpl('js/index.js.hbs', callback);
};
