'use strict';



var DefaultTreeAdapter = require('../tree_adapters/default'),

    Doctype = require('../common/doctype'),

    Utils = require('../common/utils'),

    HTML = require('../common/html');



//Aliases

var $ = HTML.TAG_NAMES,

    NS = HTML.NAMESPACES;



//Default serializer options

var DEFAULT_OPTIONS = {

    encodeHtmlEntities: true

};



//Escaping regexes

var AMP_REGEX = /&/g,

    NBSP_REGEX = /\u00a0/g,

    DOUBLE_QUOTE_REGEX = /"/g,

    LT_REGEX = /</g,

    GT_REGEX = />/g;



//Escape string

function escapeString(str, attrMode) {

    str = str

        .replace(AMP_REGEX, '&amp;')

        .replace(NBSP_REGEX, '&nbsp;');



    if (attrMode)

        str = str.replace(DOUBLE_QUOTE_REGEX, '&quot;');



    else {

        str = str

            .replace(LT_REGEX, '&lt;')

            .replace(GT_REGEX, '&gt;');

    }



    return str;

}





//Enquote doctype ID







//Serializer

var Serializer = module.exports = function (treeAdapter, options) {

    this.treeAdapter = treeAdapter || DefaultTreeAdapter;

    this.options = Utils.mergeOptions(DEFAULT_OPTIONS, options);

};





//API

Serializer.prototype.serialize = function (node) {

    this.html = '';

    this._serializeChildNodes(node);



    return this.html;

};





//Internals

Serializer.prototype._serializeChildNodes = function (parentNode) {

    var childNodes = this.treeAdapter.getChildNodes(parentNode);



    if (childNodes) {

        for (var i = 0, cnLength = childNodes.length; i < cnLength; i++) {

            var currentNode = childNodes[i];



            if (this.treeAdapter.isElementNode(currentNode))

                this._serializeElement(currentNode);



            else if (this.treeAdapter.isTextNode(currentNode))

                this._serializeTextNode(currentNode);



            else if (this.treeAdapter.isCommentNode(currentNode))

                this._serializeCommentNode(currentNode);



            else if (this.treeAdapter.isDocumentTypeNode(currentNode))

                this._serializeDocumentTypeNode(currentNode);

        }

    }

};



Serializer.prototype._serializeElement = function (node) {

    var tn = this.treeAdapter.getTagName(node),

        ns = this.treeAdapter.getNamespaceURI(node);



    this.html += '<' + tn;

    this._serializeAttributes(node);

    this.html += '>';



    if (tn !== $.AREA && tn !== $.BASE && tn !== $.BASEFONT && tn !== $.BGSOUND && tn !== $.BR && tn !== $.BR &&

        tn !== $.COL && tn !== $.EMBED && tn !== $.FRAME && tn !== $.HR && tn !== $.IMG && tn !== $.INPUT &&

        tn !== $.KEYGEN && tn !== $.LINK && tn !== $.MENUITEM && tn !== $.META && tn !== $.PARAM && tn !== $.SOURCE &&

        tn !== $.TRACK && tn !== $.WBR) {



        if (tn === $.PRE || tn === $.TEXTAREA || tn === $.LISTING) {

            var firstChild = this.treeAdapter.getFirstChild(node);



            if (firstChild && this.treeAdapter.isTextNode(firstChild)) {

                var content = this.treeAdapter.getTextNodeContent(firstChild);



                if (content[0] === '\n')

                    this.html += '\n';

            }

        }



        var childNodesHolder = tn === $.TEMPLATE && ns === NS.HTML ?

                               this.treeAdapter.getChildNodes(node)[0] :

                               node;



        this._serializeChildNodes(childNodesHolder);

        this.html += '</' + tn + '>';

    }

};



Serializer.prototype._serializeAttributes = function (node) {

    var attrs = this.treeAdapter.getAttrList(node);



    for (var i = 0, attrsLength = attrs.length; i < attrsLength; i++) {

        var attr = attrs[i],

            value = this.options.encodeHtmlEntities ? escapeString(attr.value, true) : attr.value;



        this.html += ' ';



        if (!attr.namespace)

            this.html += attr.name;



        else if (attr.namespace === NS.XML)

            this.html += 'xml:' + attr.name;



        else if (attr.namespace === NS.XMLNS) {

            if (attr.name !== 'xmlns')

                this.html += 'xmlns:';



            this.html += attr.name;

        }



        else if (attr.namespace === NS.XLINK)

            this.html += 'xlink:' + attr.name;



        else

            this.html += attr.namespace + ':' + attr.name;



        this.html += '="' + value + '"';

    }

};



Serializer.prototype._serializeTextNode = function (node) {

    var content = this.treeAdapter.getTextNodeContent(node),

        parent = this.treeAdapter.getParentNode(node),

        parentTn = void 0;



    if (parent && this.treeAdapter.isElementNode(parent))

        parentTn = this.treeAdapter.getTagName(parent);



    if (parentTn === $.STYLE || parentTn === $.SCRIPT || parentTn === $.XMP || parentTn === $.IFRAME ||

        parentTn === $.NOEMBED || parentTn === $.NOFRAMES || parentTn === $.PLAINTEXT || parentTn === $.NOSCRIPT) {

        this.html += content;

    }



    else

        this.html += this.options.encodeHtmlEntities ? escapeString(content, false) : content;

};



Serializer.prototype._serializeCommentNode = function (node) {

    this.html += '<!--' + this.treeAdapter.getCommentNodeContent(node) + '-->';

};



Serializer.prototype._serializeDocumentTypeNode = function (node) {

    var name = this.treeAdapter.getDocumentTypeNodeName(node),

        publicId = this.treeAdapter.getDocumentTypeNodePublicId(node),

        systemId = this.treeAdapter.getDocumentTypeNodeSystemId(node);



    this.html += '<' + Doctype.serializeContent(name, publicId, systemId) + '>';

};