package arez.integration;

import arez.SpyEventHandler;
import arez.spy.ActionCompletedEvent;
import arez.spy.ActionStartedEvent;
import arez.spy.ComponentCreateCompletedEvent;
import arez.spy.ComponentCreateStartedEvent;
import arez.spy.ComponentDisposeCompletedEvent;
import arez.spy.ComponentDisposeStartedEvent;
import arez.spy.ComputeCompletedEvent;
import arez.spy.ComputeStartedEvent;
import arez.spy.ComputedValueActivatedEvent;
import arez.spy.ComputedValueCreatedEvent;
import arez.spy.ComputedValueDeactivatedEvent;
import arez.spy.ComputedValueDisposedEvent;
import arez.spy.ObservableChangedEvent;
import arez.spy.ObservableCreatedEvent;
import arez.spy.ObservableDisposedEvent;
import arez.spy.ObserverCreatedEvent;
import arez.spy.ObserverDisposedEvent;
import arez.spy.ObserverErrorEvent;
import arez.spy.ReactionCompletedEvent;
import arez.spy.ReactionScheduledEvent;
import arez.spy.ReactionStartedEvent;
import arez.spy.SerializableEvent;
import arez.spy.TransactionCompletedEvent;
import arez.spy.TransactionStartedEvent;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.stream.JsonGenerator;

/**
 * A recorder used to record results of test run.
 */
public final class SpyEventRecorder
  implements SpyEventHandler
{
  private final Map<Class<?>, Consumer<SerializableEvent>> _processors = new HashMap<>();
  private final JsonArrayBuilder _events = Json.createArrayBuilder();
  private final boolean _keepValue;

  public SpyEventRecorder()
  {
    this( true );
  }

  public SpyEventRecorder( final boolean keepValue )
  {
    _keepValue = keepValue;
    register( ComponentCreateStartedEvent.class );
    register( ComponentCreateCompletedEvent.class );
    register( ComponentDisposeStartedEvent.class );
    register( ComponentDisposeCompletedEvent.class );
    register( ObserverCreatedEvent.class );
    register( ObserverDisposedEvent.class );
    register( ObserverErrorEvent.class );
    register( ObservableCreatedEvent.class );
    register( ObservableDisposedEvent.class );
    register( ObservableChangedEvent.class );
    register( ComputedValueActivatedEvent.class );
    register( ComputedValueDeactivatedEvent.class );
    register( ComputedValueCreatedEvent.class );
    register( ComputedValueDisposedEvent.class );
    register( ReactionStartedEvent.class );
    register( ReactionScheduledEvent.class );
    register( ReactionCompletedEvent.class );
    register( TransactionStartedEvent.class );
    register( TransactionCompletedEvent.class );
    register( ComputeStartedEvent.class );
    register( ComputeCompletedEvent.class );
    register( ActionStartedEvent.class );
    register( ActionCompletedEvent.class );
  }

  private <T extends SerializableEvent> void register( @Nonnull final Class<T> type )
  {
    _processors.put( type, this::log );
  }

  private void log( @Nonnull final SerializableEvent event )
  {
    final HashMap<String, Object> map = new HashMap<>();
    event.toMap( map );
    map.remove( "duration" );
    if ( !_keepValue )
    {
      map.remove( "value" );
    }
    final HashMap<String, Object> output = new HashMap<>();
    for ( final Map.Entry<String, Object> entry : map.entrySet() )
    {
      final String key = entry.getKey();
      final Object value = entry.getValue();
      if ( key.equals( "value" ) && value instanceof Collection )
      {
        // Useful for debugging repositories
        output.put( key, ( (Collection) value ).size() + " items" );
      }
      else if ( null != value && value.getClass().isArray() )
      {
        final ArrayList<Object> v = new ArrayList<>();
        for ( int i = 0, end = Array.getLength( value ); i < end; i++ )
        {
          v.add( Array.get( value, i ) );
        }
        output.put( key, v );
      }
      else
      {
        output.put( key, value );
      }
    }
    _events.add( Json.createObjectBuilder( output ) );
  }

  @Override
  public final void onSpyEvent( @Nonnull final Object event )
  {
    final Consumer<SerializableEvent> processor = _processors.get( event.getClass() );
    if ( null != processor )
    {
      processor.accept( (SerializableEvent) event );
    }
  }

  @Nonnull
  public JsonArrayBuilder getEvents()
  {
    return _events;
  }

  @Nonnull
  public String eventsAsString()
  {
    final Map<String, Object> properties = new HashMap<>( 1 );
    properties.put( JsonGenerator.PRETTY_PRINTING, true );
    final StringWriter writer = new StringWriter();
    Json.createWriterFactory( properties ).createWriter( writer ).write( getEvents().build() );
    writer.flush();
    return writer.toString();
  }
}

