content: /* amodeus - Copyright (c) 2018, ETH Zurich, Institute for Dynamic Systems and Control */
  package org.matsim.amodeus.framework;
  
  import java.io.File;
  import java.io.IOException;
  import java.net.URL;
  import java.util.zip.DataFormatException;
  
  import org.apache.log4j.Logger;
  import org.matsim.amodeus.config.AmodeusModeConfig;
  import org.matsim.amodeus.config.modal.DispatcherConfig;
  import org.matsim.api.core.v01.network.Link;
  import org.matsim.api.core.v01.network.Network;
  import org.matsim.api.core.v01.population.Population;
  import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
  import org.matsim.core.modal.ModalProviders.InstanceGetter;
  import org.matsim.core.config.Config;
  import org.matsim.core.config.ConfigGroup;
  
  import com.google.inject.Singleton;
  import com.google.inject.TypeLiteral;
  
  import amodeus.amodeus.options.ScenarioOptions;
  import amodeus.amodeus.prep.VirtualNetworkPreparer;
  import amodeus.amodeus.traveldata.StaticTravelData;
  import amodeus.amodeus.traveldata.StaticTravelDataCreator;
  import amodeus.amodeus.traveldata.TravelData;
  import amodeus.amodeus.traveldata.TravelDataGet;
  import amodeus.amodeus.traveldata.TravelDataIO;
  import amodeus.amodeus.virtualnetwork.core.VirtualNetwork;
  import amodeus.amodeus.virtualnetwork.core.VirtualNetworkGet;
  import amodeus.amodeus.virtualnetwork.core.VirtualNetworkIO;
  
  /**
   * provides the {@link VirtualNetwork} and {@link TravelData} and therefore
   * {@link VirtualNetworkPreparer} has to be run in the Preparer
   */
  public class VirtualNetworkModeModule extends AbstractDvrpModeModule {
      private final static Logger logger = Logger.getLogger(VirtualNetworkModeModule.class);
  
      private final AmodeusModeConfig modeConfig;
  
      public VirtualNetworkModeModule(AmodeusModeConfig modeConfig) {
          super(modeConfig.getMode());
          this.modeConfig = modeConfig;
      }
  
      @Override
      public void install() {
          installVirtualNetwork();
          installTravelData();
      }
  
      static private VirtualNetwork<Link> provideVirtualNetworkFromScenarioOptions(InstanceGetter getter) {
          try {
              AmodeusModeConfig modeConfig = (AmodeusModeConfig) getter.getModal(AmodeusModeConfig.class);
              ScenarioOptions scenarioOptions = (ScenarioOptions) getter.get(ScenarioOptions.class);
  
              logger.info(
                      String.format("Loading VirtualNetwork for mode '%s' from ScenarioOptions:", modeConfig.getMode()));
              logger.info(String.format("  - creator: ",
                      scenarioOptions.getVirtualNetworkCreator().getClass().getSimpleName()));
              logger.info(String.format("  - name: ", scenarioOptions.getVirtualNetworkName()));
  
              Network network = (Network) getter.getModal(Network.class);
              return VirtualNetworkGet.readDefault(network, scenarioOptions);
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      static private VirtualNetwork<Link> provideVirtualNetworkFromConfig(InstanceGetter getter) {
          try {
              ScenarioOptions scenarioOptions = (ScenarioOptions) getter.get(ScenarioOptions.class);
              Config config = (Config) getter.get(Config.class);
              Population population = (Population) getter.get(Population.class);
  
              AmodeusModeConfig modeConfig = (AmodeusModeConfig) getter.getModal(AmodeusModeConfig.class);
              DispatcherConfig dispatcherConfig = modeConfig.getDispatcherConfig();
              Network network = (Network) getter.getModal(Network.class);
  
              URL virtualNetworkUrl = ConfigGroup.getInputFileURL(config.getContext(),
                      dispatcherConfig.getVirtualNetworkPath());
              File virtualNetworkFile = new File(virtualNetworkUrl.getPath());
  
              if (!virtualNetworkFile.exists() || dispatcherConfig.getRegenerateVirtualNetwork()) {
                  logger.info(String.format("Regenerating VirtualNetwork for mode '%s' at '%s'", modeConfig.getMode(),
                          virtualNetworkFile));
                  logger.info(
                          "Currently we use information from ScenarioOptions for that. Later on this should be moved to a specific config module.");
                  logger.info(String.format("Using VirtualNetworkCreator: %s",
                          scenarioOptions.getVirtualNetworkCreator().getClass().getSimpleName()));
  
                  int numberOfVehicles = modeConfig.getGeneratorConfig().getNumberOfVehicles();
                  VirtualNetwork<Link> virtualNetwork = scenarioOptions.getVirtualNetworkCreator().create(network,
                          population, scenarioOptions, numberOfVehicles, //
                          (int) config.qsim().getEndTime().seconds());
  
                  VirtualNetworkIO.toByte(virtualNetworkFile, virtualNetwork);
              }
  
              logger.info(String.format("Loading VirtualNetwork for operator '%s' from '%s'", modeConfig.getMode(),
                      virtualNetworkFile));
              return VirtualNetworkGet.readFile(network, virtualNetworkFile);
          } catch (IOException | ClassNotFoundException | DataFormatException e) {
              throw new RuntimeException(e);
          }
      }
  
      public void installVirtualNetwork() {
          String virtualNetworkPath = modeConfig.getDispatcherConfig().getVirtualNetworkPath();
  
          if (virtualNetworkPath == null) {
              // If nothing is set in the configuration file, we fall back to ScenarioOptions.
  
              bindModal(new TypeLiteral<VirtualNetwork<Link>>() {
              }).toProvider(modalProvider(VirtualNetworkModeModule::provideVirtualNetworkFromScenarioOptions))
                      .in(Singleton.class);
          } else {
              bindModal(new TypeLiteral<VirtualNetwork<Link>>() {
              }).toProvider(modalProvider(VirtualNetworkModeModule::provideVirtualNetworkFromConfig)).in(Singleton.class);
          }
      }
  
      static private TravelData provideTravelDataFromScenarioOptions(InstanceGetter getter) {
          AmodeusModeConfig modeConfig = (AmodeusModeConfig) getter.getModal(AmodeusModeConfig.class);
          ScenarioOptions scenarioOptions = (ScenarioOptions) getter.get(ScenarioOptions.class);
  
          logger.info(String.format("Loading TravelData for mode '%s' from ScenarioOptions:", modeConfig.getMode()));
          logger.info(String.format("  - name: ", scenarioOptions.getTravelDataName()));
  
          VirtualNetwork<Link> virtualNetwork = (VirtualNetwork<Link>) getter
                  .getModal(new TypeLiteral<VirtualNetwork<Link>>() {
                  });
  
          return TravelDataGet.readStatic(virtualNetwork, scenarioOptions);
      }
  
      static private TravelData provideTravelDataFromConfig(InstanceGetter getter) {
          try {
              AmodeusModeConfig modeConfig = (AmodeusModeConfig) getter.getModal(AmodeusModeConfig.class);
              DispatcherConfig dispatcherConfig = modeConfig.getDispatcherConfig();
  
              VirtualNetwork<Link> virtualNetwork = (VirtualNetwork<Link>) getter
                      .getModal(new TypeLiteral<VirtualNetwork<Link>>() {
                      });
              Network network = (Network) getter.getModal(Network.class);
              Config config = (Config) getter.get(Config.class);
              ScenarioOptions scenarioOptions = (ScenarioOptions) getter.get(ScenarioOptions.class);
              Population population = (Population) getter.get(Population.class);
  
              URL travelDataUrl = ConfigGroup.getInputFileURL(config.getContext(), dispatcherConfig.getTravelDataPath());
              File travelDataFile = new File(travelDataUrl.getPath());
  
              if (!travelDataFile.exists() || dispatcherConfig.getRegenerateTravelData()) {
                  logger.info(String.format("Regenerating TravelData for mode '%s' at '%s'", modeConfig.getMode(),
                          travelDataFile));
                  logger.info(
                          "Currently we use information from ScenarioOptions for that. Later on this should be moved to a specific config module.");
                  logger.info("Using StaticTravelDataCreator");
  
                  File workingDirectory = new File(config.getContext().getPath()).getParentFile();
                  int numberOfVehicles = modeConfig.getGeneratorConfig().getNumberOfVehicles();
                  int interval = scenarioOptions.getdtTravelData();
  
                  StaticTravelData travelData = StaticTravelDataCreator.create(workingDirectory, virtualNetwork, network,
                          population, interval, numberOfVehicles, //
                          (int) config.qsim().getEndTime().seconds());
                  TravelDataIO.writeStatic(travelDataFile, travelData);
              }
  
              logger.info(
                      String.format("Loading TravelData for mode '%s' from '%s'", modeConfig.getMode(), travelDataFile));
              return TravelDataGet.readFile(virtualNetwork, travelDataFile);
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
  
      public void installTravelData() {
          String travelDataPath = modeConfig.getDispatcherConfig().getTravelDataPath();
  
          if (travelDataPath == null) {
              // If nothing is set in the configuration file, we fall back to ScenarioOptions.
  
              bindModal(TravelData.class)
                      .toProvider(modalProvider(VirtualNetworkModeModule::provideTravelDataFromScenarioOptions))
                      .in(Singleton.class);
          } else {
              bindModal(TravelData.class).toProvider(modalProvider(VirtualNetworkModeModule::provideTravelDataFromConfig))
                      .in(Singleton.class);
          }
      }
  }
  