// Path: core/src/main/java/com/github/jeluard/stone/helper/Consolidators.java
// public final class Consolidators {
// 
//   private Consolidators() {
//   }
// 
//   /**
//    * Instantiate a {@link Consolidator} from specified {@code type}.
//    * First look for a constructor accepting an int as unique argument then fallback to the default constructor.
//    *
//    * @param type
//    * @param maxSamples
//    * @return a {@link Consolidator} from specified {@code type}
//    */
//   public static Consolidator createConsolidator(final Class<? extends Consolidator> type, final int maxSamples) {
//     Preconditions.checkNotNull(type, "null type");
//     Preconditions2.checkSize(maxSamples);
// 
//     try {
//       //First look for a constructor accepting int as argument
//       try {
//         return type.getConstructor(int.class).newInstance(maxSamples);
//       } catch (NoSuchMethodException e) {
//         if (Loggers.BASE_LOGGER.isLoggable(Level.FINEST)) {
//           Loggers.BASE_LOGGER.log(Level.FINEST, "{0} does not define a constructor accepting int", type.getCanonicalName());
//         }
//       }
// 
//       //If we can't find such fallback to defaut constructor
//       try {
//         final Constructor<? extends Consolidator> defaultConstructor = type.getConstructor();
//         return defaultConstructor.newInstance();
//       } catch (NoSuchMethodException e) {
//         throw new IllegalArgumentException("Failed to find int or default constructor for "+type.getCanonicalName(), e);
//       }
//     } catch (ReflectiveOperationException e) {
//       throw new RuntimeException(e);
//     }
//   }
// 
//   /**
//    * @param consolidatorTypes
//    * @param maxSamples
//    * @return all {@link Consolidator} instances
//    * @see #createConsolidator(java.lang.Class)
//    */
//   public static Consolidator[] createConsolidators(final List<? extends Class<? extends Consolidator>> consolidatorTypes, final int maxSamples) {
//     Preconditions.checkNotNull(consolidatorTypes, "null consolidatorTypes");
//     Preconditions2.checkSize(maxSamples);
// 
//     final Consolidator[] consolidators = new Consolidator[consolidatorTypes.size()];
//     for (final Iterables2.Indexed<? extends Class<? extends Consolidator>> indexed : Iterables2.withIndex(consolidatorTypes)) {
//       consolidators[indexed.index] = createConsolidator(indexed.value, maxSamples);
//     }
//     return consolidators;
//   }
// 
// }
// 
// Path: core/src/main/java/com/github/jeluard/stone/spi/Dispatcher.java
// public abstract class Dispatcher {
// 
//   /**
//    * Intercept {@link Exception} thrown when a {@code value} is published.
//    */
//   public interface ExceptionHandler {
// 
//     /**
//      * Invoked when an {@link Exception} is thrown during accumulation/persistency process.
//      *
//      * @param exception
//      */
//     void onException(Exception exception);
// 
//   }
// 
//   static final Logger LOGGER = Loggers.create("dispatcher");
// 
//   private final ExceptionHandler exceptionHandler;
//   protected static final ExceptionHandler DEFAULT_EXCEPTION_HANDLER = new ExceptionHandler() {
//     @Override
//     public void onException(final Exception exception) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while publishing", exception);
//       }
//     }
//   };
// 
//   /**
//    * @param exceptionHandler 
//    */
//   public Dispatcher(final ExceptionHandler exceptionHandler) {
//     this.exceptionHandler = Preconditions.checkNotNull(exceptionHandler, "null exceptionHandler");
//   }
// 
//   /**
//    * Safely execute {@link ExceptionHandler#onException(java.lang.Exception)} 
//    *
//    * @param exception 
//    */
//   protected final void notifyExceptionHandler(final Exception exception) {
//     try {
//       this.exceptionHandler.onException(exception);
//     } catch (Exception e) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while executing "+ExceptionHandler.class.getSimpleName()+" <"+this.exceptionHandler+">", e);
//       }
//     }
//   }
// 
//   /**
//    * Dispatch {@link Listener}s executions.
//    *
//    * @param previousTimestamp {@linkplain com.github.jeluard.stone.api.TimeSeries#DEFAULT_LATEST_TIMESTAMP} if does not exist
//    * @param currentTimestamp
//    * @param value
//    * @param listeners
//    * @return true if dispatch has been accepted; false if rejected
//    */
//    public abstract boolean dispatch(long previousTimestamp, long currentTimestamp, int value, Listener[] listeners);
// 
// }


