/*
 * Copyright 2016-present The Material Motion Authors. All Rights Reserved.
 *
 * 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 com.google.android.material.motion;

import android.support.v4.util.SimpleArrayMap;
import android.util.Property;
import android.view.View;

import com.google.android.indefinite.observable.IndefiniteObservable.Subscription;

import java.util.ArrayList;
import java.util.List;

/**
 * A MotionRuntime writes the output of streams to properties and observes their overall state.
 */
public final class MotionRuntime {

  private final List<Subscription> subscriptions = new ArrayList<>();
  private final SimpleArrayMap<View, ReactiveView> cachedReactiveViews = new SimpleArrayMap<>();

  /**
   * Subscribes to the stream, writes its output to the given property, and observes its state.
   */
  public <O, T> void write(
    MotionObservable<T> stream, final O target, final Property<O, T> property) {
    write(stream, ReactiveProperty.of(target, property));
  }

  /**
   * Subscribes to the stream, writes its output to the given property, and observes its state.
   */
  public <T> void write(MotionObservable<T> stream, final ReactiveProperty<T> property) {
    subscriptions.add(stream.subscribe(new MotionObserver<T>() {

      @Override
      public void next(T value) {
        property.write(value);
      }
    }));
  }

  @SafeVarargs
  public final <O, T> void addInteraction(
    Interaction<O, T> interaction, O target, Operation<T, T>... constraints) {
    addInteraction(interaction, target, new ConstraintApplicator<>(constraints));
  }

  public final <O, T> void addInteraction(
    Interaction<O, T> interaction, O target, ConstraintApplicator<T> constraints) {
    interaction.apply(this, target, constraints);
  }

  /**
   * Returns a reactive version of the given object and caches the returned result for future access.
   */
  public ReactiveView get(View view) {
    ReactiveView reactiveView = cachedReactiveViews.get(view);
    if (reactiveView == null) {
      reactiveView = new ReactiveView(view);
      cachedReactiveViews.put(view, reactiveView);
    }

    return reactiveView;
  }
}

@@ -1226,16 +1226,119 @@
 Map%3C%3E();
+%0A  private final SimpleArrayMap%3CObject, List%3CInteraction%3E%3E cachedInteractions = new SimpleArrayMap%3C%3E();
 %0A%0A  /**%0A
@@ -2379,16 +2379,251 @@
 aints);%0A
+    List%3CInteraction%3E interactions = cachedInteractions.get(target);%0A    if (interactions == null) %7B%0A      interactions = new ArrayList%3C%3E();%0A      cachedInteractions.put(target, interactions);%0A    %7D%0A%0A    interactions.add(interaction);%0A
   %7D%0A%0A  /
@@ -2966,24 +2966,24 @@
 ew);%0A    %7D%0A%0A
-
     return r
@@ -2998,11 +2998,370 @@
 iew;%0A  %7D
+%0A%0A  public %3CI extends Interaction%3E List%3CI%3E interactions(View view, Class%3CI%3E klass) %7B%0A    List%3CInteraction%3E interactions = cachedInteractions.get(view);%0A    List%3CI%3E filteredInteractions = new ArrayList%3C%3E();%0A%0A    for (Interaction i: interactions) %7B%0A      if(klass.isInstance(i))%0A        filteredInteractions.add((I)i);%0A    %7D%0A    return filteredInteractions;%0A  %7D
 %0A%7D%0A

Implement MotionRuntime.interactions() API (#75)