// 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 UI_EVENTS_OZONE_EVDEV_LIBGESTURES_GLUE_GESTURE_PROPERTY_PROVIDER_H_
#define UI_EVENTS_OZONE_EVDEV_LIBGESTURES_GLUE_GESTURE_PROPERTY_PROVIDER_H_

#include <gestures/gestures.h>
#include <libevdev/libevdev.h>
#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>

#include "base/macros.h"
#include "ui/events/ozone/evdev/event_device_info.h"
#include "ui/events/ozone/evdev/events_ozone_evdev_export.h"

namespace ui {

class GesturesPropFunctionsWrapper;
class GestureInterpreterLibevdevCros;

// Not for public consumption, so we wrap it in namespace internal.
namespace internal {
struct GestureDevicePropertyData;
class MatchCriteria;
struct ConfigurationSection;
}

// A struct holding device properties that are useful when interacting with
// the gestures lib.
struct GestureDeviceProperties {
  int area_left = 0;
  int area_right = 0;
  int area_top = 0;
  int area_bottom = 0;
  int res_y = 0;
  int res_x = 0;
  int orientation_minimum = 0;
  int orientation_maximum = 0;
  GesturesPropBool raw_passthrough;
  GesturesPropBool dump_debug_log;
};

// Provide the interface to access the CrOS gesture library properties.
// It maintains the property data for all input devices that runs with
// the gesture library.
//
// The class also parses the configuration files on the system to
// initialize the specified property values. The configuration files are
// currently in the xorg-conf format so that they can be shared with non-Ozone
// builds.
class EVENTS_OZONE_EVDEV_EXPORT GesturePropertyProvider {
 public:
  // Property types.
  enum PropertyType {
    PT_INT,
    PT_SHORT,
    PT_BOOL,
    PT_STRING,
    PT_REAL,
  };

  // Pointer that leads to the device info.
  typedef Evdev* DevicePtr;

  // The device ids are only maintained by EventFactoryEvdev to identify the
  // input devices and are not to be confused with the Evdev input node id, for
  // example.
  typedef int DeviceId;

  GesturePropertyProvider();
  ~GesturePropertyProvider();

  // Get a list of device ids that matches a device type. Return true if the
  // list is not empty. |device_ids| can be NULL. Existing data in |device_ids|
  // won't be deleted.
  bool GetDeviceIdsByType(const EventDeviceType type,
                          std::vector<DeviceId>* device_ids);

  // Check if a device id matches a device type. Return true if it matches.
  // Return false if it doesn't match or if it doesn't use
  // the GesturePropertyProvider.
  bool IsDeviceIdOfType(const DeviceId device_id, const EventDeviceType type);

  // Get the GesturesProp object. Returns NULL if not found.
  //
  // The user may use the object returned to set/get the property value in the
  // gesture library's memory. Note that the values in preferences are not
  // synced with the ones here in realtime - they are only applied from the
  // preference side in a single way once appropriate (e.g., when the user
  // clicked "OK").
  GesturesProp* GetProperty(const DeviceId device_id, const std::string& name);

  // Get the names of all properties of one device. Mostly used for the logging
  // purpose.
  std::vector<std::string> GetPropertyNamesById(const DeviceId device_id);

  // Get the (Evdev) device name. Mostly used for the logging purpose.
  std::string GetDeviceNameById(const DeviceId device_id);

 private:
  friend class GesturesPropFunctionsWrapper;

  // Mapping table from a device id to its device pointer.
  typedef std::map<DeviceId, DevicePtr> DeviceMap;

  // Register a device. Setup data-structures and the device's default
  // properties.
  void RegisterDevice(const DeviceId id, const DevicePtr device);

  // Unregister a device. Remove all of its properties being tracked.
  void UnregisterDevice(const DeviceId id);

  // Called by functions in GesturesPropFunctionsWrapper to manipulate
  // properties. Note these functions do not new/delete the GesturesProp
  // pointers. It is caller's responsibility to manage them.
  void AddProperty(const DeviceId device_id,
                   const std::string& name,
                   std::unique_ptr<GesturesProp> property);
  void DeleteProperty(const DeviceId device_id, const std::string& name);

  // Check if a property exists for a device. Return if it is found.
  GesturesProp* FindProperty(const DeviceId device_id, const std::string& name);

  // Get the default value of a property based on the configuration files.
  GesturesProp* GetDefaultProperty(const DeviceId device_id,
                                   const std::string& name);

  // The device configuration files are parsed and stored in the memory upon
  // Chrome starts. The default property values are then applied to each device
  // when it is attached/detected.
  void LoadDeviceConfigurations();

  // Parse a xorg-conf file. We ignore all sections other than InputClass.
  // Check the xorg-conf spec for more infomation about its format.
  void ParseXorgConfFile(const std::string& content);

  // Create a match criteria.
  std::unique_ptr<internal::MatchCriteria> CreateMatchCriteria(
      const std::string& match_type,
      const std::string& arg);

  // Create a property that comes from the conf files.
  std::unique_ptr<GesturesProp> CreateDefaultProperty(const std::string& name,
                                                      const std::string& value);

  // Setup default property values for a newly found device.
  void SetupDefaultProperties(const DeviceId device_id, const DevicePtr device);

  // Map from device ids to device pointers.
  DeviceMap device_map_;

  // Mapping table from a device id to its property data.
  // GestureDevicePropertyData contains both properties in use and default
  // properties whose values will be applied upon the device attachment.
  std::unordered_map<DeviceId,
                     std::unique_ptr<internal::GestureDevicePropertyData>>
      device_data_map_;

  // A vector of parsed sections in configuration files. Owns MatchCriterias,
  // GesturesProps and ConfigurationSections in it.
  std::vector<std::unique_ptr<internal::ConfigurationSection>> configurations_;