import com.github.jeluard.guayaba.base.Preconditions2;
import com.github.jeluard.stone.helper.Consolidators;
import com.github.jeluard.stone.spi.Dispatcher;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
    @Override
    public void onPublication(final long previousTimestamp, final long currentTimestamp, final int value) {
      recordBeginningTimestampIfNeeded(previousTimestamp, currentTimestamp);
      final long currentWindowId = windowId(currentTimestamp);
      final long previousWindowId = windowId(previousTimestamp);
      final boolean newWindow = previousWindowId >= 0 && currentWindowId != previousWindowId;
      //New window, previous timestamp didn't trigger a consolidation (wasn't last slot): trigger it now.
      if (newWindow) {
        if (!isLatestFromWindow(previousTimestamp)) {
          final long previousWindowEnd = windowEnd(previousWindowId);
          generateConsolidatesThenNotify(previousWindowEnd);
        }
      }

      accumulate(currentTimestamp, value);

      //Last slot: trigger a consolidation.
      if (isLatestFromWindow(currentTimestamp)) {
        generateConsolidatesThenNotify(currentTimestamp);
      }
    }
    
  }

  /**
   * @param id
   * @param granularity
   * @param windows
   * @param dispatcher 
   */
// Path: core/src/main/java/com/github/jeluard/stone/helper/Consolidators.java
// public final class Consolidators {
// 
//   private Consolidators() {
//   }
// 
//   /**
//    * Instantiate a {@link Consolidator} from specified {@code type}.
//    * First look for a constructor accepting an int as unique argument then fallback to the default constructor.
//    *
//    * @param type
//    * @param maxSamples
//    * @return a {@link Consolidator} from specified {@code type}
//    */
//   public static Consolidator createConsolidator(final Class<? extends Consolidator> type, final int maxSamples) {
//     Preconditions.checkNotNull(type, "null type");
//     Preconditions2.checkSize(maxSamples);
// 
//     try {
//       //First look for a constructor accepting int as argument
//       try {
//         return type.getConstructor(int.class).newInstance(maxSamples);
//       } catch (NoSuchMethodException e) {
//         if (Loggers.BASE_LOGGER.isLoggable(Level.FINEST)) {
//           Loggers.BASE_LOGGER.log(Level.FINEST, "{0} does not define a constructor accepting int", type.getCanonicalName());
//         }
//       }
// 
//       //If we can't find such fallback to defaut constructor
//       try {
//         final Constructor<? extends Consolidator> defaultConstructor = type.getConstructor();
//         return defaultConstructor.newInstance();
//       } catch (NoSuchMethodException e) {
//         throw new IllegalArgumentException("Failed to find int or default constructor for "+type.getCanonicalName(), e);
//       }
//     } catch (ReflectiveOperationException e) {
//       throw new RuntimeException(e);
//     }
//   }
// 
//   /**
//    * @param consolidatorTypes
//    * @param maxSamples
//    * @return all {@link Consolidator} instances
//    * @see #createConsolidator(java.lang.Class)
//    */
//   public static Consolidator[] createConsolidators(final List<? extends Class<? extends Consolidator>> consolidatorTypes, final int maxSamples) {
//     Preconditions.checkNotNull(consolidatorTypes, "null consolidatorTypes");
//     Preconditions2.checkSize(maxSamples);
// 
//     final Consolidator[] consolidators = new Consolidator[consolidatorTypes.size()];
//     for (final Iterables2.Indexed<? extends Class<? extends Consolidator>> indexed : Iterables2.withIndex(consolidatorTypes)) {
//       consolidators[indexed.index] = createConsolidator(indexed.value, maxSamples);
//     }
//     return consolidators;
//   }
// 
// }
// 
// Path: core/src/main/java/com/github/jeluard/stone/spi/Dispatcher.java
// public abstract class Dispatcher {
// 
//   /**
//    * Intercept {@link Exception} thrown when a {@code value} is published.
//    */
//   public interface ExceptionHandler {
// 
//     /**
//      * Invoked when an {@link Exception} is thrown during accumulation/persistency process.
//      *
//      * @param exception
//      */
//     void onException(Exception exception);
// 
//   }
// 
//   static final Logger LOGGER = Loggers.create("dispatcher");
// 
//   private final ExceptionHandler exceptionHandler;
//   protected static final ExceptionHandler DEFAULT_EXCEPTION_HANDLER = new ExceptionHandler() {
//     @Override
//     public void onException(final Exception exception) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while publishing", exception);
//       }
//     }
//   };
// 
//   /**
//    * @param exceptionHandler 
//    */
//   public Dispatcher(final ExceptionHandler exceptionHandler) {
//     this.exceptionHandler = Preconditions.checkNotNull(exceptionHandler, "null exceptionHandler");
//   }
// 
//   /**
//    * Safely execute {@link ExceptionHandler#onException(java.lang.Exception)} 
//    *
//    * @param exception 
//    */
//   protected final void notifyExceptionHandler(final Exception exception) {
//     try {
//       this.exceptionHandler.onException(exception);
//     } catch (Exception e) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while executing "+ExceptionHandler.class.getSimpleName()+" <"+this.exceptionHandler+">", e);
//       }
//     }
//   }
// 
//   /**
//    * Dispatch {@link Listener}s executions.
//    *
//    * @param previousTimestamp {@linkplain com.github.jeluard.stone.api.TimeSeries#DEFAULT_LATEST_TIMESTAMP} if does not exist
//    * @param currentTimestamp
//    * @param value
//    * @param listeners
//    * @return true if dispatch has been accepted; false if rejected
//    */
//    public abstract boolean dispatch(long previousTimestamp, long currentTimestamp, int value, Listener[] listeners);
// 
// }


