/*
 * Copyright 2014 Aurélien Broszniowski
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.rainfall;

import io.rainfall.configuration.ConcurrencyConfig;
import io.rainfall.configuration.ReportingConfig;
import io.rainfall.statistics.InitStatisticsHolder;
import io.rainfall.statistics.RuntimeStatisticsHolder;
import io.rainfall.statistics.StatisticsThread;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author Aurelien Broszniowski
 */

public class ScenarioRun<E extends Enum<E>> {

  private Scenario  scenario;
  //TODO : is it possible to generify?
  private Map<Class<? extends Configuration>, Configuration> configurations = new ConcurrentHashMap<Class<? extends Configuration>, Configuration>();
  private List<AssertionEvaluator> assertions = new ArrayList<AssertionEvaluator>();
  private List<Execution> executions = null;
  private RuntimeStatisticsHolder<E> statisticsHolder;

  public ScenarioRun(final Scenario scenario) {
    this.scenario = scenario;
    initDefaultConfigurations();
  }

  private void initDefaultConfigurations() {
    this.configurations.put(ConcurrencyConfig.class, new ConcurrencyConfig());
  }

  // Add executions
  public ScenarioRun executed(Execution... executions) throws SyntaxException {
    if (this.executions != null) {
      throw new SyntaxException("Executions are already defined.");
    }
    this.executions = Arrays.asList(executions);
    return this;
  }

  // Add configuration
  public ScenarioRun config(final Configuration... configs) {
    for (Configuration config : configs) {
      this.configurations.put(config.getClass(), config);
    }
    return this;
  }

  // Add assertion
  public ScenarioRun assertion(final Assertion actual, final Assertion expected) {
    this.assertions.add(new AssertionEvaluator(actual, expected));
    return this;
  }

  // Start Scenario run
  public void start() {
    long start = System.currentTimeMillis();

    //TODO : add generics ? cast?
    ReportingConfig reportingConfig = (ReportingConfig)configurations.get(ReportingConfig.class);

    this.statisticsHolder = new RuntimeStatisticsHolder<E>((E[])reportingConfig.getResults());
    initStatistics();

    Timer timer = new Timer();
    StatisticsThread stats = new StatisticsThread(statisticsHolder, reportingConfig);
    timer.scheduleAtFixedRate(stats, 0L, 1000L);

    try {
      for (final Execution execution : executions) {
        execution.execute(statisticsHolder, scenario, configurations, assertions);
      }
    } catch (TestException e) {
      throw new RuntimeException(e);
    }

    stats.cancel();

    long end = System.currentTimeMillis();
    System.out.println("-> Taken:" + TimeUnit.MILLISECONDS.toSeconds(end - start));
  }

  private void initStatistics() {
    try {
      List<Operation> operations = scenario.getOperations();
      for (Operation  operation : operations) {
        operation.exec(new InitStatisticsHolder<E>(this.statisticsHolder), this.configurations, this.assertions);
      }
    } catch (TestException e) {
      throw new RuntimeException(e);
    }
  }

  public Scenario getScenario() {
    return scenario;
  }

  public Configuration getConfiguration(Class configurationClass) {
    return configurations.get(configurationClass);
  }

  public List<AssertionEvaluator> getAssertions() {
    return assertions;
  }

}

@@ -822,32 +822,84 @@
 atisticsHolder;%0A
+import io.rainfall.statistics.StatisticsPeekHolder;%0A
 import io.rainfa
@@ -2596,20 +2596,39 @@
  public 
-void
+StatisticsPeekHolder%3CE%3E
  start()
@@ -3343,16 +3343,81 @@
 ancel();
+%0A    StatisticsPeekHolder%3CE%3E peek = this.statisticsHolder.peek();
 %0A%0A    lo
@@ -3533,24 +3533,41 @@
  - start));%0A
+    return peek;%0A
   %7D%0A%0A  priva

return final stats at the end of the run