var ElementType = require(“domelementtype”);

var re_whitespace = /s+/g; var NodePrototype = require(“./lib/node”); var ElementPrototype = require(“./lib/element”);

function DomHandler(callback, options, elementCB){

if(typeof callback === "object"){
        elementCB = options;
        options = callback;
        callback = null;
} else if(typeof options === "function"){
        elementCB = options;
        options = defaultOpts;
}
this._callback = callback;
this._options = options || defaultOpts;
this._elementCB = elementCB;
this.dom = [];
this._done = false;
this._tagStack = [];
this._parser = this._parser || null;

}

//default options var defaultOpts = {

normalizeWhitespace: false, //Replace all whitespace with single spaces
withStartIndices: false, //Add startIndex properties to nodes

};

DomHandler.prototype.onparserinit = function(parser){

this._parser = parser;

};

//Resets the handler back to starting state DomHandler.prototype.onreset = function(){

DomHandler.call(this, this._callback, this._options, this._elementCB);

};

//Signals the handler that parsing is done DomHandler.prototype.onend = function(){

if(this._done) return;
this._done = true;
this._parser = null;
this._handleCallback(null);

};

DomHandler.prototype._handleCallback = DomHandler.prototype.onerror = function(error){

if(typeof this._callback === "function"){
        this._callback(error, this.dom);
} else {
        if(error) throw error;
}

};

DomHandler.prototype.onclosetag = function(){

//if(this._tagStack.pop().name !== name) this._handleCallback(Error("Tagname didn't match!"));
var elem = this._tagStack.pop();
if(this._elementCB) this._elementCB(elem);

};

DomHandler.prototype._addDomElement = function(element){

var parent = this._tagStack[this._tagStack.length - 1];
var siblings = parent ? parent.children : this.dom;
var previousSibling = siblings[siblings.length - 1];

element.next = null;

if(this._options.withStartIndices){
        element.startIndex = this._parser.startIndex;
}

if (this._options.withDomLvl1) {
        element.__proto__ = element.type === "tag" ? ElementPrototype : NodePrototype;
}

if(previousSibling){
        element.prev = previousSibling;
        previousSibling.next = element;
} else {
        element.prev = null;
}

siblings.push(element);
element.parent = parent || null;

};

DomHandler.prototype.onopentag = function(name, attribs){

var element = {
        type: name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag,
        name: name,
        attribs: attribs,
        children: []
};

this._addDomElement(element);

this._tagStack.push(element);

};

DomHandler.prototype.ontext = function(data){

//the ignoreWhitespace is officially dropped, but for now,
//it's an alias for normalizeWhitespace
var normalize = this._options.normalizeWhitespace || this._options.ignoreWhitespace;

var lastTag;

if(!this._tagStack.length && this.dom.length && (lastTag = this.dom[this.dom.length-1]).type === ElementType.Text){
        if(normalize){
                lastTag.data = (lastTag.data + data).replace(re_whitespace, " ");
        } else {
                lastTag.data += data;
        }
} else {
        if(
                this._tagStack.length &&
                (lastTag = this._tagStack[this._tagStack.length - 1]) &&
                (lastTag = lastTag.children[lastTag.children.length - 1]) &&
                lastTag.type === ElementType.Text
        ){
                if(normalize){
                        lastTag.data = (lastTag.data + data).replace(re_whitespace, " ");
                } else {
                        lastTag.data += data;
                }
        } else {
                if(normalize){
                        data = data.replace(re_whitespace, " ");
                }

                this._addDomElement({
                        data: data,
                        type: ElementType.Text
                });
        }
}

};

DomHandler.prototype.oncomment = function(data){

var lastTag = this._tagStack[this._tagStack.length - 1];

if(lastTag && lastTag.type === ElementType.Comment){
        lastTag.data += data;
        return;
}

var element = {
        data: data,
        type: ElementType.Comment
};

this._addDomElement(element);
this._tagStack.push(element);

};

DomHandler.prototype.oncdatastart = function(){

var element = {
        children: [{
                data: "",
                type: ElementType.Text
        }],
        type: ElementType.CDATA
};

this._addDomElement(element);
this._tagStack.push(element);

};

DomHandler.prototype.oncommentend = DomHandler.prototype.oncdataend = function(){

this._tagStack.pop();

};

DomHandler.prototype.onprocessinginstruction = function(name, data){

this._addDomElement({
        name: name,
        data: data,
        type: ElementType.Directive
});

};

module.exports = DomHandler;