/**
 *  format html prettily
 * @author yiminghe@gmail.com
 */
KISSY.add("htmlparser/writer/beautify", function (S, BasicWriter, dtd, Utils) {

    function BeautifyWriter() {
        var self = this;
        BeautifyWriter.superclass.constructor.apply(self, arguments);
        // tag in pre should not indent
        // space (\t\r\n ) in pre should not collapse
        self.inPre = 0;
        self.indentChar = "\t";
        self.indentLevel = 0;
        // whether to indent on current line
        // if already indent and then not line break ,next tag should not indent
        self.allowIndent = 0;
        self.rules = {};

        for (var e in S.merge(
            dtd.$nonBodyContent,
            dtd.$block,
            dtd.$listItem,
            dtd.$tableContent,
            // may add unnecessary whitespaces
            {
                "select":1,
                // add unnecessary whitespaces is ok for script and style
                "script":1,
                "style":1
            }
        )) {
            self.setRules(e, {
                // whether its tag/text children should indent
                allowIndent:1,
                breakBeforeOpen:1,
                breakAfterOpen:1,
                breakBeforeClose:1, // !dtd[e]['#text']
                breakAfterClose:1
            });
        }

        self.setRules('option', {
            breakBeforeOpen:1
        });

        self.setRules('optiongroup', {
            breakBeforeOpen:1
        });

        self.setRules('br', {
            breakAfterOpen:1
        });

        self.setRules('title', {
            allowIndent:0,
            breakBeforeClose:0,
            breakAfterOpen:0
        });

        // Disable indentation on <pre>.
        self.setRules('pre', {
            allowIndent:0
        });
    }

    S.extend(BeautifyWriter, BasicWriter, {
        indentation:function () {
            if (!this.inPre) {
                this.append(new Array(this.indentLevel + 1).join(this.indentChar));
            }
            // already indent ,unless line break  it will not indent again
            this.allowIndent = 0;
        },

        lineBreak:function () {
            var o = this.output;
            if (!this.inPre && o.length) {
                // prevent adding more \n between tags :
                // before : <div>\n<div>\n</div>\n</div> => <div>\n\t' '\n<div>
                // now : <div>\n<div>\n</div>\n</div> => <div>\n<div> => indentation =><div>\n\t<div>
                for (var j = o.length - 1; j >= 0; j--) {
                    if (!(/[\r\n\t ]/.test(o[j]))) {
                        break;
                    }
                }
                o.length = j + 1;
                this.append("\n");
            }
            // allow indentation if encounter next tag
            this.allowIndent = 1;
        },

        setRules:function (tagName, rule) {
            if (!this.rules[tagName]) {
                this.rules[tagName] = {};
            }
            S.mix(this.rules[tagName], rule);
        },

        openTag:function (el) {

            var tagName = el.tagName,
                rules = this.rules[tagName] || {};
            if (this.allowIndent) {
                this.indentation();
            } else if (rules.breakBeforeOpen) {
                this.lineBreak();
                this.indentation();
            }
            BeautifyWriter.superclass.openTag.apply(this, arguments);
        },

        openTagClose:function (el) {

            var tagName = el.tagName;
            var rules = this.rules[tagName] || {};
            if (el.isSelfClosed) {
                this.append(" />")
            } else {
                this.append(">");
                if (rules.allowIndent) {
                    this.indentLevel++;
                }
            }
            if (rules.breakAfterOpen) {
                this.lineBreak();
            }
            if (tagName === 'pre') {
                this.inPre = 1;
            }
        },

        closeTag:function (el) {
            var self = this,
                tagName = el.tagName,
                rules = self.rules[tagName] || {};

            if (rules.allowIndent) {
                self.indentLevel--;
            }

            if (self.allowIndent) {
                self.indentation();
            } else if (rules.breakBeforeClose) {
                self.lineBreak();
                self.indentation();
            }

            BeautifyWriter.superclass.closeTag.apply(self, arguments);

            if (tagName === "pre") {
                self.inPre = 0;
            }

            if (rules.breakAfterClose) {
                self.lineBreak();
            }

        },

        text:function (text) {
            if (this.allowIndent) {
                this.indentation();
            }
            if (!this.inPre) {
                // shrink consequential spaces into one space
                // 换行也没了，否则由于 closeTag 时 lineBreak 会导致换行越来越多
                text = Utils.collapseWhitespace(text);
            }
            this.append(text);
        },

        comment:function (comment) {
            if (this.allowIndent) {
                this.indentation();
            }
            this.append("<!--" + comment + "-->");
        },

        cdata:function (text) {
            if (this.allowIndent) {
                this.indentation();
            }
            this.append(S.trim(text));
        }

    });

    return BeautifyWriter;

}, {
    requires:['./basic', '../dtd', '../Utils']
});