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

#include "base/basictypes.h"

namespace device {

// An element of a HID report descriptor.
class HidReportDescriptorItem {
 private:
  friend class HidReportDescriptor;

  enum Type {
    kTypeMain = 0,
    kTypeGlobal = 1,
    kTypeLocal = 2,
    kTypeReserved = 3
  };

  enum MainTag {
    kMainTagDefault = 0x00,       // 0000
    kMainTagInput = 0x08,         // 1000
    kMainTagOutput = 0x09,        // 1001
    kMainTagFeature = 0x0B,       // 1011
    kMainTagCollection = 0x0A,    // 1010
    kMainTagEndCollection = 0x0C  // 1100
  };

  enum GlobalTag {
    kGlobalTagUsagePage = 0x00,        // 0000
    kGlobalTagLogicalMinimum = 0x01,   // 0001
    kGlobalTagLogicalMaximum = 0x02,   // 0010
    kGlobalTagPhysicalMinimum = 0x03,  // 0011
    kGlobalTagPhysicalMaximum = 0x04,  // 0100
    kGlobalTagUnitExponent = 0x05,     // 0101
    kGlobalTagUnit = 0x06,             // 0110
    kGlobalTagReportSize = 0x07,       // 0111
    kGlobalTagReportId = 0x08,         // 1000
    kGlobalTagReportCount = 0x09,      // 1001
    kGlobalTagPush = 0x0A,             // 1010
    kGlobalTagPop = 0x0B               // 1011
  };

  enum LocalTag {
    kLocalTagUsage = 0x00,              // 0000
    kLocalTagUsageMinimum = 0x01,       // 0001
    kLocalTagUsageMaximum = 0x02,       // 0010
    kLocalTagDesignatorIndex = 0x03,    // 0011
    kLocalTagDesignatorMinimum = 0x04,  // 0100
    kLocalTagDesignatorMaximum = 0x05,  // 0101
    kLocalTagStringIndex = 0x07,        // 0111
    kLocalTagStringMinimum = 0x08,      // 1000
    kLocalTagStringMaximum = 0x09,      // 1001
    kLocalTagDelimiter = 0x0A           // 1010
  };

  enum ReservedTag {
    kReservedTagLong = 0xF  // 1111
  };

 public:
  enum Tag {
    kTagDefault = kMainTagDefault << 2 | kTypeMain,
    kTagInput = kMainTagInput << 2 | kTypeMain,
    kTagOutput = kMainTagOutput << 2 | kTypeMain,
    kTagFeature = kMainTagFeature << 2 | kTypeMain,
    kTagCollection = kMainTagCollection << 2 | kTypeMain,
    kTagEndCollection = kMainTagEndCollection << 2 | kTypeMain,
    kTagUsagePage = kGlobalTagUsagePage << 2 | kTypeGlobal,
    kTagLogicalMinimum = kGlobalTagLogicalMinimum << 2 | kTypeGlobal,
    kTagLogicalMaximum = kGlobalTagLogicalMaximum << 2 | kTypeGlobal,
    kTagPhysicalMinimum = kGlobalTagPhysicalMinimum << 2 | kTypeGlobal,
    kTagPhysicalMaximum = kGlobalTagPhysicalMaximum << 2 | kTypeGlobal,
    kTagUnitExponent = kGlobalTagUnitExponent << 2 | kTypeGlobal,
    kTagUnit = kGlobalTagUnit << 2 | kTypeGlobal,
    kTagReportSize = kGlobalTagReportSize << 2 | kTypeGlobal,
    kTagReportId = kGlobalTagReportId << 2 | kTypeGlobal,
    kTagReportCount = kGlobalTagReportCount << 2 | kTypeGlobal,
    kTagPush = kGlobalTagPush << 2 | kTypeGlobal,
    kTagPop = kGlobalTagPop << 2 | kTypeGlobal,
    kTagUsage = kLocalTagUsage << 2 | kTypeLocal,
    kTagUsageMinimum = kLocalTagUsageMinimum << 2 | kTypeLocal,
    kTagUsageMaximum = kLocalTagUsageMaximum << 2 | kTypeLocal,
    kTagDesignatorIndex = kLocalTagDesignatorIndex << 2 | kTypeLocal,
    kTagDesignatorMinimum = kLocalTagDesignatorMinimum << 2 | kTypeLocal,
    kTagDesignatorMaximum = kLocalTagDesignatorMaximum << 2 | kTypeLocal,
    kTagStringIndex = kLocalTagStringIndex << 2 | kTypeLocal,
    kTagStringMinimum = kLocalTagStringMinimum << 2 | kTypeLocal,
    kTagStringMaximum = kLocalTagStringMaximum << 2 | kTypeLocal,
    kTagDelimiter = kLocalTagDelimiter << 2 | kTypeLocal,
    kTagLong = kReservedTagLong << 2 | kTypeReserved
  };

  // HID Input/Output/Feature report information.
  // Can be retrieved from GetShortData()
  // when item.tag() == HidReportDescriptorItem::kTagInput
  // or HidReportDescriptorItem::kTagOutput
  // or HidReportDescriptorItem::kTagFeature
  struct ReportInfo {
    uint8_t data_or_constant : 1;
    uint8_t array_or_variable : 1;
    uint8_t absolute_or_relative : 1;
    uint8_t wrap : 1;
    uint8_t linear : 1;
    uint8_t preferred : 1;
    uint8_t null : 1;
    uint8_t reserved_1 : 1;
    uint8_t bit_field_or_buffer : 1;
    uint8_t reserved_2 : 1;
  };

  // HID collection type.
  // Can be retrieved from GetShortData()
  // when item.tag() == HidReportDescriptorItem::kTagCollection
  enum CollectionType {
    kCollectionTypePhysical,
    kCollectionTypeApplication,
    kCollectionTypeLogical,
    kCollectionTypeReport,
    kCollectionTypeNamedArray,
    kCollectionTypeUsageSwitch,
    kCollectionTypeUsageModifier,
    kCollectionTypeReserved,
    kCollectionTypeVendor
  };

 private:
  HidReportDescriptorItem(const uint8_t* bytes,
                          HidReportDescriptorItem* previous);

 public:
  ~HidReportDescriptorItem() {}

  // Previous element in report descriptor.
  // Owned by descriptor instance.
  HidReportDescriptorItem* previous() const {
    return previous_;
  };
  // Next element in report descriptor.
  // Owned by descriptor instance.
  HidReportDescriptorItem* next() const {
    return next_;
  };
  // Parent element in report descriptor.
  // Owned by descriptor instance.
  // Can be NULL.
  HidReportDescriptorItem* parent() const {
    return parent_;
  };
  // Level in Parent-Children relationship tree.
  // 0 for top-level items (parent()==NULL).
  // 1 if parent() is top-level.
  // 2 if parent() has a top-level parent. Etc.
  size_t GetDepth() const;
  Tag tag() const { return tag_; }
  // Returns true for a long item, false otherwise.
  bool IsLong() const;
  // Raw data of a short item.
  // Not valid for a long item.
  uint32_t GetShortData() const;

  static CollectionType GetCollectionTypeFromValue(uint32_t value);

 private:
  size_t GetHeaderSize() const;
  size_t payload_size() const { return payload_size_; }
  size_t GetSize() const;

  HidReportDescriptorItem* previous_;
  HidReportDescriptorItem* next_;
  HidReportDescriptorItem* parent_;
  Tag tag_;
  uint32_t shortData_;
  size_t payload_size_;
};

}  // namespace device

#endif  // DEVICE_HID_HID_REPORT_DESCRIPTOR_ITEM_H_
