package com.conveyal.r5.analyst.scenario;

import com.beust.jcommander.internal.Lists;
import com.conveyal.r5.transit.TransportNetwork;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;

/**
 * A scenario is an ordered sequence of modifications that will be applied non-destructively on top of a baseline graph.
 */
public class Scenario implements Serializable {

    private static final Logger LOG = LoggerFactory.getLogger(Scenario.class);

    public final int id;

    public String description = "no description provided";

    /**
     * If non-null, only modifications whose activeInVariant list contain this string will be applied.
     * If null, all modifications will be applied.
     */
    public String useVariant;

    public List<Modification> modifications = Lists.newArrayList();

    public Scenario (@JsonProperty("id") int id) {
        this.id = id;
    }

    /**
     * @return a copy of the supplied network with the modifications in this scenario non-destructively applied.
     */
    public TransportNetwork applyToTransportNetwork (TransportNetwork originalNetwork) {
        LOG.info("Applying scenario {}", this);
        useVariant = null;
        // This is a kludge to select a variant by looking at the modification comments TODO replace this kludge
        for (Modification modification : modifications) {
            if (modification.comment != null && modification.comment.startsWith("useVariant: ")) {
                useVariant = modification.comment.split(" ")[1];
            }
        }
        List<Modification> filteredModifications = modifications.stream()
                .filter(m -> m.isActiveInVariant(useVariant)).collect(Collectors.toList());
        LOG.info("Variant '{}' selected, with {} modifications active out of {} total.",
                useVariant, filteredModifications.size(), modifications.size());
        TransportNetwork copiedNetwork = originalNetwork.scenarioCopy();
        LOG.info("Resolving modifications against TransportNetwork and sanity checking.");
        // Check all the parameters before applying any modifications.
        // FIXME might some parameters may become valid/invalid because of previous modifications in the list?
        boolean errorsInScenario = false;
        for (Modification modification : filteredModifications) {
            boolean errorsInModification = modification.resolve(originalNetwork);
            if (errorsInModification) {
                LOG.error("Errors were detected in a scenario modification of type {}:", modification.getType());
                for (String warning : modification.warnings) {
                    LOG.error(warning);
                }
                errorsInScenario = true;
            }
        }
        if (errorsInScenario) {
            throw new RuntimeException("Errors were found in the Scenario, bailing out.");
        }
        // Apply each modification in turn to the same extensible copy of the TransitNetwork.
        LOG.info("Applying modifications to TransportNetwork.");
        for (Modification modification : filteredModifications) {
            LOG.info("Applying modification of type {}", modification.getType());
            boolean errors = modification.apply(copiedNetwork);
            if (errors) {
                LOG.error("Error while applying modification {}", modification);
                throw new RuntimeException("Errors occured while applying the Scenario to the TransportNetwork, bailing out.");
            }
        }
        // FIXME can we do this once after all modifications are applied, or do we need to do it after every mod?
        copiedNetwork.transitLayer.rebuildTransientIndexes();
        return copiedNetwork;
    }

}

@@ -1024,24 +1024,96 @@
 %0A    %7D%0A%0A
+    private static final boolean VERIFY_BASE_NETWORK_UNCHANGED = true;%0A%0A
     /**%0A
      * @
@@ -1100,24 +1100,24 @@
 e;%0A%0A    /**%0A
-
      * @retu
@@ -1374,296 +1374,144 @@
     
-useVariant = null;%0A        // This is a kludge to select a variant by looking at the modification comments TODO replace this kludge%0A        for (Modification modification : modifications) %7B%0A            if (modification.comment != null && modification.comment.startsWith(%22useVariant: %22)) %7B
+long baseNetworkChecksum = 0;%0A        if (VERIFY_BASE_NETWORK_UNCHANGED) %7B%0A            baseNetworkChecksum = originalNetwork.checksum();
 %0A   
@@ -1507,32 +1507,34 @@
 ksum();%0A        
+%7D%0A
         useVaria
@@ -1542,67 +1542,13 @@
 t = 
-modification.comment.split(%22 %22)%5B1%5D;%0A            %7D%0A        %7D
+null;
 %0A   
@@ -3691,24 +3691,405 @@
 tIndexes();%0A
+        if (VERIFY_BASE_NETWORK_UNCHANGED) %7B%0A            if (originalNetwork.checksum() != baseNetworkChecksum) %7B%0A                LOG.error(%22Applying a scenario mutated the base transportation network. THIS IS A BUG.%22);%0A            %7D else %7B%0A                LOG.info(%22Applying the scenario left the base transport network unchanged with high probability.%22);%0A            %7D%0A        %7D%0A
         retu

check that base network is unchanged by scenario application.