package net.ranides.test.data;

import net.ranides.assira.collection.arrays.ArrayAllocator;
import net.ranides.assira.collection.arrays.NativeArrayUtils;
import net.ranides.assira.collection.lists.ListUtils;
import net.ranides.assira.collection.lookups.Lookup;
import net.ranides.assira.collection.maps.IntMap;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

public final class TMapItems<K, V> implements Iterable<TMapItem<K, V>> {

    protected final TMap<K, V> that;

    protected final List<TMapItem<K, V>> data = new ArrayList<>();

    protected TMapItems(TMap<K, V> that) {
        this.that = that;
    }

    public int[] indexes() {
        int[] array = new int[data.size()];
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).index();
        }
        return array;
    }

    public K[] keys() {
        K[] array = ArrayAllocator.forComponent(that.key(0).getClass(), data.size());
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).key();
        }
        return array;
    }

    public V[] values() {
        V[] array = ArrayAllocator.forComponent(that.value(0).getClass(), data.size());
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).value();
        }
        return array;
    }

    public int[] keysInt() {
        int[] array = new int[data.size()];
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).keyInt();
        }
        return array;
    }

    public int[] valuesInt() {
        int[] array = new int[data.size()];
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).valueInt();
        }
        return array;
    }

    @SuppressWarnings("unchecked")
    public Map.Entry<K, V>[] entries() {
        Map.Entry<K, V>[] array = new Map.Entry[data.size()];
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).entry();
        }
        return array;
    }

    @SuppressWarnings("unchecked")
    public IntMap.IntEntry<V>[] entriesInt() {
        IntMap.IntEntry<V>[] array = new IntMap.IntEntry[data.size()];
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).entryInt();
        }
        return array;
    }

    @SuppressWarnings("unchecked")
    public Lookup.LookupEntry<K>[] lookups() {
        Lookup.LookupEntry<K>[] array = new Lookup.LookupEntry[data.size()];
        for (int i = 0, n = data.size(); i < n; i++) {
            array[i] = data.get(i).lookup();
        }
        return array;
    }

    public TMapItems<K, V> sort(Comparator<K> cmp) {
        TMapItems<K, V> items = new TMapItems<>(that);
        items.data.addAll(data);
        items.data.sort((a, b) -> cmp.compare(a.key(), b.key()));
        return items;
    }

    public TMapItems<K, V> shuffle(long seed) {
        NativeArrayUtils.shuffle(new Random(seed), data.size(), ListUtils.swapper(data));
        return this;
    }

    public TMapItems<K, V> item(int item) {
        data.add(that.item(item, 0));
        return this;
    }

    public TMapItems<K, V> item(int item, int n) {
        data.add(that.item(item, n));
        return this;
    }

    @Override
    public Iterator<TMapItem<K, V>> iterator() {
        return data.iterator();
    }

    public TMapItem<K, V> at(int index) {
        return data.get(index);
    }

    public <M extends Map<K, V>> M into(M target) {
        for (TMapItem<K, V> item : data) {
            target.put(item.key(), item.value());
        }
        return target;
    }

    public void dump(PrintStream out) {
        out.printf("TItems(%d)%n", data.size());
        for (int i = 0, n = data.size(); i < n; i++) {
            out.printf(" - %3d. %s%n", i, data.get(i).key());
        }
    }

}
