//
//  Copyright (C) 2015 Google, Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at:
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

#include "service/common/bluetooth/binder/parcel_helpers.h"

#include "service/common/bluetooth/util/address_helper.h"

using android::Parcel;

using bluetooth::AdvertiseData;
using bluetooth::AdvertiseSettings;
using bluetooth::GattIdentifier;
using bluetooth::ScanFilter;
using bluetooth::ScanResult;
using bluetooth::ScanSettings;
using bluetooth::UUID;

namespace ipc {
namespace binder {

// TODO(armansito): The helpers below currently don't match the Java
// definitions. We need to change the AIDL and framework code to comply with the
// new definition and Parcel format provided here.

void WriteAdvertiseDataToParcel(const AdvertiseData& data, Parcel* parcel) {
  CHECK(parcel);
  parcel->writeByteVector(data.data());
  parcel->writeInt32(data.include_device_name());
  parcel->writeInt32(data.include_tx_power_level());
}

std::unique_ptr<AdvertiseData> CreateAdvertiseDataFromParcel(
    const Parcel& parcel) {
  std::unique_ptr<std::vector<uint8_t>> data;
  parcel.readByteVector(&data);
  CHECK(data.get());

  bool include_device_name = parcel.readInt32();
  bool include_tx_power = parcel.readInt32();

  std::unique_ptr<AdvertiseData> adv(new AdvertiseData(*data));
  adv->set_include_device_name(include_device_name);
  adv->set_include_tx_power_level(include_tx_power);

  return adv;
}

void WriteAdvertiseSettingsToParcel(const AdvertiseSettings& settings,
                                    Parcel* parcel) {
  CHECK(parcel);
  parcel->writeInt32(settings.mode());
  parcel->writeInt32(settings.tx_power_level());
  parcel->writeInt32(settings.connectable());
  parcel->writeInt64(settings.timeout().InMilliseconds());
}

std::unique_ptr<AdvertiseSettings> CreateAdvertiseSettingsFromParcel(
    const Parcel& parcel) {
  AdvertiseSettings::Mode mode =
      static_cast<AdvertiseSettings::Mode>(parcel.readInt32());
  AdvertiseSettings::TxPowerLevel tx_power =
      static_cast<AdvertiseSettings::TxPowerLevel>(parcel.readInt32());
  bool connectable = parcel.readInt32();
  base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
      parcel.readInt64());

