package com.natpryce.snodge;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.natpryce.snodge.internal.JsonWalk;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

import static com.natpryce.snodge.Mutagens.allMutagens;
import static java.util.stream.Collectors.toList;

public class JsonMutator implements Mutator<JsonElement> {
    private final Random rng;
    private final Mutagen mutagens;

    public JsonMutator() {
        this(allMutagens());
    }

    public JsonMutator(Mutagen mutagen) {
        this(new Random(), mutagen);
    }

    public JsonMutator(Random rng, Mutagen mutagen) {
        this.rng = rng;
        this.mutagens = mutagen;
    }

    @Override
    public List<JsonElement> mutate(final JsonElement document, int mutationCount) {
        return mutations(document, mutationCount).stream()
                .map(mutation -> mutation.apply(document))
                .collect(toList());
    }

    private List<DocumentMutation> mutations(JsonElement document, int mutationCount) {
        List<DocumentMutation> selectedMutations = new ArrayList<>(mutationCount);

        AtomicInteger counter = new AtomicInteger(0) ;

        JsonWalk.walk(document)
                .flatMap(path -> mutagens.potentialMutations(document, path, path.apply(document)))
                .sequential()
                .forEach(potentialMutation -> {
                    int count = counter.getAndIncrement();
                    if (count < mutationCount) {
                        selectedMutations.add(potentialMutation);
                    } else {
                        int index = rng.nextInt(count);
                        if (index < selectedMutations.size()) {
                            selectedMutations.set(index, potentialMutation);
                        }
                    }
                });

        return selectedMutations;
    }

    public Mutator<String> forStrings() {
        Gson gson = new Gson();

        return (originalJsonString, mutationCount) -> {
            JsonElement originalJsonDocument = gson.fromJson(originalJsonString, JsonElement.class);
            return mutate(originalJsonDocument, mutationCount).stream()
                    .map(Object::toString)
                    .collect(toList());
        };
    }
}

@@ -91,54 +91,8 @@
 ent;
-%0Aimport com.natpryce.snodge.internal.JsonWalk;
 %0A%0Aim
@@ -272,16 +272,74 @@
 tagens;%0A
+import static com.natpryce.snodge.internal.JsonWalk.walk;%0A
 import s
@@ -1205,17 +1205,16 @@
 Count);%0A
-%0A
         
@@ -1257,17 +1257,16 @@
 teger(0)
- 
 ;%0A%0A     
@@ -1268,25 +1268,16 @@
         
-JsonWalk.
 walk(doc
@@ -1505,23 +1505,17 @@
 ter.
-getAndI
+i
 ncrement
 ();%0A
@@ -1510,16 +1510,22 @@
 ncrement
+AndGet
 ();%0A    
@@ -1551,16 +1551,17 @@
 (count %3C
+=
  mutatio

Fix reservoir sampling