package de.accso.flexinale.common.shared_kernel;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

public interface Selection<T> extends Set<T> {
    Set<T> next(); // select one or more options from the list based on a selection algorithm

    enum SelectionAlgorithm {
        RANDOM, CONSTANT_FIRST, CONSTANT_LAST, ROUND_ROBIN, ALL
    }

    final class RandomSelector<T> extends SynchronizedSet<T> implements Selection<T> {
        @Override
        public Set<T> next() {
            if (this.size() == 0) return Set.of();

            int randomIndex = ThreadLocalRandom.current().nextInt(this.size());
            return Set.of(this.stream()
                    .skip(randomIndex)
                    .findFirst()
                    .orElseThrow(DeveloperMistakeException::new));
        }
    }

    final class ConstantFirstSelector<T> extends SynchronizedSet<T> implements Selection<T> {
        @Override
        public Set<T> next() {
            if (this.size() == 0) return Set.of();

            return Set.of(this.stream()
                    .findFirst()
                    .orElseThrow(DeveloperMistakeException::new));
        }
    }

    final class ConstantLastSelector<T> extends SynchronizedSet<T> implements Selection<T> {
        @Override
        public Set<T> next() {
            if (this.size() == 0) return Set.of();

            return Set.of(this.stream()
                    .skip(this.size()-1)
                    .findFirst()
                    .orElseThrow(DeveloperMistakeException::new));
        }
    }

    final class RoundRobinSelector<T> extends SynchronizedSet<T> implements Selection<T> {
        private int roundRobinIndex;

        @Override
        public Set<T> next() {
            if (this.size() == 0) return Set.of();

            T result = this.stream().toList().get(roundRobinIndex++);
            if (roundRobinIndex == this.size()) roundRobinIndex = 0;

            return Set.of(result);
        }
    }

    final class AllSelector<T> extends SynchronizedSet<T> implements Selection<T> {
        @Override
        public Set<T> next() {
            if (this.size() == 0) return Set.of();

            return this;
        }
    }
}