@@ -69,971 +69,20 @@
 spy.
-ActionCompletedEvent;%0Aimport arez.spy.ActionStartedEvent;%0Aimport arez.spy.ComponentCreateCompletedEvent;%0Aimport arez.spy.ComponentCreateStartedEvent;%0Aimport arez.spy.ComponentDisposeCompletedEvent;%0Aimport arez.spy.ComponentDisposeStartedEvent;%0Aimport arez.spy.ComputeCompletedEvent;%0Aimport arez.spy.ComputeStartedEvent;%0Aimport arez.spy.ComputedValueActivatedEvent;%0Aimport arez.spy.ComputedValueCreatedEvent;%0Aimport arez.spy.ComputedValueDeactivatedEvent;%0Aimport arez.spy.ComputedValueDisposedEvent;%0Aimport arez.spy.ObservableChangedEvent;%0Aimport arez.spy.ObservableCreatedEvent;%0Aimport arez.spy.ObservableDisposedEvent;%0Aimport arez.spy.ObserverCreatedEvent;%0Aimport arez.spy.ObserverDisposedEvent;%0Aimport arez.spy.ObserverErrorEvent;%0Aimport arez.spy.ReactionCompletedEvent;%0Aimport arez.spy.ReactionScheduledEvent;%0Aimport arez.spy.ReactionStartedEvent;%0Aimport arez.spy.SerializableEvent;%0Aimport arez.spy.TransactionCompletedEvent;%0Aimport arez.spy.TransactionStarted
+Serializable
 Even
@@ -254,44 +254,8 @@
 ap;%0A
-import java.util.function.Consumer;%0A
 impo
@@ -513,98 +513,8 @@
 r%0A%7B%0A
-  private final Map%3CClass%3C?%3E, Consumer%3CSerializableEvent%3E%3E _processors = new HashMap%3C%3E();%0A
   pr
@@ -760,1232 +760,8 @@
 ue;%0A
-    register( ComponentCreateStartedEvent.class );%0A    register( ComponentCreateCompletedEvent.class );%0A    register( ComponentDisposeStartedEvent.class );%0A    register( ComponentDisposeCompletedEvent.class );%0A    register( ObserverCreatedEvent.class );%0A    register( ObserverDisposedEvent.class );%0A    register( ObserverErrorEvent.class );%0A    register( ObservableCreatedEvent.class );%0A    register( ObservableDisposedEvent.class );%0A    register( ObservableChangedEvent.class );%0A    register( ComputedValueActivatedEvent.class );%0A    register( ComputedValueDeactivatedEvent.class );%0A    register( ComputedValueCreatedEvent.class );%0A    register( ComputedValueDisposedEvent.class );%0A    register( ReactionStartedEvent.class );%0A    register( ReactionScheduledEvent.class );%0A    register( ReactionCompletedEvent.class );%0A    register( TransactionStartedEvent.class );%0A    register( TransactionCompletedEvent.class );%0A    register( ComputeStartedEvent.class );%0A    register( ComputeCompletedEvent.class );%0A    register( ActionStartedEvent.class );%0A    register( ActionCompletedEvent.class );%0A  %7D%0A%0A  private %3CT extends SerializableEvent%3E void register( @Nonnull final Class%3CT%3E type )%0A  %7B%0A    _processors.put( type, this::log );%0A
   %7D%0A
@@ -1926,148 +1926,65 @@
     
-final Consumer%3CSerializableEvent%3E processor = _processors.get( event.getClass() );%0A    if ( null != processor )%0A    %7B%0A      processor.accept
+if ( event instanceof SerializableEvent )%0A    %7B%0A      log
 ( (S

Remove unused infrastructure