/**
 * @fileoverview Responsible for loading ignore config files and managing ignore patterns
 * @author Jonathan Rajavuori
 */
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var fs = require("fs"),
    debug = require("debug"),
    minimatch = require("minimatch"),
    FileFinder = require("./file-finder");

debug = debug("eslint:ignored-paths");


//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

var ESLINT_IGNORE_FILENAME = ".eslintignore";


//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Load and parse ignore patterns from the file at the given path
 * @param {string} filepath Path to the ignore file.
 * @returns {string[]} An array of ignore patterns or an empty array if no ignore file.
 */
function loadIgnoreFile(filepath) {
    var ignorePatterns = [];

    function nonEmpty(line) {
        return line.trim() !== "" && line[0] !== "#";
    }

    if (filepath) {
        try {
            ignorePatterns = fs.readFileSync(filepath, "utf8").split(/\r?\n/).filter(nonEmpty);
        } catch (e) {
            e.message = "Cannot read ignore file: " + filepath + "\nError: " + e.message;
            throw e;
        }
    }

    return ["node_modules/**"].concat(ignorePatterns);
}

var ignoreFileFinder;

/**
 * Find an ignore file in the current directory or a parent directory.
 * @returns {string} Path of ignore file or an empty string.
 */
function findIgnoreFile() {
    if (!ignoreFileFinder) {
        ignoreFileFinder = new FileFinder(ESLINT_IGNORE_FILENAME);
    }

    return ignoreFileFinder.findInDirectoryOrParents();
}


//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

/**
 * IgnoredPaths
 * @constructor
 * @class IgnoredPaths
 * @param {Array} patterns to be matched against file paths
 */
function IgnoredPaths(patterns) {
    this.patterns = patterns;
}

/**
 * IgnoredPaths initializer
 * @param {Object} options object containing 'ignore' and 'ignorePath' properties
 * @returns {IgnoredPaths} object, with patterns loaded from the ignore file
 */
IgnoredPaths.load = function (options) {
    var patterns;

    options = options || {};

    if (options.ignore) {
        patterns = loadIgnoreFile(options.ignorePath || findIgnoreFile());
    } else {
        patterns = [];
    }

    if (options.ignorePattern) {
        patterns.push(options.ignorePattern);
    }

    return new IgnoredPaths(patterns);
};

/**
 * Determine whether a file path is included in the configured ignore patterns
 * @param {string} filepath Path to check
 * @returns {boolean} true if the file path matches one or more patterns, false otherwise
 */
IgnoredPaths.prototype.contains = function (filepath) {
    if (this.patterns === null) {
        throw new Error("No ignore patterns loaded, call 'load' first");
    }

    filepath = filepath.replace("\\", "/");
    filepath = filepath.replace(/^\.\//, "");
    return this.patterns.reduce(function(ignored, pattern) {
        var negated = pattern[0] === "!",
            matches;

        if (negated) {
            pattern = pattern.slice(1);
        }

        // Remove leading "current folder" prefix
        if (pattern.indexOf("./") === 0) {
            pattern = pattern.slice(2);
        }

        matches = minimatch(filepath, pattern) || minimatch(filepath, pattern + "/**");

        return matches ? !negated : ignored;
    }, false);
};

module.exports = IgnoredPaths;
