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

import ru.yandex.autodoc.common.doc.view.Markup;
import ru.yandex.autodoc.common.doc.view.format.JsonValue;
import ru.yandex.autodoc.common.out.json.builder.JsonAppendable;

import java.util.List;

/**
 * @author avhaliullin
 */
public class JsonRenderer {
    public String renderJson(HtmlRenderer.ElementContext ctx, JsonValue json) {
        String id = ctx.rCtx.bindId(ctx.idPrefix, "json");
        StringBuilder result = new StringBuilder();
        result.append("<pre id='").append(id).append("' parentid='").append(ctx.idPrefix)
                .append("' class='json code'>");
        HtmlJSONAppendable jsonAppendable = new HtmlJSONAppendable(result, ctx, false);
        doRender(jsonAppendable, ctx, json);
        result.append("</pre>");
        return result.toString();
    }

    protected void doRender(JsonAppendable appendable, HtmlRenderer.ElementContext ctx, JsonValue json) {
        String desc = json.getDescription() == Markup.EMPTY ? null : renderMarkup(ctx, json.getDescription());
        if (json instanceof JsonValue.IntValue) {
            appendable.appendNumericValue(String.valueOf(((JsonValue.IntValue) json).getExample()));
            appendable.commentForLastKey(desc);
        } else if (json instanceof JsonValue.FloatValue) {
            appendable.appendNumericValue(String.valueOf(((JsonValue.FloatValue) json).getExample()));
            appendable.commentForLastKey(desc);
        } else if (json instanceof JsonValue.BooleanValue) {
            appendable.appendBooleanValue(((JsonValue.BooleanValue) json).getExample());
            appendable.commentForLastKey(desc);
        } else if (json instanceof JsonValue.StringValue) {
            appendable.appendStringValue(renderMarkup(ctx, ((JsonValue.StringValue) json).getExample()));
            appendable.commentForLastKey(desc);
        } else if (json instanceof JsonValue.Array) {
            appendable.startArray();
            appendable.commentForLastKey(desc);
            doRender(appendable, ctx, ((JsonValue.Array) json).getItem());
            appendable.endArray();
        } else if (json instanceof JsonValue.Object) {
            JsonValue.Object obj = (JsonValue.Object) json;
            appendable.startObject();
            appendable.comment(desc);
            renderFields(ctx, appendable, obj.getFields());
            if (obj.isMap()) {
                appendable.endMapObject();
            } else {
                appendable.endObject();
            }
        } else if (json instanceof JsonValue.PolyObject) {
            JsonValue.PolyObject poly = (JsonValue.PolyObject) json;
            JsonAppendable.NoTypePolymorphicHandler polyAppendable = appendable.polymorphic();
            for (JsonValue.PolyCase pCase : poly.getCases()) {
                JsonAppendable caseAppendable = polyAppendable.withCase(pCase.getCaseName());
                renderField(ctx, caseAppendable,
                        new JsonValue.JsonField(
                                new Markup.Text(poly.getDiscriminator()),
                                new JsonValue.StringValue(
                                        new Markup.Text(pCase.getCaseName()),
                                        Markup.EMPTY
                                ),
                                Markup.EMPTY
                        )
                );
                if (!pCase.getFields().isEmpty()) {
                    renderFields(ctx, caseAppendable, pCase.getFields());
                }
                polyAppendable.endCase();
            }
        }
    }

    private void renderFields(HtmlRenderer.ElementContext ctx, JsonAppendable appendable,
                              List<JsonValue.JsonField> fields) {
        if (!fields.isEmpty()) {
            boolean first = true;
            for (JsonValue.JsonField field : fields) {
                if (!first) {
                    appendable.appendComma();
                }
                first = false;
                renderField(ctx, appendable, field);
            }
        }
    }

    private void renderField(HtmlRenderer.ElementContext ctx, JsonAppendable appendable, JsonValue.JsonField field) {
        appendable.appendKey(renderMarkup(ctx, field.getKey()));
        if (field.getDescription() != Markup.EMPTY) {
            appendable.commentForLastKey(renderMarkup(ctx, field.getDescription()));
        }
        doRender(appendable, ctx, field.getValue());
    }

    private String renderMarkup(HtmlRenderer.ElementContext ctx, Markup m) {
        return ctx.htmlRenderer.renderElement(ctx.rCtx, ctx.level, m, ctx.idPrefix, ctx.inInvisible);
    }
}