  return std::unique_ptr<AdvertiseSettings>(
      new AdvertiseSettings(mode, timeout, tx_power, connectable));
}

void WriteUUIDToParcel(const UUID& uuid, android::Parcel* parcel) {
  // The scheme used by android.os.ParcelUuid is to wrote the most significant
  // bits first as one 64-bit integer, followed by the least significant bits in
  // a second 64-bit integer. This is the same as writing the raw-bytes in
  // sequence, but we don't want to assume any host-endianness here. So follow
  // the same scheme and use the same Parcel APIs.
  UUID::UUID128Bit bytes = uuid.GetFullBigEndian();

  uint64_t most_sig_bits =
      ((((uint64_t) bytes[0]) << 56) |
       (((uint64_t) bytes[1]) << 48) |
       (((uint64_t) bytes[2]) << 40) |
       (((uint64_t) bytes[3]) << 32) |
       (((uint64_t) bytes[4]) << 24) |
       (((uint64_t) bytes[5]) << 16) |
       (((uint64_t) bytes[6]) << 8) |
       bytes[7]);

  uint64_t least_sig_bits =
      ((((uint64_t) bytes[8]) << 56) |
       (((uint64_t) bytes[9]) << 48) |
       (((uint64_t) bytes[10]) << 40) |
       (((uint64_t) bytes[11]) << 32) |
       (((uint64_t) bytes[12]) << 24) |
       (((uint64_t) bytes[13]) << 16) |
       (((uint64_t) bytes[14]) << 8) |
       bytes[15]);

  parcel->writeUint64(most_sig_bits);
  parcel->writeUint64(least_sig_bits);
}

std::unique_ptr<UUID> CreateUUIDFromParcel(
    const android::Parcel& parcel) {
  UUID::UUID128Bit bytes;

  uint64_t most_sig_bits = parcel.readUint64();
  uint64_t least_sig_bits = parcel.readUint64();

  bytes[0] = (most_sig_bits >> 56) & 0xFF;
  bytes[1] = (most_sig_bits >> 48) & 0xFF;
  bytes[2] = (most_sig_bits >> 40) & 0xFF;
  bytes[3] = (most_sig_bits >> 32) & 0xFF;
  bytes[4] = (most_sig_bits >> 24) & 0xFF;
  bytes[5] = (most_sig_bits >> 16) & 0xFF;
  bytes[6] = (most_sig_bits >> 8) & 0xFF;
  bytes[7] = most_sig_bits & 0xFF;

  bytes[8] = (least_sig_bits >> 56) & 0xFF;
  bytes[9] = (least_sig_bits >> 48) & 0xFF;
  bytes[10] = (least_sig_bits >> 40) & 0xFF;
  bytes[11] = (least_sig_bits >> 32) & 0xFF;
  bytes[12] = (least_sig_bits >> 24) & 0xFF;
  bytes[13] = (least_sig_bits >> 16) & 0xFF;
  bytes[14] = (least_sig_bits >> 8) & 0xFF;
  bytes[15] = least_sig_bits & 0xFF;

  return std::unique_ptr<UUID>(new UUID(bytes));
}

void WriteGattIdentifierToParcel(
    const GattIdentifier& gatt_id,
    android::Parcel* parcel) {
  parcel->writeCString(gatt_id.device_address().c_str());
  parcel->writeInt32(gatt_id.is_primary());

  WriteUUIDToParcel(gatt_id.service_uuid(), parcel);
  WriteUUIDToParcel(gatt_id.characteristic_uuid(), parcel);
  WriteUUIDToParcel(gatt_id.descriptor_uuid(), parcel);

  parcel->writeInt32(gatt_id.service_instance_id());
  parcel->writeInt32(gatt_id.characteristic_instance_id());
  parcel->writeInt32(gatt_id.descriptor_instance_id());
}

std::unique_ptr<GattIdentifier> CreateGattIdentifierFromParcel(
    const android::Parcel& parcel) {
  std::string device_address = parcel.readCString();
  bool is_primary = parcel.readInt32();

  auto service_uuid = CreateUUIDFromParcel(parcel);
  auto char_uuid = CreateUUIDFromParcel(parcel);
  auto desc_uuid = CreateUUIDFromParcel(parcel);

  int service_id = parcel.readInt32();
  int char_id = parcel.readInt32();
  int desc_id = parcel.readInt32();

  return std::unique_ptr<GattIdentifier>(
      new GattIdentifier(
          device_address, is_primary,
          *service_uuid, *char_uuid, *desc_uuid,
          service_id, char_id, desc_id));
}

void WriteScanFilterToParcel(
    const ScanFilter& filter,
    android::Parcel* parcel) {
  bool has_name = !filter.device_name().empty();
  parcel->writeInt32(has_name ? 1 : 0);
  if (has_name)
    parcel->writeCString(filter.device_name().c_str());

  bool has_address = !filter.device_address().empty();
  parcel->writeInt32(has_address ? 1 : 0);
  if (has_address)
    parcel->writeCString(filter.device_address().c_str());

  parcel->writeInt32(filter.service_uuid() ? 1 : 0);
  if (filter.service_uuid()) {
    WriteUUIDToParcel(*filter.service_uuid(), parcel);
    parcel->writeInt32(filter.service_uuid_mask() ? 1 : 0);
    if (filter.service_uuid_mask())
      WriteUUIDToParcel(*filter.service_uuid_mask(), parcel);
  }

  // TODO(armansito): Support service and manufacturer data.
}

std::unique_ptr<ScanFilter> CreateScanFilterFromParcel(
    const android::Parcel& parcel) {
  std::string device_name;
  if (parcel.readInt32() == 1)
    device_name = parcel.readCString();

  std::string device_address;
  if (parcel.readInt32() == 1)
    device_address = parcel.readCString();

  std::unique_ptr<UUID> service_uuid, service_uuid_mask;
  if (parcel.readInt32() == 1) {
    service_uuid = CreateUUIDFromParcel(parcel);
    if (parcel.readInt32() == 1)
      service_uuid_mask = CreateUUIDFromParcel(parcel);
  }

  // TODO(armansito): Support service and manufacturer data.

  std::unique_ptr<ScanFilter> filter(new ScanFilter());

  filter->set_device_name(device_name);

  if (!filter->SetDeviceAddress(device_address))
    return nullptr;

  if (!service_uuid)
    return filter;

  if (service_uuid_mask)
    filter->SetServiceUuidWithMask(*service_uuid, *service_uuid_mask);
  else
    filter->SetServiceUuid(*service_uuid);

  return filter;
}

void WriteScanSettingsToParcel(
    const ScanSettings& settings,
    android::Parcel* parcel) {
  parcel->writeInt32(settings.mode());
  parcel->writeInt32(settings.callback_type());
  parcel->writeInt32(settings.result_type());
  parcel->writeInt64(settings.report_delay().InMilliseconds());
  parcel->writeInt32(settings.match_mode());
  parcel->writeInt32(settings.match_count_per_filter());
}

std::unique_ptr<ScanSettings> CreateScanSettingsFromParcel(
    const android::Parcel& parcel) {
  ScanSettings::Mode mode =
      static_cast<ScanSettings::Mode>(parcel.readInt32());
  ScanSettings::CallbackType callback_type =
      static_cast<ScanSettings::CallbackType>(parcel.readInt32());
  ScanSettings::ResultType result_type =
      static_cast<ScanSettings::ResultType>(parcel.readInt32());
  base::TimeDelta report_delay = base::TimeDelta::FromMilliseconds(
      parcel.readInt64());
  ScanSettings::MatchMode match_mode =
      static_cast<ScanSettings::MatchMode>(parcel.readInt32());
  ScanSettings::MatchCount match_count_per_filter =
      static_cast<ScanSettings::MatchCount>(parcel.readInt32());

  return std::unique_ptr<ScanSettings>(new ScanSettings(
      mode, callback_type, result_type, report_delay,
      match_mode, match_count_per_filter));
}

void WriteScanResultToParcel(
    const bluetooth::ScanResult& scan_result,
    android::Parcel* parcel) {
  // The Java framework code conditionally inserts 1 or 0 to indicate if the
  // device adress and the scan record fields are present, based on whether the
  // Java object is null. We do something similar here for consistency, although
  // the native definition of ScanResult requires a valid BD_ADDR.
  if (util::IsAddressValid(scan_result.device_address())) {
    parcel->writeInt32(1);
    parcel->writeCString(scan_result.device_address().c_str());
  } else {
    parcel->writeInt32(0);
  }

  parcel->writeByteVector(scan_result.scan_record());
  parcel->writeInt32(scan_result.rssi());
}

std::unique_ptr<bluetooth::ScanResult> CreateScanResultFromParcel(
    const android::Parcel& parcel) {
  std::string device_address;
  if (parcel.readInt32())
    device_address = parcel.readCString();

  std::unique_ptr<std::vector<uint8_t>> scan_record;
  parcel.readByteVector(&scan_record);
  CHECK(scan_record.get());

  int rssi = parcel.readInt32();

  return std::unique_ptr<ScanResult>(new ScanResult(
      device_address, *scan_record, rssi));
}
}  // namespace binder
}  // namespace ipc
