/**
 * Copyright (c) 2015-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.devsupport;

import java.util.Locale;

import android.annotation.TargetApi;
import android.view.Choreographer;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.facebook.common.logging.FLog;
import com.facebook.react.R;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.modules.debug.FpsDebugFrameCallback;

/**
 * View that automatically monitors and displays the current app frame rate. Also logs the current
 * FPS to logcat while active.
 *
 * NB: Requires API 16 for use of FpsDebugFrameCallback.
 */
@TargetApi(16)
public class FpsView extends FrameLayout {

  private static final int UPDATE_INTERVAL_MS = 500;

  private final TextView mTextView;
  private final FpsDebugFrameCallback mFrameCallback;
  private final FPSMonitorRunnable mFPSMonitorRunnable;

  public FpsView(ReactContext reactContext) {
    super(reactContext);
    inflate(reactContext, R.layout.fps_view, this);
    mTextView = (TextView) findViewById(R.id.fps_text);
    mFrameCallback = new FpsDebugFrameCallback(Choreographer.getInstance(), reactContext);
    mFPSMonitorRunnable = new FPSMonitorRunnable();
    setCurrentFPS(0, 0, 0, 0);
  }

  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    mFrameCallback.reset();
    mFrameCallback.start();
    mFPSMonitorRunnable.start();
  }

  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    mFrameCallback.stop();
    mFPSMonitorRunnable.stop();
  }

  private void setCurrentFPS(double currentFPS, double currentJSFPS, int droppedUIFrames, int total4PlusFrameStutters) {
    String fpsString = String.format(
        Locale.US,
        "UI: %.1f fps\n%d dropped so far\n%d stutters (4+) so far\nJS: %.1f fps",
        currentFPS,
        droppedUIFrames,
        total4PlusFrameStutters,
        currentJSFPS);
    mTextView.setText(fpsString);
    FLog.d(ReactConstants.TAG, fpsString);
  }

  /**
   * Timer that runs every UPDATE_INTERVAL_MS ms and updates the currently displayed FPS.
   */
  private class FPSMonitorRunnable implements Runnable {

    private boolean mShouldStop = false;
    private int mTotalFramesDropped = 0;
    private int mTotal4PlusFrameStutters = 0;

    @Override
    public void run() {
      if (mShouldStop) {
        return;
      }
      mTotalFramesDropped += mFrameCallback.getExpectedNumFrames() - mFrameCallback.getNumFrames();
      mTotal4PlusFrameStutters += mFrameCallback.get4PlusFrameStutters();
      setCurrentFPS(mFrameCallback.getFPS(), mFrameCallback.getJSFPS(), mTotalFramesDropped, mTotal4PlusFrameStutters);
      mFrameCallback.reset();

      postDelayed(this, UPDATE_INTERVAL_MS);
    }

    public void start() {
      mShouldStop = false;
      post(this);
    }

    public void stop() {
      mShouldStop = true;
    }
  }
}
