package ru.yandex.autodoc.common.doc.view.renderers;

import ru.yandex.autodoc.common.doc.view.Markup;
import ru.yandex.autodoc.common.out.json.builder.CommentedJsonAppendable;
import ru.yandex.autodoc.common.out.json.builder.JsonAppendable;
import ru.yandex.autodoc.common.util.enums.IEnumResolver;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * @author avhaliullin
 */
public class HtmlJSONAppendable extends CommentedJsonAppendable {
    protected final boolean quoteEnabled;
    protected final StringBuilder sb = new StringBuilder();
    protected final HtmlRenderer renderer;
    protected final HtmlRenderer.ElementContext ctx;

    public HtmlJSONAppendable(StringBuilder sb, HtmlRenderer.ElementContext ctx, boolean quoteEnabled) {
        super(sb, "  ");
        this.renderer = ctx.htmlRenderer;
        this.ctx = ctx;
        this.quoteEnabled = quoteEnabled;
    }

    private String newSpoiler() {
        return ctx.rCtx.nextSpoilerId();
    }

    private int newPoly() {
        ctx.rCtx.polyTypeSets.add(new HashSet<>());
        return ctx.rCtx.polyTypeSets.size() - 1;
    }

    private void registerType(int polyId, String name) {
        ctx.rCtx.polyTypeSets.get(polyId).add(name);
    }

    protected String inlineMarkup(Markup m) {
        return renderer.renderElement(ctx.rCtx, ctx.level, m, ctx.idPrefix, ctx.inInvisible);
    }

    public <T> PolymorphicHandler<T> polymorphic(IEnumResolver<T> er, String fieldName) {
        return new HtmlPolymorphicHandler<>(er, fieldName, new ArrayList<>(comments));
    }

    public NoTypePolymorphicHandler polymorphic() {
        return new HtmlNoTypePolyHandler();
    }

    protected void startSpan(String mainClass, String... extras) {
        CSSClass clazz = CSSClass.create(mainClass).withPrefixes().add(CSSClass.create(extras));
        append("<span " + clazz.getAttribute() + ">");
    }

    protected void endSpan() {
        append("</span>");
    }

    private class HtmlNoTypePolyHandler implements NoTypePolymorphicHandler {
        @Override
        public JsonAppendable getParent() {
            return HtmlJSONAppendable.this;
        }

        @Override
        public JsonAppendable withCase(String caseId) {
            comments.forEach(HtmlJSONAppendable.this::commentForLastKey);
            String spoilerId = newSpoiler();
            newLine();
            startSpan("json-fake-comment-multiline");
            append(HtmlRenderer.href("/*" + caseId + "*/", "javascript:spoiler(\"" + spoilerId + "\");"));
            endSpan();
            append("<span id='" + spoilerId + "' class='hide'>");

            startObject();
            return HtmlJSONAppendable.this;
        }

        @Override
        public void endCase() {
            endObject();
            append("</span>");
        }
    }

    private class HtmlPolymorphicHandler<T> implements PolymorphicHandler<T> {
        private final IEnumResolver<T> er;
        private final String fieldName;
        private final List<String> comments;
        private final int polyId;

        public HtmlPolymorphicHandler(IEnumResolver<T> er, String fieldName, List<String> comments) {
            this.er = er;
            this.fieldName = fieldName;
            this.comments = comments;
            this.polyId = newPoly();
        }

        @Override
        public JsonAppendable getParent() {
            return HtmlJSONAppendable.this;
        }

        @Override
        public JsonAppendable withCase(T tpe) {
            comments.forEach(HtmlJSONAppendable.this::commentForLastKey);
            String typeName = er.getName(tpe);
            registerType(polyId, typeName);
            append("<div id='poly_block_" + polyId + "_" + typeName + "' class='poly_block_" + polyId + "'>");
            startObject();
            appendKey(fieldName);
            startSpan("json-type-selector");
            append("<select class='type_selector type_selector_" + polyId + "' onchange=\"processTypeChange('" + polyId + "', this.value);\"></select>");
            endSpan();
            return HtmlJSONAppendable.this;
        }

        @Override
        public void endCase() {
            endObject();
            append("</div>");
        }
    }

    protected void printMultilineCommentStart() {
        startSpan("json-fake-comment-multiline");
        super.printMultilineCommentStart();
    }

    protected void printMultilineCommentEnd() {
        super.printMultilineCommentEnd();
        endSpan();
    }

    protected void printSingleLineCommentStart() {
        startSpan("json-fake-comment-singleline");
        super.printSingleLineCommentStart();
    }

    protected void printArrayContinuation() {
        startSpan("json-fake-arraycontinuation");
        super.printArrayContinuation();
        endSpan();
    }

    protected void printSingleLineCommentEnd() {
        super.printSingleLineCommentEnd();
        endSpan();
    }

    protected void printKey(String key) {
        startSpan("json-key");
        super.printKey(key);
        endSpan();
    }

    protected void printColon() {
        startSpan("json-colon");
        super.printColon();
        endSpan();
    }

    protected void printStringValue(String value) {
        startSpan("json-value-string");
        super.printStringValue(value);
        endSpan();
    }


    protected void printBooleanValue(boolean value) {
        startSpan("json-value-boolean");
        super.printBooleanValue(value);
        endSpan();
    }

    protected void printNumericValue(String value) {
        startSpan("json-value-numeric");
        super.printNumericValue(value);
        endSpan();
    }

    protected void printNullValue() {
        startSpan("json-value-null");
        super.printNullValue();
        endSpan();
    }

    protected void printComma() {
        startSpan("json-comma");
        super.printComma();
        endSpan();
    }

    protected void printStartObject() {
        startSpan("json-brackets-curly-open", "brackets-pair-" + ctx.rCtx.bracketIndexes.open());
        super.printStartObject();
        endSpan();
    }

    protected void printEndObject() {
        startSpan("json-brackets-curly-close", "brackets-pair-" + ctx.rCtx.bracketIndexes.close());
        super.printEndObject();
        endSpan();
    }

    protected void printStartArray() {
        startSpan("json-brackets-square-open", "brackets-pair-" + ctx.rCtx.bracketIndexes.open());
        super.printStartArray();
        endSpan();
    }

    protected void printEndArray() {
        startSpan("json-brackets-square-close", "brackets-pair-" + ctx.rCtx.bracketIndexes.close());
        super.printEndArray();
        endSpan();
    }

    @Override
    protected String quoteString(String s) {
        return quoteEnabled ? super.quoteString(s) : s;
    }
}
