package ru.yandex.webmaster3.api.http.rest.routing;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author avhaliullin
 */
public class HierarchicStorage<T> {
    private final HierarchicStorage<T> variableChild;
    private final Map<String, HierarchicStorage<T>> staticChildren;
    private final T value;

    private HierarchicStorage(HierarchicStorage<T> variableChild, Map<String, HierarchicStorage<T>> staticChildren, T value) {
        this.variableChild = variableChild;
        this.staticChildren = staticChildren;
        this.value = value;
    }

    public Optional<T> getValue(List<String> pathSegments) {
        HierarchicStorage<T> cur = this;
        for (String segment : pathSegments) {
            Optional<HierarchicStorage<T>> next = cur.next(segment);
            if (!next.isPresent()) {
                return Optional.empty();
            }
            cur = next.get();
        }
        return Optional.ofNullable(cur.value);
    }

    private Optional<HierarchicStorage<T>> next(String segment) {
        return Optional.ofNullable(staticChildren.getOrDefault(segment, variableChild));
    }

    public static class Builder<T> {
        private T value = null;
        private Builder<T> variableChild = null;
        private Map<String, Builder<T>> staticChildren = new HashMap<>();

        public Builder<T> setValue(T value) {
            this.value = value;
            return this;
        }

        public Builder<T> addVariable() {
            if (variableChild == null) {
                variableChild = new Builder<T>();
            }
            return variableChild;
        }

        public Builder<T> addStatic(String segment) {
            return staticChildren.computeIfAbsent(segment, ignore -> new Builder<>());
        }

        public HierarchicStorage<T> build() {
            HierarchicStorage<T> variableChild = this.variableChild == null ? null : this.variableChild.build();
            Map<String, HierarchicStorage<T>> staticChildren = new HashMap<>(this.staticChildren.size());
            for (Map.Entry<String, Builder<T>> entry : this.staticChildren.entrySet()) {
                staticChildren.put(entry.getKey(), entry.getValue().build());
            }
            return new HierarchicStorage<T>(variableChild, staticChildren, value);
        }
    }
}
