/* UtilLib - Copyright (c) 2006-2023 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.html;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.io.AppendableWriter;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.Litteral;
import net.mapeadores.util.localisation.Message;
import net.mapeadores.util.localisation.MessageLocalisation;
import net.mapeadores.util.localisation.MessageLocalisationPack;


/**
 *
 * @author Vincent Calame
 */
public class HtmlPrinter {

    protected PrintWriter pw;
    private final MessageLocalisationPack messageLocalisationPack = new MessageLocalisationPack();
    private final MessageLocalisation messageLocalisation = messageLocalisationPack.getMessageLocalisationInterface();
    protected boolean breakLine = true;
    private boolean withJavascript = false;
    private int availableId = 1;

    public HtmlPrinter() {
    }

    public final void addMessageLocalisation(MessageLocalisation messageLocalisation) {
        addMessageLocalisation(messageLocalisation, 1);
    }

    public final void addMessageLocalisation(MessageLocalisation messageLocalisation, int priority) {
        if (messageLocalisation == null) {
            throw new NullPointerException("messageLocalisation is null");
        }
        messageLocalisationPack.addMessageLocalisation(messageLocalisation, priority);
    }

    public MessageLocalisation getMessageLocalisation() {
        return messageLocalisation;
    }

    public String generateId() {
        StringBuilder buf = new StringBuilder();
        buf.append("html-");
        if (availableId < 10) {
            buf.append("000");
        } else if (availableId < 100) {
            buf.append("00");
        } else if (availableId < 1000) {
            buf.append("0");
        }
        buf.append(availableId);
        availableId++;
        return buf.toString();
    }

    public PrintWriter initPrinter(PrintWriter pw) {
        PrintWriter old = this.pw;
        this.pw = pw;
        return old;
    }

    public PrintWriter initPrinter(Appendable appendable) {
        PrintWriter old = this.pw;
        if (appendable instanceof PrintWriter) {
            this.pw = (PrintWriter) appendable;
        } else if (appendable instanceof Writer) {
            this.pw = new PrintWriter((Writer) appendable);
        } else {
            this.pw = new PrintWriter(new AppendableWriter(appendable));
        }
        return old;
    }

    public final void setWithJavascript(boolean withJavascript) {
        this.withJavascript = withJavascript;
    }

    public boolean isWithJavascript() {
        return withJavascript;
    }

    public void flush() {
        pw.flush();
    }

    public PrintWriter getPrintWriter() {
        return pw;
    }

    public HtmlAttributes name(String name) {
        return HA.name(name).id(generateId());
    }

    public HtmlPrinter __append(TrustedHtml trustedHtml) {
        pw.append(trustedHtml);
        return this;
    }

    public HtmlPrinter __escape(char c) {
        return __escape(c, false);
    }

    public HtmlPrinter __escape(char carac, boolean preformatted) {
        switch (carac) {
            case '&':
                pw.print("&amp;");
                break;
            case '"':
                pw.print("&quot;");
                break;
            case '<':
                pw.print("&lt;");
                break;
            case '>':
                pw.print("&gt;");
                break;
            case '\'':
                pw.print("&#x27;");
                break;
            case '\\':
                pw.print("&#x5C;");
                break;
            case '`':
                pw.print("&#x60;");
                break;
            default:
                if ((!preformatted) && (carac < 32)) {
                    pw.print(' ');
                } else {
                    pw.print(carac);
                }
        }
        return this;
    }

    public HtmlPrinter __append(int i) {
        pw.print(i);
        return this;
    }

    public HtmlPrinter __append(long l) {
        pw.print(l);
        return this;
    }

    public HtmlPrinter __colon() {
        HtmlPrinter.this.__localize("_ label.global.colon");
        __space();
        return this;
    }

    public HtmlPrinter __dash() {
        pw.print(" – ");
        return this;
    }

    public HtmlPrinter __space() {
        pw.print(' ');
        return this;
    }

    public HtmlPrinter __newLine() {
        pw.print('\n');
        return this;
    }

    public HtmlPrinter __nonBreakableSpace() {
        pw.print("&nbsp;");
        return this;
    }

    public HtmlPrinter __doublespace() {
        pw.print("&nbsp; ");
        return this;
    }

    public HtmlPrinter __escape(Object obj) {
        if (obj == null) {
            pw.print("null");
            return this;
        }
        return HtmlPrinter.this.__escape(obj.toString());
    }

    public HtmlPrinter __escape(CharSequence s) {
        return HtmlPrinter.this.__escape(s, false);
    }

