package etomica.server.resources.data;

import etomica.data.DataDump;
import etomica.data.DataPumpListener;
import etomica.data.IDataSink;
import etomica.data.IEtomicaDataSource;
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.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 DataSourceIndex index;

    @Inject
    public DataStreamResource(SimulationStore simStore, DataStreamStore dataStore, DataSourceIndex index) {
        this.simStore = simStore;
        this.dataStore = dataStore;
        this.index = index;
    }


    @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
    public List<ConstructionInfo> list(@PathParam("simId") String simId) {
        SimulationModel model = this.simStore.get(UUID.fromString(simId));

        Set<Class> dataSources = index.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());
    }
}

@@ -53,24 +53,17 @@
 ca.data.
-DataDump
+*
 ;%0Aimport
@@ -75,100 +75,27 @@
 ica.
-data.DataPumpListener;%0Aimport etomica.data.IDataSink;%0Aimport etomica.data.IEtomicaDataSource
+meta.ComponentIndex
 ;%0Aim
@@ -688,24 +688,50 @@
 tException;%0A
+import java.util.HashSet;%0A
 import java.
@@ -1069,33 +1069,130 @@
 e final 
-DataSourceIndex i
+ComponentIndex%3CIEtomicaDataSource%3E dataSourceIndex;%0A    private final ComponentIndex%3CDataAccumulator%3E accumulatorI
 ndex;%0A%0A 
@@ -1397,21 +1397,144 @@
 his.
-index = index
+dataSourceIndex = new ComponentIndex%3C%3E(IEtomicaDataSource.class);%0A        accumulatorIndex = new ComponentIndex%3C%3E(DataAccumulator.class)
 ;%0A  
@@ -2419,16 +2419,36 @@
    @GET%0A
+    @Path(%22meters%22)%0A
     publ
@@ -2477,16 +2477,22 @@
 fo%3E list
+Meters
 (@PathPa
@@ -2629,17 +2629,27 @@
 urces = 
-i
+dataSourceI
 ndex.get
@@ -3013,11 +3013,550 @@
 );%0A    %7D
+%0A%0A    @GET%0A    @Path(%22accumulators%22)%0A    public List%3CConstructionInfo%3E listAccumulators(@PathParam(%22simId%22) String simId) %7B%0A        SimulationModel model = this.simStore.get(UUID.fromString(simId));%0A%0A        Set%3CClass%3C? extends DataAccumulator%3E%3E accumulators = accumulatorIndex.getComponentSet();%0A%0A        return accumulators.stream()%0A                .map(cls -%3E Construction.getConstructionInfo(cls, model))%0A                .flatMap(opt -%3E opt.map(Stream::of).orElseGet(Stream::empty))%0A                .collect(Collectors.toList());%0A    %7D
 %0A%7D%0A

Add endpoint for listing accumulators