var VNode = require('./VNode');
var VComment = require('./VComment');
var VDocumentFragment = require('./VDocumentFragment');
var VElement = require('./VElement');
var VText = require('./VText');
var defaultDocument = typeof document != 'undefined' && document;

var specialHtmlRegexp = /[&<]/;
var range;

function virtualizeChildNodes(node, vdomParent) {
    var curChild = node.firstChild;
    while(curChild) {
        vdomParent.$__appendChild(virtualize(curChild));
        curChild = curChild.nextSibling;
    }
}

function virtualize(node) {
    switch(node.nodeType) {
        case 1:
            var attributes = node.attributes;
            var attrCount = attributes.length;

            var attrs;

            if (attrCount) {
                attrs = {};

                for (var i=0; i<attrCount; i++) {
                    var attr = attributes[i];
                    var attrName;

                    if (attr.namespaceURI === 'http://www.w3.org/1999/xlink' && attr.localName === 'href') {
                        attrName = 'xlink:href';
                    } else {
                        attrName = attr.name;
                    }

                    attrs[attrName] = attr.value;
                }
            }

            var vdomEL = new VElement(node.nodeName, attrs);

            if (vdomEL.$__isTextArea) {
                vdomEL.$__value = node.value;
            } else {
                virtualizeChildNodes(node, vdomEL);
            }

            return vdomEL;
        case 3:
            return new VText(node.nodeValue);
        case 8:
            return new VComment(node.nodeValue);
        case 11:
            var vdomDocFragment = new VDocumentFragment();
            virtualizeChildNodes(node, vdomDocFragment);
            return vdomDocFragment;
    }
}

function virtualizeHTML(html, doc) {
    if (!specialHtmlRegexp.test(html)) {
        return new VText(html);
    }

    if (!range && doc.createRange) {
        range = doc.createRange();
        range.selectNode(doc.body);
    }

    var vdomFragment;

    var fragment;
    if (range && range.createContextualFragment) {
        fragment = range.createContextualFragment(html);
        vdomFragment = virtualize(fragment);
    } else {
        var container = doc.createElement('body');
        container.innerHTML = html;
        vdomFragment = new VDocumentFragment();

        var curChild = container.firstChild;
        while(curChild) {
            vdomFragment.$__appendChild(virtualize(curChild));
            curChild = curChild.nextSibling;
        }
    }

    return vdomFragment;
}

var Node_prototype = VNode.prototype;

/**
 * Shorthand method for creating and appending a Text node with a given value
 * @param  {String} value The text value for the new Text node
 */
Node_prototype.t = function(value) {
    var type = typeof value;
    var vdomNode;

    if (type !== 'string') {
        if (value == null) {
            value = '';
        } else if (type === 'object') {
            if (value.toHTML) {
                vdomNode = virtualizeHTML(value.toHTML(), document);
            }
        }
    }

    this.$__appendChild(vdomNode || new VText(value.toString()));
    return this.$__finishChild();
};

/**
 * Shorthand method for creating and appending a Comment node with a given value
 * @param  {String} value The value for the new Comment node
 */
Node_prototype.c = function(value) {
    this.$__appendChild(new VComment(value));
    return this.$__finishChild();
};

Node_prototype.$__appendDocumentFragment = function() {
    return this.$__appendChild(new VDocumentFragment());
};

exports.$__VComment = VComment;
exports.$__VDocumentFragment = VDocumentFragment;
exports.$__VElement = VElement;
exports.$__VText = VText;
exports.$__virtualize = virtualize;
exports.$__virtualizeHTML = virtualizeHTML;
exports.$__defaultDocument = defaultDocument;