  DISALLOW_COPY_AND_ASSIGN(GesturePropertyProvider);
};

// Wrapper of GesturesProp related functions. We group them together so that we
// can friend them all at once.
class GesturesPropFunctionsWrapper {
 public:
  // Property provider interface implementation.
  //
  // These functions will create a GesturesProp object that can link back to the
  // memory that holds the real value, which is often declared in the gesture
  // lib.
  static GesturesProp* CreateInt(void* device_data,
                                 const char* name,
                                 int* value,
                                 size_t count,
                                 const int* init);
  static GesturesProp* CreateShort(void* device_data,
                                   const char* name,
                                   short* value,
                                   size_t count,
                                   const short* init);
  static GesturesProp* CreateBool(void* device_data,
                                  const char* name,
                                  GesturesPropBool* value,
                                  size_t count,
                                  const GesturesPropBool* init);

  // String GestureProps needs special care due to the use of const char* in the
  // gesture lib. Its argument list is also different from numeric properties'.
  static GesturesProp* CreateString(void* device_data,
                                    const char* name,
                                    const char** value,
                                    const char* init);

  static GesturesProp* CreateReal(void* device_data,
                                  const char* name,
                                  double* value,
                                  size_t count,
                                  const double* init);

  // Set the handlers to call when a property is accessed.
  static void RegisterHandlers(void* device_data,
                               GesturesProp* property,
                               void* handler_data,
                               GesturesPropGetHandler get,
                               GesturesPropSetHandler set);

  // Free a property.
  static void Free(void* device_data, GesturesProp* property);

  // Initialize hardware-related device properties which will be used in the
  // gesture lib.
  static bool InitializeDeviceProperties(void* device_data,
                                         GestureDeviceProperties* properties);

  // Unregister device from the gesture property provider.
  static void UnregisterDevice(void* device_data);

 private:
  // Property helper functions.
  // Core template function for creating GestureProps. Used by numerical types.
  template <typename T, class PROPTYPE>
  static GesturesProp* CreateProperty(void* device_data,
                                      const char* name,
                                      T* value,
                                      size_t count,
                                      const T* init);

  // Do things that should happen BEFORE we create the property.
  static bool PreCreateProperty(void* device_data,
                                const char* name,
                                GesturesProp** default_property);

  // Do things that should happen AFTER we create the property.
  static void PostCreateProperty(void* device_data,
                                 const char* name,
                                 std::unique_ptr<GesturesProp> property);

  // Some other utility functions used in InitializeDeviceProperties.
  static GesturesProp* CreateIntSingle(void* device_data,
                                       const char* name,
                                       int* value,
                                       int init);
  static GesturesProp* CreateBoolSingle(void* device_data,
                                        const char* name,
                                        GesturesPropBool* value,
                                        GesturesPropBool init);

  // Routines to extract information from the GestureInterpreterLibevdevCros
  // pointer.
  static GesturePropertyProvider* GetPropertyProvider(void* device_data);
  static GesturePropertyProvider::DevicePtr GetDevicePointer(void* device_data);
  static GesturePropertyProvider::DeviceId GetDeviceId(void* device_data);
};

extern const GesturesPropProvider kGesturePropProvider;

}  // namspace ui

// GesturesProp logging function.
std::ostream& operator<<(std::ostream& os, const GesturesProp& prop);

// Implementation of GesturesProp declared in gestures.h
//
// libgestures requires that this be in the top level namespace. We have also
// to put it in the header file so that other classes will be able to use the
// gesture property objects.
struct GesturesProp {
 public:
  typedef ui::GesturePropertyProvider::PropertyType PropertyType;

  GesturesProp(const std::string& name,
               const PropertyType type,
               const size_t count);
  virtual ~GesturesProp() {}

  // Variant-ish interfaces for accessing the property value. Each type of
  // property should override the corresponding interfaces for it.
  virtual std::vector<int> GetIntValue() const;
  virtual bool SetIntValue(const std::vector<int>& value);
  virtual std::vector<int16_t> GetShortValue() const;
  virtual bool SetShortValue(const std::vector<int16_t>& value);
  virtual std::vector<bool> GetBoolValue() const;
  virtual bool SetBoolValue(const std::vector<bool>& value);
  virtual std::string GetStringValue() const;
  virtual bool SetStringValue(const std::string& value);
  virtual std::vector<double> GetDoubleValue() const;
  virtual bool SetDoubleValue(const std::vector<double>& value);

  // Set property access handlers.
  void SetHandlers(GesturesPropGetHandler get,
                   GesturesPropSetHandler set,
                   void* data);

  // Accessors.
  const std::string& name() const { return name_; }
  PropertyType type() const { return type_; }
  size_t count() const { return count_; }
  virtual bool IsReadOnly() const = 0;

 protected:
  // Functions to be called when the property value was accessed.
  void OnGet() const;
  void OnSet() const;

 private:
  // For logging purpose.
  friend std::ostream& operator<<(std::ostream& os,
                                  const GesturesProp& property);

  // Interfaces for getting internal pointers and stuff.
  virtual const char** GetStringWritebackPtr() const;
  virtual bool IsAllocated() const;

  // Property name, type and number of elements.
  std::string name_;
  PropertyType type_;
  size_t count_;

  // Handler function pointers and the data to be passed to them when the
  // property is accessed.
  GesturesPropGetHandler get_ = nullptr;
  GesturesPropSetHandler set_ = nullptr;
  void* handler_data_ = nullptr;

  DISALLOW_COPY_AND_ASSIGN(GesturesProp);
};

#endif  // UI_EVENTS_OZONE_EVDEV_LIBGESTURES_GLUE_GESTURE_PROPERTY_PROVIDER_H_
