package etomica.server.resources.data;

import etomica.data.*;
import etomica.meta.ComponentIndex;
import etomica.meta.DataSourceIndex;
import etomica.meta.SimulationModel;
import etomica.server.core.construction.Construction;
import etomica.server.dao.DataStreamStore;
import etomica.server.dao.SimulationStore;
import etomica.server.representations.ConstructionInfo;
import etomica.server.representations.ConstructionParams;
import org.apache.commons.beanutils.PropertyUtils;

import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Path("/simulations/{simId}/data")
@Produces(MediaType.APPLICATION_JSON)
public class DataStreamResource {
    private final SimulationStore simStore;
    private final DataStreamStore dataStore;
    private final ComponentIndex<IEtomicaDataSource> dataSourceIndex;
    private final ComponentIndex<DataAccumulator> accumulatorIndex;

    @Inject
    public DataStreamResource(SimulationStore simStore, DataStreamStore dataStore, DataSourceIndex index) {
        this.simStore = simStore;
        this.dataStore = dataStore;
        this.dataSourceIndex = new ComponentIndex<>(IEtomicaDataSource.class);
        accumulatorIndex = new ComponentIndex<>(DataAccumulator.class);
    }


    @POST
    public UUID createDataStream(@NotNull ConstructionParams constructionParams, @PathParam("simId") String simId) {
        SimulationModel simModel = simStore.get(UUID.fromString(simId));
        UUID dataId = UUID.randomUUID();

        try {
            IEtomicaDataSource meter = Construction.<IEtomicaDataSource>createInstance(constructionParams, simModel)
                    .orElseThrow(() -> new WebApplicationException("Unable to create meter"));

            DataDump dump = new DataDump();
            DataPumpListener pump = new DataPumpListener(meter, dump, 100);
            this.dataStore.put(dataId, new DataStreamStore.DataPlumbing(pump, dump));
            return dataId;

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new WebApplicationException("Unable to create meter");
        }
    }

    @GET
    @Path("meters")
    public List<ConstructionInfo> listMeters(@PathParam("simId") String simId) {
        SimulationModel model = this.simStore.get(UUID.fromString(simId));

        Set<Class> dataSources = dataSourceIndex.getComponentSet().stream()
                .filter(cls -> !IDataSink.class.isAssignableFrom(cls)).collect(Collectors.toSet());

        return dataSources.stream()
                .map(cls -> Construction.getConstructionInfo(cls, model))
                .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
                .collect(Collectors.toList());
    }

    @GET
    @Path("accumulators")
    public List<ConstructionInfo> listAccumulators(@PathParam("simId") String simId) {
        SimulationModel model = this.simStore.get(UUID.fromString(simId));

        Set<Class<? extends DataAccumulator>> accumulators = accumulatorIndex.getComponentSet();

        return accumulators.stream()
                .map(cls -> Construction.getConstructionInfo(cls, model))
                .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
                .collect(Collectors.toList());
    }
}

@@ -259,24 +259,87 @@
 treamStore;%0A
+import static etomica.server.dao.DataStreamStore.DataPlumbing;%0A
 import etomi
@@ -365,24 +365,24 @@
 ationStore;%0A
-
 import etomi
@@ -486,59 +486,8 @@
 ams;
-%0Aimport org.apache.commons.beanutils.PropertyUtils;
 %0A%0Aim
@@ -618,124 +618,8 @@
 pe;%0A
-import java.lang.reflect.Constructor;%0Aimport java.lang.reflect.InvocationTargetException;%0Aimport java.util.HashSet;%0A
 impo
@@ -2298,32 +2298,941 @@
        %7D%0A    %7D%0A%0A
+    @POST%0A    @Path(%22%7BdataId%7D/accumulator%22)%0A    public void addAccumulator(@NotNull ConstructionParams params, @PathParam(%22simId%22) String simID, @PathParam(%22dataId%22) String dataId) %7B%0A        SimulationModel model = simStore.get(UUID.fromString(simID));%0A%0A        try %7B%0A            DataAccumulator accumulator = Construction.%3CDataAccumulator%3EcreateInstance(params, model)%0A                    .orElseThrow(() -%3E new WebApplicationException(%22Unable to create accumulator%22));%0A%0A            DataPlumbing plumbing = this.dataStore.get(UUID.fromString(dataId));%0A            DataPump pump = plumbing.getPump();%0A            DataDump dump = plumbing.getDump();%0A            pump.setDataSink(accumulator);%0A            accumulator.addDataSink(dump);%0A        %7D catch (ClassNotFoundException e) %7B%0A            e.printStackTrace();%0A            throw new WebApplicationException(%22Unable to create accumulator%22);%0A        %7D%0A    %7D%0A%0A
     @GET%0A    @Pa
@@ -4297,32 +4297,32 @@
 Stream::empty))%0A
-
                 
@@ -4357,11 +4357,273 @@
 );%0A    %7D
+%0A%0A    @GET%0A    @Path(%22%7BdataId%7D%22)%0A    public IEtomicaDataInfo getDataInfo(@PathParam(%22simId%22) String simId, @PathParam(%22dataId%22) String dataId) %7B%0A        //TODO: serializers%0A        return this.dataStore.get(UUID.fromString(dataId)).getDump().getDataInfo();%0A    %7D
 %0A%7D%0A

Add endpoint to add accumulator