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

#ifndef CONTENT_BROWSER_DEVICE_SENSORS_SENSOR_MANAGER_ANDROID_H_
#define CONTENT_BROWSER_DEVICE_SENSORS_SENSOR_MANAGER_ANDROID_H_

#include <memory>

#include "base/android/scoped_java_ref.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "content/browser/device_sensors/device_sensors_consts.h"
#include "content/common/content_export.h"
#include "device/sensors/public/cpp/device_light_hardware_buffer.h"
#include "device/sensors/public/cpp/device_motion_hardware_buffer.h"
#include "device/sensors/public/cpp/device_orientation_hardware_buffer.h"

namespace base {
template <typename T>
struct DefaultSingletonTraits;
}

namespace content {

// Android implementation of Device {Motion|Orientation|Light} API.
//
// Android's SensorManager has a push API, so when Got*() methods are called
// by the system the browser process puts the received data into a shared
// memory buffer, which is read by the renderer processes.
class CONTENT_EXPORT SensorManagerAndroid {
 public:
  // Must be called at startup, before GetInstance().
  static bool Register(JNIEnv* env);

  // Should be called only on the UI thread.
  static SensorManagerAndroid* GetInstance();

  // Called from Java via JNI.
  void GotLight(JNIEnv*,
                const base::android::JavaParamRef<jobject>&,
                double value);
  void GotOrientation(JNIEnv*,
                      const base::android::JavaParamRef<jobject>&,
                      double alpha,
                      double beta,
                      double gamma);
  void GotOrientationAbsolute(JNIEnv*,
                              const base::android::JavaParamRef<jobject>&,
                              double alpha,
                              double beta,
                              double gamma);
  void GotAcceleration(JNIEnv*,
                       const base::android::JavaParamRef<jobject>&,
                       double x,
                       double y,
                       double z);
  void GotAccelerationIncludingGravity(
      JNIEnv*,
      const base::android::JavaParamRef<jobject>&,
      double x,
      double y,
      double z);
  void GotRotationRate(JNIEnv*,
                       const base::android::JavaParamRef<jobject>&,
                       double alpha,
                       double beta,
                       double gamma);

  // Shared memory related methods.
  void StartFetchingDeviceLightData(DeviceLightHardwareBuffer* buffer);
  void StopFetchingDeviceLightData();

  void StartFetchingDeviceMotionData(DeviceMotionHardwareBuffer* buffer);
  void StopFetchingDeviceMotionData();

  void StartFetchingDeviceOrientationData(
      DeviceOrientationHardwareBuffer* buffer);
  void StopFetchingDeviceOrientationData();

  void StartFetchingDeviceOrientationAbsoluteData(
      DeviceOrientationHardwareBuffer* buffer);
  void StopFetchingDeviceOrientationAbsoluteData();

  void Shutdown();

  // A Java counterpart will be generated for this enum.
  // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content.browser
  // When adding new constants don't modify the order as they are used for UMA.
  enum OrientationSensorType {
    NOT_AVAILABLE = 0,
    ROTATION_VECTOR = 1,
    ACCELEROMETER_MAGNETIC = 2,
    GAME_ROTATION_VECTOR = 3,
    ORIENTATION_SENSOR_MAX = 4,
  };

 protected:
  SensorManagerAndroid();
  virtual ~SensorManagerAndroid();

  // Starts listening to the sensors corresponding to the consumer_type.
  // Returns true if the registration with sensors was successful.
  virtual bool Start(ConsumerType consumer_type);
  // Stops listening to the sensors corresponding to the consumer_type.
  virtual void Stop(ConsumerType consumer_type);
  // Returns currently used sensor type for device orientation.
  virtual OrientationSensorType GetOrientationSensorTypeUsed();
  // Returns the number of active sensors corresponding to
  // ConsumerType.DEVICE_MOTION.
  virtual int GetNumberActiveDeviceMotionSensors();

 private:
  friend struct base::DefaultSingletonTraits<SensorManagerAndroid>;

  enum {
    RECEIVED_MOTION_DATA_ACCELERATION = 0,
    RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY = 1,
    RECEIVED_MOTION_DATA_ROTATION_RATE = 2,
    RECEIVED_MOTION_DATA_MAX = 3,
  };

  void SetLightBufferValue(double lux);

  void CheckMotionBufferReadyToRead();
  void SetMotionBufferReadyStatus(bool ready);
  void ClearInternalMotionBuffers();

  // The Java provider of sensors info.
  base::android::ScopedJavaGlobalRef<jobject> device_sensors_;
  int number_active_device_motion_sensors_;
  int received_motion_data_[RECEIVED_MOTION_DATA_MAX];

  // Cached pointers to buffers, owned by DataFetcherSharedMemoryBase.
  DeviceLightHardwareBuffer* device_light_buffer_;
  DeviceMotionHardwareBuffer* device_motion_buffer_;
  DeviceOrientationHardwareBuffer* device_orientation_buffer_;
  DeviceOrientationHardwareBuffer* device_orientation_absolute_buffer_;

  bool motion_buffer_initialized_;
  bool orientation_buffer_initialized_;
  bool orientation_absolute_buffer_initialized_;

  base::Lock light_buffer_lock_;
  base::Lock motion_buffer_lock_;
  base::Lock orientation_buffer_lock_;
  base::Lock orientation_absolute_buffer_lock_;

  bool is_shutdown_;
  base::ThreadChecker thread_checker_;

  DISALLOW_COPY_AND_ASSIGN(SensorManagerAndroid);
};

}  // namespace content

#endif  // CONTENT_BROWSER_DEVICE_SENSORS_SENSOR_MANAGER_ANDROID_H_