    public HtmlPrinter __escape(CharSequence s, boolean preformatted) {
        if (s == null) {
            pw.print("null");
            return this;
        }
        int length = s.length();
        for (int i = 0; i < length; i++) {
            __escape(s.charAt(i), preformatted);
        }
        return this;
    }

    public HtmlPrinter __breakLine() {
        if (breakLine) {
            pw.println("");
        }
        return this;
    }

    public HtmlPrinter __jsObject(String name, JsObject jsObject) {
        this
                .__escape(name)
                .__escape(" = ")
                .__(jsObject.print(this))
                .__scriptLineEnd();
        return this;
    }

    public HtmlPrinter __jsObject(String declaration, String name, JsObject jsObject) {
        this
                .__escape(declaration)
                .__space()
                .__escape(name)
                .__escape(" = ")
                .__(jsObject.print(this))
                .__scriptLineEnd();
        return this;
    }

    public HtmlPrinter __jsAssignObject(String name, JsObject jsObject) {
        this
                .__escape("Object.assign(")
                .__escape(name)
                .__escape(",")
                .__(jsObject.print(this))
                .__escape(")")
                .__scriptLineEnd();
        return this;
    }

    public HtmlPrinter __scriptLineEnd() {
        pw.println(";");
        return this;
    }

    public HtmlPrinter __scriptLiteral(CharSequence s) {
        if (s == null) {
            pw.print("null");
            return this;
        }
        pw.print('"');
        int length = s.length();
        for (int i = 0; i < length; i++) {
            char carac = s.charAt(i);
            switch (carac) {
                case '\n':
                    pw.print("\\n");
                    break;
                case '\t':
                    pw.print("\\t");
                    break;
                case '\r':
                    pw.print("\\r");
                    break;
                case '"':
                    pw.print("\\\"");
                    break;
                case '\'':
                    pw.print("\\'");
                    break;
                case '\\':
                    pw.print("\\\\");
                    break;
                case '<':
                    pw.print("\\u003c");
                    break;
                case '>':
                    pw.print("\\u003e");
                    break;
                case '&':
                    pw.print("\\u0026");
                    break;
                default:
                    if (carac < 32) {
                        pw.print(' ');
                    } else {
                        pw.print(carac);
                    }
            }
        }
        pw.print('"');
        return this;
    }


    public String getLocalization(String messageKey) {
        String loc = messageLocalisation.toString(messageKey);
        if (loc == null) {
            loc = "?" + messageKey + "?";
        }
        return loc;
    }

    public String getLocalization(String messageKey, Object... messageValues) {
        String loc = messageLocalisation.toString(messageKey, messageValues);
        if (loc == null) {
            loc = "?" + messageKey + "?";
        }
        return loc;
    }

    public String getLocalization(Message message) {
        String loc = messageLocalisation.toString(message);
        if (loc == null) {
            loc = "?" + message.getMessageKey() + "?";
        }
        return loc;
    }

    public HtmlPrinter __localize(String messageKey) {
        HtmlPrinter.this.__escape(getLocalization(messageKey));
        return this;
    }

    public HtmlPrinter __localize(String messageKey, Object... messageValues) {
        HtmlPrinter.this.__escape(getLocalization(messageKey, messageValues));
        return this;
    }

    public HtmlPrinter __localize(Message message) {
        HtmlPrinter.this.__escape(getLocalization(message));
        return this;
    }

    public HtmlPrinter __localize(Object l10nObject) {
        if (l10nObject instanceof String) {
            return __localize((String) l10nObject);
        } else if (l10nObject instanceof Message) {
            return __localize((Message) l10nObject);
        } else if (l10nObject instanceof Litteral) {
            return __escape(l10nObject.toString());
        } else if (l10nObject instanceof Runnable) {
            return __((Runnable) l10nObject);
        } else if (l10nObject instanceof Consumer) {
            return __((Consumer<HtmlPrinter>) l10nObject);
        } else {
            return __localize(l10nObject.toString());
        }
    }

    public HtmlPrinter __(boolean b) {
        return this;
    }

    public HtmlPrinter __(@Nullable Consumer<HtmlPrinter> consumer) {
        if (consumer != null) {
            consumer.accept(this);
        }
        return this;
    }

    public HtmlPrinter __(@Nullable Runnable runnable) {
        if (runnable != null) {
            runnable.run();
        }
        return this;
    }

    public HtmlPrinter __(@Nullable BiConsumer<HtmlPrinter, Object> biconsumer, Object argument) {
        if (biconsumer != null) {
            biconsumer.accept(this, argument);
        }
        return this;
    }

    public HtmlPrinter __(@Nullable HtmlWrapper wrapper, @Nullable Consumer<HtmlPrinter> consumer) {
        if (wrapper != null) {
            if (consumer != null) {
                wrapper.wrap(this, consumer);
            }
        } else if (consumer != null) {
            consumer.accept(this);
        }
        return this;
    }

