package tc.oc.commons.bukkit.localization;

import java.nio.file.Path;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;

import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.inject.assistedinject.Assisted;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.command.CommandSender;
import tc.oc.commons.bukkit.chat.ComponentRenderContext;
import tc.oc.commons.bukkit.chat.RenderableComponent;
import tc.oc.commons.core.chat.ImmutableComponent;
import tc.oc.commons.core.localization.Locales;

public class LocalizedMessageMap extends AbstractMap<String, BaseComponent> {

    public interface Factory {
        LocalizedMessageMap create(@Assisted("source") Path sourcePath,
                                   @Assisted("localized") Path localizedPath);
    }

    private final LocalizedDocument<Map<String, BaseComponent>> document;

    @Inject LocalizedMessageMap(@Assisted("source") Path sourcePath, @Assisted("localized") Path localizedPath, LocalizedDocument.Factory<Map<String, BaseComponent>> factory) {
        this.document = factory.create(sourcePath, localizedPath);
    }

    public void disable() {
        document.disable();
    }

    @Override
    public int size() {
        return document.getDefault().map(Map::size).orElse(0);
    }

    @Override
    public boolean isEmpty() {
        return !document.getDefault().filter(map -> !map.isEmpty()).isPresent();
    }

    @Override
    public boolean containsKey(Object key) {
        return document.getDefault().filter(map -> map.containsKey(key)).isPresent();
    }

    @Override
    public Set<String> keySet() {
        return document.getDefault().map(Map::keySet).orElseGet(ImmutableSet::of);
    }

    @Override
    public Collection<BaseComponent> values() {
        return Collections2.transform(keySet(), this::get);
    }

    @Override
    public Set<Entry<String, BaseComponent>> entrySet() {
        return Maps.asMap(keySet(), this::get).entrySet();
    }

    @Override
    public BaseComponent get(Object key) {
        return key instanceof String ? new Component((String) key) : null;
    }

    private class Component extends ImmutableComponent implements RenderableComponent {
        final String key;

        Component(String key) {
            this.key = key;
        }

        @Override
        public BaseComponent render(ComponentRenderContext context, CommandSender viewer) {
            final Locale locale = Locales.locale(viewer);
            return document.get(locale)
                           .map(map -> map.get(key))
                           .filter(c -> c != null)
                           .findFirst()
                           .map(c -> context.render(c, viewer))
                           .orElseThrow(() -> new IllegalStateException("Can't find localized message " + key +
                                                                        " for locale " + locale));
        }
    }
}
