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

#include "device/gamepad/public/cpp/gamepad_mojom_traits.h"

#include <cstdint>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/notreached.h"

namespace mojo {

// static
void StructTraits<
    device::mojom::GamepadQuaternionDataView,
    device::GamepadQuaternion>::SetToNull(device::GamepadQuaternion* out) {
  *out = {};
  out->not_null = false;
}

// static
bool StructTraits<device::mojom::GamepadQuaternionDataView,
                  device::GamepadQuaternion>::
    Read(device::mojom::GamepadQuaternionDataView data,
         device::GamepadQuaternion* out) {
  out->not_null = true;
  out->x = data.x();
  out->y = data.y();
  out->z = data.z();
  out->w = data.w();
  return true;
}

// static
void StructTraits<device::mojom::GamepadVectorDataView,
                  device::GamepadVector>::SetToNull(device::GamepadVector*
                                                        out) {
  *out = {};
  out->not_null = false;
}

// static
bool StructTraits<device::mojom::GamepadVectorDataView, device::GamepadVector>::
    Read(device::mojom::GamepadVectorDataView data,
         device::GamepadVector* out) {
  out->not_null = true;
  out->x = data.x();
  out->y = data.y();
  out->z = data.z();
  return true;
}

// static
device::mojom::GamepadButtonType EnumTraits<
    device::mojom::GamepadButtonType,
    device::GamepadButtonType>::ToMojom(device::GamepadButtonType input) {
  switch (input) {
    case device::GamepadButtonType::kNonStandard:
      return device::mojom::GamepadButtonType::GamepadButtonTypeNonStandard;
    case device::GamepadButtonType::kStandard:
      return device::mojom::GamepadButtonType::GamepadButtonTypeStandard;
    case device::GamepadButtonType::kTrackpad:
      return device::mojom::GamepadButtonType::GamepadButtonTypeTrackpad;
  }

  NOTREACHED();
}

// static
device::GamepadButtonType
EnumTraits<device::mojom::GamepadButtonType, device::GamepadButtonType>::
    FromMojom(device::mojom::GamepadButtonType input) {
  switch (input) {
    case device::mojom::GamepadButtonType::GamepadButtonTypeNonStandard:
      return device::GamepadButtonType::kNonStandard;
    case device::mojom::GamepadButtonType::GamepadButtonTypeStandard:
      return device::GamepadButtonType::kStandard;
    case device::mojom::GamepadButtonType::GamepadButtonTypeTrackpad:
      return device::GamepadButtonType::kTrackpad;
  }

  NOTREACHED();
}

// static
bool StructTraits<device::mojom::GamepadButtonDataView, device::GamepadButton>::
    Read(device::mojom::GamepadButtonDataView data,
         device::GamepadButton* out) {
  out->pressed = data.pressed();
  out->touched = data.touched();
  out->value = data.value();
  return data.ReadType(&out->type);
}

// static
device::mojom::GamepadHapticActuatorType
EnumTraits<device::mojom::GamepadHapticActuatorType,
           device::GamepadHapticActuatorType>::
    ToMojom(device::GamepadHapticActuatorType input) {
  switch (input) {
    case device::GamepadHapticActuatorType::kVibration:
      return device::mojom::GamepadHapticActuatorType::
          GamepadHapticActuatorTypeVibration;
    case device::GamepadHapticActuatorType::kDualRumble:
      return device::mojom::GamepadHapticActuatorType::
          GamepadHapticActuatorTypeDualRumble;
    case device::GamepadHapticActuatorType::kTriggerRumble:
      return device::mojom::GamepadHapticActuatorType::
          GamepadHapticActuatorTypeTriggerRumble;
  }

  NOTREACHED();
}

// static
device::GamepadHapticActuatorType
EnumTraits<device::mojom::GamepadHapticActuatorType,
           device::GamepadHapticActuatorType>::
    FromMojom(device::mojom::GamepadHapticActuatorType input) {
  switch (input) {
    case device::mojom::GamepadHapticActuatorType::
        GamepadHapticActuatorTypeVibration:
      return device::GamepadHapticActuatorType::kVibration;
    case device::mojom::GamepadHapticActuatorType::
        GamepadHapticActuatorTypeDualRumble:
      return device::GamepadHapticActuatorType::kDualRumble;
    case device::mojom::GamepadHapticActuatorType::
        GamepadHapticActuatorTypeTriggerRumble:
      return device::GamepadHapticActuatorType::kTriggerRumble;
  }

  NOTREACHED();
}

// static
void StructTraits<device::mojom::GamepadHapticActuatorDataView,
                  device::GamepadHapticActuator>::
    SetToNull(device::GamepadHapticActuator* out) {
  *out = {};
  out->not_null = false;
}

// static
bool StructTraits<device::mojom::GamepadHapticActuatorDataView,
                  device::GamepadHapticActuator>::
    Read(device::mojom::GamepadHapticActuatorDataView data,
         device::GamepadHapticActuator* out) {
  out->not_null = true;
  if (!data.ReadType(&out->type))
    return false;
  return true;
}

// static
bool StructTraits<device::mojom::GamepadTouchDataView, device::GamepadTouch>::
    Read(device::mojom::GamepadTouchDataView data, device::GamepadTouch* out) {
  out->touch_id = data.touch_id();
  out->surface_id = data.surface_id();
  out->x = data.x();
  out->y = data.y();
  out->has_surface_dimensions = data.has_surface_dimensions();
  out->surface_width = data.surface_width();
  out->surface_height = data.surface_height();

  return true;
}

// static
void StructTraits<device::mojom::GamepadPoseDataView,
                  device::GamepadPose>::SetToNull(device::GamepadPose* out) {
  *out = {};
  out->not_null = false;
}

// static
bool StructTraits<device::mojom::GamepadPoseDataView, device::GamepadPose>::
    Read(device::mojom::GamepadPoseDataView data, device::GamepadPose* out) {
  out->not_null = true;
  if (!data.ReadOrientation(&out->orientation)) {
    return false;
  }
  out->has_orientation = out->orientation.not_null;

  if (!data.ReadPosition(&out->position)) {
    return false;
  }
  out->has_position = out->position.not_null;

  if (!data.ReadAngularVelocity(&out->angular_velocity)) {
    return false;
  }
  if (!data.ReadLinearVelocity(&out->linear_velocity)) {
    return false;
  }
  if (!data.ReadAngularAcceleration(&out->angular_acceleration)) {
    return false;
  }
  if (!data.ReadLinearAcceleration(&out->linear_acceleration)) {
    return false;
  }
  return true;
}

// static
device::mojom::GamepadMapping
EnumTraits<device::mojom::GamepadMapping, device::GamepadMapping>::ToMojom(
    device::GamepadMapping input) {
  switch (input) {
    case device::GamepadMapping::kNone:
      return device::mojom::GamepadMapping::GamepadMappingNone;
    case device::GamepadMapping::kStandard:
      return device::mojom::GamepadMapping::GamepadMappingStandard;
    case device::GamepadMapping::kXrStandard:
      return device::mojom::GamepadMapping::GamepadMappingXRStandard;
  }

  NOTREACHED();
}

// static
device::GamepadMapping
EnumTraits<device::mojom::GamepadMapping, device::GamepadMapping>::FromMojom(
    device::mojom::GamepadMapping input) {
  switch (input) {
    case device::mojom::GamepadMapping::GamepadMappingNone:
      return device::GamepadMapping::kNone;
    case device::mojom::GamepadMapping::GamepadMappingStandard:
      return device::GamepadMapping::kStandard;
    case device::mojom::GamepadMapping::GamepadMappingXRStandard:
      return device::GamepadMapping::kXrStandard;
  }

  NOTREACHED();
}

// static
device::mojom::GamepadHand
EnumTraits<device::mojom::GamepadHand, device::GamepadHand>::ToMojom(
    device::GamepadHand input) {
  switch (input) {
    case device::GamepadHand::kNone:
      return device::mojom::GamepadHand::GamepadHandNone;
    case device::GamepadHand::kLeft:
      return device::mojom::GamepadHand::GamepadHandLeft;
    case device::GamepadHand::kRight:
      return device::mojom::GamepadHand::GamepadHandRight;
  }

  NOTREACHED();
}

// static
device::GamepadHand
EnumTraits<device::mojom::GamepadHand, device::GamepadHand>::FromMojom(
    device::mojom::GamepadHand input) {
  switch (input) {
    case device::mojom::GamepadHand::GamepadHandNone:
      return device::GamepadHand::kNone;
    case device::mojom::GamepadHand::GamepadHandLeft:
      return device::GamepadHand::kLeft;
    case device::mojom::GamepadHand::GamepadHandRight:
      return device::GamepadHand::kRight;
  }

  NOTREACHED();
}

// static
base::span<const uint16_t>
StructTraits<device::mojom::GamepadDataView, device::Gamepad>::id(
    const device::Gamepad& r) {
  size_t id_length = 0;
  while (id_length < device::Gamepad::kIdLengthCap && r.id[id_length] != 0) {
    id_length++;
  }
  return base::span(r.id).first(id_length);
}

// static
bool StructTraits<device::mojom::GamepadDataView, device::Gamepad>::Read(
    device::mojom::GamepadDataView data,
    device::Gamepad* out) {
  out->connected = data.connected();

  base::span<uint16_t> id(out->id);
  if (!data.ReadId(&id)) {
    return false;
  }

  out->timestamp = data.timestamp();

  base::span<double> axes(out->axes);
  if (!data.ReadAxes(&axes)) {
    return false;
  }
  out->axes_length = axes.size();

  std::vector<device::GamepadButton> buttons_vector;
  if (!data.ReadButtons(&buttons_vector)) {
    return false;
  }
  out->buttons_length = buttons_vector.size();
  base::span(out->buttons)
      .first(buttons_vector.size())
      .copy_from(buttons_vector);

  if (!data.ReadVibrationActuator(&out->vibration_actuator))
    return false;

  if (!data.ReadMapping(&out->mapping)) {
    return false;
  }

  if (!data.ReadPose(&out->pose)) {
    return false;
  }

  std::vector<device::GamepadTouch> touch_events_vector;
  if (!data.ReadTouchEvents(&touch_events_vector)) {
    return false;
  }
  out->touch_events_length = touch_events_vector.size();
  base::span(out->touch_events)
      .first(touch_events_vector.size())
      .copy_from(touch_events_vector);

  device::GamepadHand hand;
  if (!data.ReadHand(&hand)) {
    return false;
  }
  out->hand = hand;

  out->display_id = data.display_id();

  return true;
}

}  // namespace mojo