    public HtmlPrinter __(@Nullable HtmlWrapper wrapper, @Nullable Runnable runnable) {
        if (wrapper != null) {
            if (runnable != null) {
                wrapper.wrap(this, runnable);
            }
        } else {
            if (runnable != null) {
                runnable.run();
            }
        }
        return this;
    }

    public HtmlPrinter __if(boolean condition, @Nullable Consumer<HtmlPrinter> consumer) {
        if ((condition) && (consumer != null)) {
            consumer.accept(this);
        }
        return this;
    }

    public HtmlPrinter __if(boolean condition, @Nullable Runnable runnable) {
        if ((condition) && (runnable != null)) {
            runnable.run();
        }
        return this;
    }

    public HtmlPrinter __start(Object o) {
        return this;
    }

    public HtmlPrinter __end(Object o) {
        return this;
    }

    public HtmlPrinter A(HtmlAttributes htmlAttributes) {
        openTag("a");
        if (htmlAttributes != null) {
            printAttribute("href", htmlAttributes.href());
            printAttribute("target", htmlAttributes.target());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _A() {
        endTag("a");
        return this;
    }

    public HtmlPrinter BODY() {
        startTag("body");
        __breakLine();
        return this;
    }

    public HtmlPrinter BODY(String classes) {
        openTag("body");
        printAttribute("class", classes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter BODY(HtmlAttributes htmlAttributes) {
        openTag("body");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _BODY() {
        endTag("body");
        __breakLine();
        return this;
    }

    public HtmlPrinter BR() {
        startTag("br");
        return this;
    }

    public HtmlPrinter BUTTON(HtmlAttributes htmlAttributes) {
        openTag("button");
        printHTMLAttributes(htmlAttributes);
        printBooleanAttribute("disabled", htmlAttributes.disabled());
        printAttribute("type", htmlAttributes.type());
        closeTag();
        return this;
    }

    public HtmlPrinter _BUTTON() {
        endTag("button");
        return this;
    }

    public HtmlPrinter COL(String classes) {
        openTag("col");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter COL(HtmlAttributes htmlAttributes) {
        openTag("col");
        if (htmlAttributes != null) {
            printAttribute("width", htmlAttributes.width());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter CODE() {
        startTag("code");
        return this;
    }

    public HtmlPrinter CODE(String classes) {
        openTag("code");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter CODE(HtmlAttributes htmlAttributes) {
        openTag("code");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _CODE() {
        endTag("code");
        return this;
    }

    public HtmlPrinter COMMENT() {
        pw.print("<!-- ");
        return this;
    }

    public HtmlPrinter _COMMENT() {
        pw.print(" -->");
        return this;
    }

    public HtmlPrinter DD() {
        startTag("dd");
        return this;
    }

    public HtmlPrinter DD(String classes) {
        openTag("dd");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter DD(HtmlAttributes htmlAttributes) {
        openTag("dd");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _DD() {
        endTag("dd");
        __breakLine();
        return this;
    }

    public HtmlPrinter DEL() {
        startTag("del");
        return this;
    }

    public HtmlPrinter DEL(HtmlAttributes htmlAttributes) {
        openTag("del");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _DEL() {
        endTag("del");
        return this;
    }

    public HtmlPrinter DETAILS() {
        startTag("details");
        return this;
    }

    public HtmlPrinter DETAILS(String classes) {
        openTag("details");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter DETAILS(HtmlAttributes htmlAttributes) {
        openTag("details");
        if (htmlAttributes != null) {
            printBooleanAttribute("open", htmlAttributes.open());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _DETAILS() {
        endTag("details");
        __breakLine();
        return this;
    }

    public HtmlPrinter DIV() {
        startTag("div");
        return this;
    }

    public HtmlPrinter DIV(String classes) {
        openTag("div");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter DIV(HtmlAttributes htmlAttributes) {
        openTag("div");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _DIV() {
        endTag("div");
        __breakLine();
        return this;
    }

    public HtmlPrinter DL() {
        startTag("dl");
        return this;
    }

    public HtmlPrinter DL(String classes) {
        openTag("dl");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter DL(HtmlAttributes htmlAttributes) {
        openTag("dl");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _DL() {
        endTag("dl");
        __breakLine();
        return this;
    }

    public HtmlPrinter DOCTYPE_html5() {
        pw.println("<!DOCTYPE html>");
        return this;
    }

    public HtmlPrinter DOCTYPE_frameset() {
        pw.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"http://www.w3.org/TR/html4/frameset.dtd\">");
        return this;
    }

    public HtmlPrinter DT() {
        startTag("dt");
        return this;
    }

    public HtmlPrinter DT(String classes) {
        openTag("dt");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter DT(HtmlAttributes htmlAttributes) {
        openTag("dt");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _DT() {
        endTag("dt");
        __breakLine();
        return this;
    }

    public HtmlPrinter EM() {
        startTag("em");
        return this;
    }

    public HtmlPrinter EM(HtmlAttributes htmlAttributes) {
        openTag("em");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _EM() {
        endTag("em");
        return this;
    }

    public HtmlPrinter FIELDSET() {
        startTag("fieldset");
        return this;
    }

    public HtmlPrinter FIELDSET(String classes) {
        openTag("fieldset");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter FIELDSET(HtmlAttributes htmlAttributes) {
        openTag("fieldset");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _FIELDSET() {
        endTag("fieldset");
        return this;
    }

    public HtmlPrinter FORM(String method, String action, String target, String enctype) {
        openTag("form");
        printAttribute("action", action);
        printAttribute("method", method);
        printAttribute("target", target);
        printAttribute("enctype", enctype);
        closeTag();
        return this;
    }

    public HtmlPrinter FORM(HtmlAttributes htmlAttributes) {
        openTag("form");
        if (htmlAttributes != null) {
            printAttribute("action", htmlAttributes.action());
            printAttribute("method", htmlAttributes.method());
            printAttribute("target", htmlAttributes.target());
            printAttribute("enctype", htmlAttributes.enctype());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter FORM_get(String action) {
        return FORM(HtmlConstants.GET_METHOD, action, null, null);
    }

    public HtmlPrinter FORM_get(String action, String target) {
        return FORM(HtmlConstants.GET_METHOD, action, target, null);
    }

    public HtmlPrinter FORM_get(HtmlAttributes htmlAttributes) {
        return FORM(htmlAttributes.method(HtmlConstants.GET_METHOD));
    }

    public HtmlPrinter FORM_post(String action) {
        return FORM(HtmlConstants.POST_METHOD, action, null, null);
    }

    public HtmlPrinter FORM_post(String action, String target) {
        return FORM(HtmlConstants.POST_METHOD, action, target, null);
    }

    public HtmlPrinter FORM_post(String action, String target, String enctype) {
        return FORM(HtmlConstants.POST_METHOD, action, target, enctype);
    }

    public HtmlPrinter FORM_post(HtmlAttributes htmlAttributes) {
        return FORM(htmlAttributes.method(HtmlConstants.POST_METHOD));
    }

    public HtmlPrinter _FORM() {
        endTag("form");
        return this;
    }

    public HtmlPrinter FRAME(HtmlAttributes htmlAttributes) {
        openTag("frame");
        if (htmlAttributes != null) {
            printAttribute("src", htmlAttributes.src());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter FRAMESET(HtmlAttributes htmlAttributes) {
        openTag("frameset");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _FRAMESET() {
        endTag("frameset");
        __breakLine();
        return this;
    }

    public HtmlPrinter H1() {
        startTag("h1");
        return this;
    }

    public HtmlPrinter H1(String classes) {
        openTag("h1");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter H1(HtmlAttributes htmlAttributes) {
        openTag("h1");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _H1() {
        endTag("h1");
        __breakLine();
        return this;
    }

    public HtmlPrinter H2() {
        startTag("h2");
        return this;
    }

    public HtmlPrinter H2(String classes) {
        openTag("h2");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter H2(HtmlAttributes htmlAttributes) {
        openTag("h2");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _H2() {
        endTag("h2");
        __breakLine();
        return this;
    }

    public HtmlPrinter H3() {
        startTag("h3");
        return this;
    }

    public HtmlPrinter H3(String classes) {
        openTag("h3");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter H3(HtmlAttributes htmlAttributes) {
        openTag("h3");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _H3() {
        endTag("h3");
        __breakLine();
        return this;
    }

    public HtmlPrinter H4() {
        startTag("h4");
        return this;
    }

    public HtmlPrinter H4(String classes) {
        openTag("h4");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter H4(HtmlAttributes htmlAttributes) {
        openTag("h4");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _H4() {
        endTag("h4");
        __breakLine();
        return this;
    }

    public HtmlPrinter H5() {
        startTag("h5");
        return this;
    }

    public HtmlPrinter H5(String classes) {
        openTag("h5");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter H5(HtmlAttributes htmlAttributes) {
        openTag("h5");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _H5() {
        endTag("h5");
        __breakLine();
        return this;
    }

    public HtmlPrinter H6() {
        startTag("h6");
        return this;
    }

    public HtmlPrinter H6(String classes) {
        openTag("h6");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter H6(HtmlAttributes htmlAttributes) {
        openTag("h6");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _H6() {
        endTag("h6");
        __breakLine();
        return this;
    }

    public HtmlPrinter HEAD() {
        startTag("head");
        __breakLine();
        return this;
    }

    public HtmlPrinter HEAD(HtmlAttributes htmlAttributes) {
        openTag("head");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _HEAD() {
        endTag("head");
        __breakLine();
        return this;
    }

    public HtmlPrinter HEADER() {
        startTag("header");
        __breakLine();
        return this;
    }

    public HtmlPrinter HEADER(String classes) {
        openTag("header");
        printAttribute("class", classes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter HEADER(HtmlAttributes htmlAttributes) {
        openTag("header");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _HEADER() {
        endTag("header");
        __breakLine();
        return this;
    }

    public HtmlPrinter HR() {
        startTag("hr");
        return this;
    }

    public HtmlPrinter HR(String classes) {
        openTag("hr");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter HR(HtmlAttributes htmlAttributes) {
        openTag("hr");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter HTML() {
        startTag("html");
        __breakLine();
        return this;
    }

    public HtmlPrinter HTML(Lang lang) {
        openTag("html");
        if (lang != null) {
            printAttribute("lang", lang.toString());
            if (lang.isRTLScript()) {
                printAttribute("dir", "rtl");
            }

        }
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _HTML() {
        endTag("html");
        return this;
    }

    public HtmlPrinter IFRAME(HtmlAttributes htmlAttributes) {
        openTag("iframe");
        if (htmlAttributes != null) {
            printAttribute("src", htmlAttributes.src());
            printAttribute("width", htmlAttributes.width());
            printAttribute("height", htmlAttributes.height());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _IFRAME() {
        endTag("iframe");
        __breakLine();
        return this;
    }

    public HtmlPrinter IMG(HtmlAttributes htmlAttributes) {
        openTag("img");
        if (htmlAttributes != null) {
            printAttribute("src", htmlAttributes.src());
            printAttribute("alt", htmlAttributes.alt());
            printAttribute("width", htmlAttributes.width());
            printAttribute("height", htmlAttributes.height());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter INPUT(HtmlAttributes htmlAttributes) {
        return INPUT(htmlAttributes, false);
    }

    public HtmlPrinter INPUT(HtmlAttributes htmlAttributes, boolean preformatted) {
        openTag("input");
        if (htmlAttributes != null) {
            printAttribute("type", htmlAttributes.type());
            printPreformattedAttribute("value", htmlAttributes.value(), preformatted);
            printAttribute("pattern", htmlAttributes.pattern());
            printAttribute("size", htmlAttributes.size());
            printBooleanAttribute("checked", htmlAttributes.checked());
            printBooleanAttribute("disabled", htmlAttributes.disabled());
            printBooleanAttribute("readonly", htmlAttributes.readonly());
            printBooleanAttribute("required", htmlAttributes.required());
            printBooleanAttribute("multiple", htmlAttributes.multiple());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter INPUT_checkbox(HtmlAttributes htmlAttributes) {
        return INPUT(htmlAttributes.type(HtmlConstants.CHECKBOX_TYPE), false);
    }

    public HtmlPrinter INPUT_hidden(String name, String value) {
        openTag("input");
        printAttribute("type", HtmlConstants.HIDDEN_TYPE);
        printAttribute("name", name);
        printAttribute("value", value);
        closeTag();
        return this;
    }

    public HtmlPrinter INPUT_hidden(Map<String, String> hiddenParametersMap) {
        for (Map.Entry<String, String> entry : hiddenParametersMap.entrySet()) {
            INPUT_hidden(entry.getKey(), entry.getValue());
        }
        return this;
    }

    public HtmlPrinter INPUT_radio(HtmlAttributes htmlAttributes) {
        return INPUT(htmlAttributes.type(HtmlConstants.RADIO_TYPE), false);
    }

    public HtmlPrinter INPUT_text(HtmlAttributes htmlAttributes) {
        return INPUT(htmlAttributes.type(HtmlConstants.TEXT_TYPE), false);
    }


    public HtmlPrinter LABEL() {
        startTag("label");
        return this;
    }

    public HtmlPrinter LABEL(String classes) {
        openTag("label");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter LABEL(HtmlAttributes htmlAttributes) {
        openTag("label");
        if (htmlAttributes != null) {
            printAttribute("for", htmlAttributes.forId());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter LABEL_for(String forId) {
        openTag("label");
        printAttribute("for", forId);
        closeTag();
        return this;
    }

    public HtmlPrinter _LABEL() {
        endTag("label");
        return this;
    }

    public HtmlPrinter LEGEND() {
        startTag("legend");
        return this;
    }

    public HtmlPrinter LEGEND(String classes) {
        openTag("legend");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter LEGEND(HtmlAttributes htmlAttributes) {
        openTag("legend");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _LEGEND() {
        endTag("legend");
        __breakLine();
        return this;
    }

    public HtmlPrinter LI() {
        startTag("li");
        __breakLine();
        return this;
    }

    public HtmlPrinter LI(String classes) {
        openTag("li");
        printAttribute("class", classes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter LI(HtmlAttributes htmlAttributes) {
        openTag("li");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _LI() {
        endTag("li");
        __breakLine();
        return this;
    }

    public HtmlPrinter LINK(HtmlAttributes htmlAttributes) {
        openTag("link");
        if (htmlAttributes != null) {
            printAttribute("href", htmlAttributes.href());
            printAttribute("rel", htmlAttributes.rel());
            printAttribute("type", htmlAttributes.type());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter MAIN() {
        startTag("main");
        return this;
    }

    public HtmlPrinter MAIN(String classes) {
        openTag("main");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter MAIN(HtmlAttributes htmlAttributes) {
        openTag("main");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _MAIN() {
        endTag("main");
        __breakLine();
        return this;
    }

    public HtmlPrinter META(HtmlAttributes htmlAttributes) {
        openTag("meta");
        if (htmlAttributes != null) {
            printAttribute("http-equiv", htmlAttributes.httpEquiv());
            printAttribute("content", htmlAttributes.content());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter META_htmlcontenttype() {
        openTag("meta");
        printAttribute("http-equiv", "Content-Type");
        printAttribute("content", "text/html; charset=UTF-8");
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter NAV() {
        startTag("nav");
        return this;
    }

    public HtmlPrinter NAV(String classes) {
        openTag("nav");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter NAV(HtmlAttributes htmlAttributes) {
        openTag("nav");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _NAV() {
        endTag("nav");
        __breakLine();
        return this;
    }

    public HtmlPrinter NOSCRIPT() {
        startTag("noscript");
        return this;
    }

    public HtmlPrinter _NOSCRIPT() {
        endTag("noscript");
        return this;
    }

    public HtmlPrinter OPTGROUP(HtmlAttributes htmlAttributes) {
        openTag("optgroup");
        if (htmlAttributes != null) {
            printAttribute("label", htmlAttributes.label());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _OPTGROUP() {
        endTag("optgroup");
        __breakLine();
        return this;
    }

    public HtmlPrinter OPTION(String value, boolean selected) {
        openTag("option");
        printAttribute("value", value);
        printBooleanAttribute("selected", selected);
        closeTag();
        return this;
    }

    public HtmlPrinter OPTION(String value, String current) {
        boolean selected = false;
        if (value.equals(current)) {
            selected = true;
        }
        return OPTION(value, selected);
    }

    public HtmlPrinter OPTION(HtmlAttributes htmlAttributes) {
        openTag("option");
        if (htmlAttributes != null) {
            printAttribute("value", htmlAttributes.value());
            printBooleanAttribute("selected", htmlAttributes.selected());
            printBooleanAttribute("disabled", htmlAttributes.disabled());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _OPTION() {
        endTag("option");
        __breakLine();
        return this;
    }

    public HtmlPrinter P() {
        startTag("p");
        return this;
    }

    public HtmlPrinter P(String classes) {
        openTag("p");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter P(HtmlAttributes htmlAttributes) {
        openTag("p");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _P() {
        endTag("p");
        __breakLine();
        return this;
    }

    public HtmlPrinter PRE() {
        startTag("pre");
        return this;
    }

    public HtmlPrinter PRE(String classes) {
        openTag("pre");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter PRE(HtmlAttributes htmlAttributes) {
        openTag("pre");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _PRE() {
        endTag("pre");
        return this;
    }

    public HtmlPrinter SCRIPT() {
        startTag("script");
        return this;
    }

    public HtmlPrinter SCRIPT(HtmlAttributes htmlAttributes) {
        openTag("script");
        if (htmlAttributes != null) {
            printAttribute("type", htmlAttributes.type());
            printAttribute("src", htmlAttributes.src());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _SCRIPT() {
        endTag("script");
        __breakLine();
        return this;
    }

    public HtmlPrinter SECTION() {
        startTag("section");
        return this;
    }

    public HtmlPrinter SECTION(String classes) {
        openTag("section");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter SECTION(HtmlAttributes htmlAttributes) {
        openTag("section");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _SECTION() {
        endTag("section");
        __breakLine();
        return this;
    }

    public HtmlPrinter SELECT(String name) {
        openTag("select");
        printAttribute("name", name);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter SELECT(HtmlAttributes htmlAttributes) {
        openTag("select");
        printHTMLAttributes(htmlAttributes);
        printBooleanAttribute("multiple", htmlAttributes.multiple());
        printAttribute("size", htmlAttributes.size());
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _SELECT() {
        endTag("select");
        return this;
    }


    public HtmlPrinter SMALL() {
        startTag("small");
        return this;
    }

    public HtmlPrinter _SMALL() {
        endTag("small");
        return this;
    }

    public HtmlPrinter SPAN() {
        startTag("span");
        return this;
    }

    public HtmlPrinter SPAN(String classes) {
        openTag("span");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter SPAN(HtmlAttributes htmlAttributes) {
        openTag("span");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _SPAN() {
        endTag("span");
        return this;
    }

    public HtmlPrinter STRONG() {
        startTag("strong");
        return this;
    }

    public HtmlPrinter STRONG(HtmlAttributes htmlAttributes) {
        openTag("strong");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _STRONG() {
        endTag("strong");
        return this;
    }

    public HtmlPrinter STYLE() {
        startTag("style");
        __breakLine();
        return this;
    }

    public HtmlPrinter STYLE(HtmlAttributes htmlAttributes) {
        openTag("style");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _STYLE() {
        endTag("style");
        __breakLine();
        return this;
    }

    public HtmlPrinter SUMMARY() {
        startTag("summary");
        return this;
    }

    public HtmlPrinter SUMMARY(String classes) {
        openTag("summary");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter SUMMARY(HtmlAttributes htmlAttributes) {
        openTag("summary");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _SUMMARY() {
        endTag("summary");
        return this;
    }

    public HtmlPrinter TABLE() {
        startTag("table");
        __breakLine();
        return this;
    }

    public HtmlPrinter TABLE(String classes) {
        openTag("table");
        printAttribute("class", classes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter TABLE(HtmlAttributes htmlAttributes) {
        openTag("table");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _TABLE() {
        endTag("table");
        __breakLine();
        return this;
    }

    public HtmlPrinter TBODY() {
        startTag("tbody");
        __breakLine();
        return this;
    }

    public HtmlPrinter TBODY(HtmlAttributes htmlAttributes) {
        openTag("tbody");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _TBODY() {
        endTag("tbody");
        __breakLine();
        return this;
    }

    public HtmlPrinter TD() {
        startTag("td");
        return this;
    }

    public HtmlPrinter TD(String classes) {
        openTag("td");
        printAttribute("class", classes);
        closeTag();
        return this;
    }

    public HtmlPrinter TD(HtmlAttributes htmlAttributes) {
        openTag("td");
        if (htmlAttributes != null) {
            int colSpan = htmlAttributes.colspan();
            if (colSpan > 1) {
                printAttribute("colspan", String.valueOf(colSpan));
            }
            int rowSpan = htmlAttributes.rowspan();
            if (rowSpan > 1) {
                printAttribute("rowspan", String.valueOf(rowSpan));
            }
            printAttribute("width", htmlAttributes.width());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _TD() {
        endTag("td");
        __breakLine();
        return this;
    }

    public HtmlPrinter TEXTAREA(HtmlAttributes htmlAttributes) {
        openTag("textarea");
        int cols = 0;
        int rows = 0;
        if (htmlAttributes != null) {
            cols = htmlAttributes.cols();
            rows = htmlAttributes.rows();
        }
        if (cols < 1) {
            cols = 50;
        }
        if (rows < 1) {
            rows = 3;
        }
        printAttribute("cols", String.valueOf(cols));
        printAttribute("rows", String.valueOf(rows));
        printBooleanAttribute("disabled", htmlAttributes.disabled());
        printBooleanAttribute("readonly", htmlAttributes.readonly());
        printBooleanAttribute("required", htmlAttributes.required());
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _TEXTAREA() {
        endTag("textarea");
        return this;
    }

    public HtmlPrinter TFOOT() {
        startTag("tfoot");
        __breakLine();
        return this;
    }

    public HtmlPrinter TFOOT(HtmlAttributes htmlAttributes) {
        openTag("tfoot");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _TFOOT() {
        endTag("tfoot");
        __breakLine();
        return this;
    }

    public HtmlPrinter TH() {
        startTag("th");
        return this;
    }

    public HtmlPrinter TH(HtmlAttributes htmlAttributes) {
        openTag("th");
        if (htmlAttributes != null) {
            int colSpan = htmlAttributes.colspan();
            if (colSpan > 1) {
                printAttribute("colspan", String.valueOf(colSpan));
            }
            int rowSpan = htmlAttributes.rowspan();
            if (rowSpan > 1) {
                printAttribute("rowspan", String.valueOf(rowSpan));
            }
            printAttribute("width", htmlAttributes.width());
        }
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _TH() {
        endTag("th");
        __breakLine();
        return this;
    }

    public HtmlPrinter THEAD() {
        startTag("thead");
        __breakLine();
        return this;
    }

    public HtmlPrinter THEAD(HtmlAttributes htmlAttributes) {
        openTag("thead");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _THEAD() {
        endTag("thead");
        __breakLine();
        return this;
    }

    public HtmlPrinter TITLE() {
        startTag("title");
        return this;
    }

    public HtmlPrinter TITLE(HtmlAttributes htmlAttributes) {
        openTag("title");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        return this;
    }

    public HtmlPrinter _TITLE() {
        endTag("title");
        __breakLine();
        return this;
    }

    public HtmlPrinter TR() {
        startTag("tr");
        __breakLine();
        return this;
    }

    public HtmlPrinter TR(HtmlAttributes htmlAttributes) {
        openTag("tr");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _TR() {
        endTag("tr");
        __breakLine();
        return this;
    }

    public HtmlPrinter UL() {
        startTag("ul");
        __breakLine();
        return this;
    }

    public HtmlPrinter UL(String classes) {
        openTag("ul");
        printAttribute("class", classes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter UL(HtmlAttributes htmlAttributes) {
        openTag("ul");
        printHTMLAttributes(htmlAttributes);
        closeTag();
        __breakLine();
        return this;
    }

    public HtmlPrinter _UL() {
        endTag("ul");
        __breakLine();
        return this;
    }


    private boolean printHTMLAttributes(HtmlAttributes htmlAttributes) {
        if (htmlAttributes == null) {
            return false;
        }
        printAttribute("class", htmlAttributes.classes());
        printAttribute("id", htmlAttributes.id());
        printAttribute("style", htmlAttributes.style());
        printAttribute("name", htmlAttributes.name());
        Map<String, String> attrMap = htmlAttributes.getAttrMap();
        if (attrMap != null) {
            for (Map.Entry<String, String> entry : attrMap.entrySet()) {
                pw.print(" ");
                HtmlPrinter.this.__escape(entry.getKey());
                pw.print("=\"");
                HtmlPrinter.this.__escape(entry.getValue());
                pw.print("\"");
            }
        }
        printAttributeObject(htmlAttributes.getTitleType(), "title", htmlAttributes.title());
        return true;
    }

    private void printAttributeObject(short type, String name, Object object) {
        switch (type) {
            case HtmlAttributes.STRING:
                printAttribute(name, (String) object);
                break;
            case HtmlAttributes.LOCKEY:
                printLocAttribute(name, (String) object);
                break;
            case HtmlAttributes.MESSAGE:
                printLocAttribute(name, (Message) object);
                break;
        }
    }

    private void printAttribute(String name, String value) {
        if (value == null) {
            return;
        }
        pw.print(" ");
        pw.print(name);
        pw.print("=\"");
        HtmlPrinter.this.__escape(value);
        pw.print("\"");
    }

    private void printLocAttribute(String name, String locKey) {
        if (locKey == null) {
            return;
        }
        pw.print(" ");
        pw.print(name);
        pw.print("=\"");
        HtmlPrinter.this.__localize(locKey);
        pw.print("\"");
    }

    private void printLocAttribute(String name, Message message) {
        if (message == null) {
            return;
        }
        pw.print(" ");
        pw.print(name);
        pw.print("=\"");
        HtmlPrinter.this.__localize(message);
        pw.print("\"");
    }

    private void printBooleanAttribute(String name, boolean value) {
        if (value) {
            printAttribute(name, name);
        }
    }

    private void startTag(String tag) {
        pw.print("<");
        pw.print(tag);
        pw.print(">");
    }

    private void openTag(String tag) {
        pw.print("<");
        pw.print(tag);
    }

    private void closeTag() {
        pw.print(">");
    }

    private void endTag(String tag) {
        pw.print("</");
        pw.print(tag);
        pw.print(">");
    }

    private void printPreformattedAttribute(String name, String value, boolean preformatted) {
        if (value == null) {
            return;
        }
        pw.print(" ");
        pw.print(name);
        pw.print("=\"");
        HtmlPrinter.this.__escape(value, preformatted);
        pw.print("\"");
    }

}
