package org.xbib.settings;

import org.xbib.datastructures.api.Builder;
import org.xbib.datastructures.api.DataStructure;
import org.xbib.datastructures.api.ListNode;
import org.xbib.datastructures.api.MapNode;
import org.xbib.datastructures.api.Node;
import org.xbib.datastructures.api.Parser;
import org.xbib.datastructures.api.ValueNode;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.List;
import java.util.Map;

public abstract class AbstractSettingsLoader implements SettingsLoader {

    public AbstractSettingsLoader() {
    }

    public abstract DataStructure dataStructure();

    @Override
    public Map<String, String> load(Map<String, Object> map) throws IOException {
        Builder builder = dataStructure().createBuilder();
        builder.buildMap(map);
        return load(builder.build());
    }

    @Override
    public Map<String, String> load(String source) throws IOException {
        Parser parser = dataStructure().createParser();
        return load(parser, new StringReader(source));
    }

    protected abstract Map<String, String> load(Parser parser, Reader reader) throws IOException;

    protected void parseObject(Node<?> node,
                             Map<String, String> map,
                             List<CharSequence> path,
                             CharSequence name) {
        if (node instanceof ValueNode valueNode) {
            StringBuilder sb = new StringBuilder();
            for (CharSequence s : path) {
                sb.append(s).append('.');
            }
            sb.append(name);
            Object object = valueNode.get();
            map.put(sb.toString(), object != null ? object.toString() : null);
        } else if (node instanceof ListNode listNode) {
            int counter = 0;
            for (Node<?> nn : listNode.get()) {
                parseObject(nn, map, path, name + "." + (counter++));
            }
        } else if (node instanceof MapNode mapNode) {
            if (name != null) {
                path.add(name);
            }
            for (Map.Entry<CharSequence, Node<?>> me : mapNode.get().entrySet()) {
                parseObject(me.getValue(), map, path, me.getKey());
            }
            if (name != null) {
                path.removeLast();
            }
        }
    }
}