// Path: core/src/main/java/com/github/jeluard/stone/api/WindowedTimeSeries.java
import com.github.jeluard.guayaba.base.Preconditions2;
import com.github.jeluard.stone.helper.Consolidators;
import com.github.jeluard.stone.spi.Dispatcher;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

    @Override
    public void onPublication(final long previousTimestamp, final long currentTimestamp, final int value) {
      recordBeginningTimestampIfNeeded(previousTimestamp, currentTimestamp);
      final long currentWindowId = windowId(currentTimestamp);
      final long previousWindowId = windowId(previousTimestamp);
      final boolean newWindow = previousWindowId >= 0 && currentWindowId != previousWindowId;
      //New window, previous timestamp didn't trigger a consolidation (wasn't last slot): trigger it now.
      if (newWindow) {
        if (!isLatestFromWindow(previousTimestamp)) {
          final long previousWindowEnd = windowEnd(previousWindowId);
          generateConsolidatesThenNotify(previousWindowEnd);
        }
      }

      accumulate(currentTimestamp, value);

      //Last slot: trigger a consolidation.
      if (isLatestFromWindow(currentTimestamp)) {
        generateConsolidatesThenNotify(currentTimestamp);
      }
    }
    
  }

  /**
   * @param id
   * @param granularity
   * @param windows
   * @param dispatcher 
   */
  public WindowedTimeSeries(final String id, final int granularity, final List<Window> windows, final Dispatcher dispatcher) throws IOException {