/**
 * Copyright (c) 2014-present, Facebook, Inc.
 * All rights reserved.
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.facebook.react.testing;

import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;

/**
 * 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)
        .addJSModule(ScrollViewTestModule.class);
  }

  // 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;
    }
  }
}
