/**
 * Module dependencies
 */

var select = require('CSSselect'),
    parse = require('./parse'),
    render = require('./render'),
    _ = require('lodash');

/**
 * $.load(str)
 */

exports.load = function (content, options, isFragment) {
    var Cheerio = require('./cheerio');

    options = _.defaults(options || {}, Cheerio.prototype.options);

    var root = parse(content, options, !isFragment);

    var initialize = function (selector, context, r, opts) {
        opts = _.defaults(opts || {}, options);
        return Cheerio.call(this, selector, context, r || root, opts);
    };

    // Ensure that selections created by the "loaded" `initialize` function are
    // true Cheerio instances.
    initialize.prototype = Cheerio.prototype;

    // Add in the static methods
    _.merge(initialize, exports);

    // Add in the root
    initialize._root = root;
    // store options
    initialize._options = options;

    return initialize;
};

/**
 * $.html([selector | dom])
 */

exports.html = function (dom, options) {
    var Cheerio = require('./cheerio');

    // be flexible about parameters, sometimes we call html(),
    // with options as only parameter
    // check dom argument for dom element specific properties
    // assume there is no 'length' or 'type' properties in the options object
    if (Object.prototype.toString.call(dom) === '[object Object]' && !options && !('length' in dom) &&
        !('type' in dom)) {
        options = dom;
        dom = undefined;
    }

    // sometimes $.html() used without preloading html
    // so fallback non existing options to the default ones
    options = _.defaults(options || {}, this._options, Cheerio.prototype.options);

    if (dom) {
        dom = (typeof dom === 'string') ? select(dom, this._root, options) : dom;
        return render(dom, options);
    } else if (this._root && this._root.children) {
        return render(this._root.children, options);
    } else {
        return '';
    }
};

/**
 * $.xml([selector | dom])
 */

exports.xml = function (dom) {
    if (dom) {
        dom = (typeof dom === 'string') ? select(dom, this._root, this.options) : dom;
        return render(dom, { xmlMode: true });
    } else if (this._root && this._root.children) {
        return render(this._root.children, { xmlMode: true });
    } else {
        return '';
    }
};

/**
 * $.text(dom)
 */

exports.text = function (elems) {
    if (!elems) return '';

    var ret = '',
        len = elems.length,
        elem;

    for (var i = 0; i < len; i++) {
        elem = elems[i];
        if (elem.type === 'text') ret += elem.data;
        else if (elem.children && elem.type !== 'comment') {
            ret += exports.text(elem.children);
        }
    }

    return ret;
};

/**
 * $.parseHTML(data [, context ] [, keepScripts ])
 * Parses a string into an array of DOM nodes. The `context` argument has no
 * meaning for Cheerio, but it is maintained for API compatibility with jQuery.
 */
exports.parseHTML = function (data, context, keepScripts) {
    var parsed;

    if (!data || typeof data !== 'string') {
        return null;
    }

    if (typeof context === 'boolean') {
        keepScripts = context;
    }

    parsed = this.load(data, null, true);
    if (!keepScripts) {
        parsed('script').remove();
    }

    return parsed.root()[0].children;
};

/**
 * $.root()
 */
exports.root = function () {
    return this(this._root);
};

/**
 * $.contains()
 */
exports.contains = function (container, contained) {

    // According to the jQuery API, an element does not "contain" itself
    if (contained === container) {
        return false;
    }

    // Step up the descendants, stopping when the root element is reached
    // (signaled by `.parent` returning a reference to the same object)
    while (contained && contained !== contained.parent) {
        contained = contained.parent;
        if (contained === container) {
            return true;
        }
    }

    return false;
};
