/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.testing;

import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * Shared by {@link ReactScrollViewTestCase} and {@link ReactHorizontalScrollViewTestCase}. See also
 * ScrollViewTestModule.js
 */
public abstract class AbstractScrollViewTestCase extends ReactAppInstrumentationTestCase {

  protected ScrollListenerModule mScrollListenerModule;

  protected static interface ScrollViewTestModule extends JavaScriptModule {
    public void scrollTo(float destX, float destY);
  }

  @Override
  protected void tearDown() throws Exception {
    waitForBridgeAndUIIdle(60000);
    super.tearDown();
  }

  @Override
  protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
    mScrollListenerModule = new ScrollListenerModule();
    return super.createReactInstanceSpecForTest().addNativeModule(mScrollListenerModule);
  }

  // See ScrollViewListenerModule.js
  protected static class ScrollListenerModule extends BaseJavaModule {

    private final ArrayList<Double> mXOffsets = new ArrayList<Double>();
    private final ArrayList<Double> mYOffsets = new ArrayList<Double>();
    private final ArrayList<Integer> mItemsPressed = new ArrayList<Integer>();
    private final Semaphore mScrollSignaler = new Semaphore(0);
    private boolean mScrollBeginDragCalled;
    private boolean mScrollEndDragCalled;

    // Matches ScrollViewListenerModule.js
    @Override
    public String getName() {
      return "ScrollListener";
    }

    @ReactMethod
    public void onScroll(double x, double y) {
      mXOffsets.add(x);
      mYOffsets.add(y);
      mScrollSignaler.release();
    }

    @ReactMethod
    public void onItemPress(int itemNumber) {
      mItemsPressed.add(itemNumber);
    }

    @ReactMethod
    public void onScrollBeginDrag(double x, double y) {
      mScrollBeginDragCalled = true;
    }

    @ReactMethod
    public void onScrollEndDrag(double x, double y) {
      mScrollEndDragCalled = true;
    }

    public void waitForScrollIdle() {
      while (true) {
        try {
          boolean gotScrollSignal = mScrollSignaler.tryAcquire(1000, TimeUnit.MILLISECONDS);
          if (!gotScrollSignal) {
            return;
          }
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
      }
    }

    public ArrayList<Double> getXOffsets() {
      return mXOffsets;
    }

    public ArrayList<Double> getYOffsets() {
      return mYOffsets;
    }

    public ArrayList<Integer> getItemsPressed() {
      return mItemsPressed;
    }

    public boolean dragEventsMatch() {
      return mScrollBeginDragCalled && mScrollEndDragCalled;
    }
  }
}
