package ru.yandex.webmaster3.core.text.templates;

import java.util.*;

/**
 * @author avhaliullin
 */
public class TextTemplate<T> {
    private final List<Entry<T>> entries;
    private final int textLength;
    private final Map<T, Integer> phsCount;

    public TextTemplate(List<Entry<T>> entries, int textLength, Map<T, Integer> phsCount) {
        this.entries = entries;
        this.textLength = textLength;
        this.phsCount = phsCount;
    }

    public int estimateTextLength(Map<T, String> placeholders) {
        int textLength = this.textLength;
        for (Map.Entry<T, Integer> entry : phsCount.entrySet()) {
            textLength += entry.getValue() * placeholders.get(entry.getKey()).length();
        }
        return textLength;
    }

    public void renderTo(StringBuilder sb, Map<T, String> placeholders) {
        for (Entry<T> entry : entries) {
            entry.renderTo(sb, placeholders);
        }
    }

    public String render(Map<T, String> placeholders) {
        int capacity = textLength;
        for (Map.Entry<T, Integer> entry : phsCount.entrySet()) {
            capacity += entry.getValue() * placeholders.get(entry.getKey()).length();
        }
        StringBuilder sb = new StringBuilder(capacity);
        renderTo(sb, placeholders);
        return sb.toString();
    }

    public RenderableText createRenderable(Map<T, String> placeholders) {
        return new RenderableText() {
            @Override
            public String render() {
                return TextTemplate.this.render(placeholders);
            }

            @Override
            public void renderTo(StringBuilder sb) {
                TextTemplate.this.renderTo(sb, placeholders);
            }
        };
    }

    public static abstract class Entry<T> {
        abstract void renderTo(StringBuilder sb, Map<T, String> placeholderValues);
    }

    public static class PlaceholderEntry<T> extends Entry<T> {
        private final T placeholder;

        public PlaceholderEntry(T placeholder) {
            this.placeholder = placeholder;
        }

        @Override
        void renderTo(StringBuilder sb, Map<T, String> placeholderValues) {
            String value = placeholderValues.get(placeholder);
            if (value == null) {
                throw new IllegalArgumentException(
                        "Required placeholder \"" + placeholder + "\" is missing, placeholders: " + placeholderValues.keySet());
            }
            sb.append(value);
        }
    }

    public static class TextEntry<T> extends Entry<T> {
        private final String text;

        public TextEntry(String text) {
            this.text = text;
        }

        @Override
        void renderTo(StringBuilder sb, Map<T, String> placeholderValues) {
            sb.append(text);
        }
    }

    public static class ConditionEntry<T> extends Entry<T> {

        private final T placeholder;
        private final List<Entry<T>> trueEntries = new ArrayList<>();
        private final List<Entry<T>> falseEntries = new ArrayList<>();

        public ConditionEntry(T placeholder) {
            this.placeholder = placeholder;
        }

        @Override
        void renderTo(StringBuilder sb, Map<T, String> placeholderValues) {
            Object condition = placeholderValues.get(placeholder);
            if (condition == null || Boolean.FALSE.toString().equals(condition)) {
                falseEntries.forEach(e -> e.renderTo(sb, placeholderValues));
            } else {
                trueEntries.forEach(e -> e.renderTo(sb, placeholderValues));
            }
        }

        public ConditionEntry addTrueEntry(Entry<T> entry) {
            trueEntries.add(entry);
            return this;
        }

        public ConditionEntry addFalseEntry(Entry<T> entry) {
            falseEntries.add(entry);
            return this;
        }
    }

    public static <T> TextTemplate<T> createStatic(String content) {
        return new TextTemplate<>(Collections.singletonList(new TextEntry<>(content)), content.length(), Collections.emptyMap());
    }
}
