// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/android/input_hint_checker.h"

#include "base/base_jni/InputHintChecker_jni.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/no_destructor.h"
#include "base/time/time.h"

namespace base::android {

namespace {

bool g_input_hint_enabled;
base::TimeDelta g_poll_interval;
InputHintChecker* g_test_instance;

}  // namespace

// Whether to fetch the input hint from the system. When disabled, pretends
// that no input is ever queued.
BASE_EXPORT
BASE_FEATURE(kYieldWithInputHint,
             "YieldWithInputHint",
             base::FEATURE_DISABLED_BY_DEFAULT);

// Min time delta between checks for the input hint. Must be a smaller than
// time to produce a frame, but a bit longer than the time it takes to retrieve
// the hint.
const base::FeatureParam<int> kPollIntervalMillisParam{&kYieldWithInputHint,
                                                       "poll_interval_ms", 3};

// static
void InputHintChecker::InitializeFeatures() {
  bool is_enabled = base::FeatureList::IsEnabled(kYieldWithInputHint);
  g_input_hint_enabled = is_enabled;
  if (is_enabled) {
    g_poll_interval = Milliseconds(kPollIntervalMillisParam.Get());
  }
}

bool InputHintChecker::HasInputImplWithThrottling() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  auto now = base::TimeTicks::Now();
  if (last_checked_.is_null() || (now - last_checked_) >= g_poll_interval) {
    last_checked_ = now;
    // TODO(pasko): Implement fetching the hint from the system.
    return false;
  }
  return false;
}

void InputHintChecker::SetView(JNIEnv* env, jobject root_view) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  view_ = JavaObjectWeakGlobalRef(env, root_view);
}

// static
bool InputHintChecker::HasInput() {
  if (!g_input_hint_enabled) {
    return false;
  }
  return GetInstance().HasInputImplWithThrottling();
}

InputHintChecker& InputHintChecker::GetInstance() {
  static NoDestructor<InputHintChecker> checker;
  if (g_test_instance) {
    return *g_test_instance;
  }
  return *checker.get();
}

InputHintChecker::ScopedOverrideInstance::ScopedOverrideInstance(
    InputHintChecker* checker) {
  g_test_instance = checker;
}

InputHintChecker::ScopedOverrideInstance::~ScopedOverrideInstance() {
  g_test_instance = nullptr;
}

void JNI_InputHintChecker_SetView(_JNIEnv* env,
                                  const JavaParamRef<jobject>& v) {
  InputHintChecker::GetInstance().SetView(env, v.obj());
}

}  // namespace base::android
