//
//  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/ipc/binder/interface_with_instances_base.h"

#include <base/logging.h>

namespace ipc {
namespace binder {

bool InterfaceWithInstancesBase::RegisterInstanceBase(
    const android::sp<IInterface>& callback,
    bluetooth::BluetoothInstanceFactory* factory) {
  VLOG(2) << __func__;
  CHECK(factory);

  if (!callback.get()) {
    LOG(ERROR) << "Cannot register a NULL callback";
    return false;
  }

  // Store the callback in the pending list. It will get removed later when the
  // stack notifies us asynchronously.
  bluetooth::UUID app_uuid = bluetooth::UUID::GetRandom();
  if (!pending_callbacks_.Register(app_uuid, callback)) {
    LOG(ERROR) << "Failed to store |callback| to map";
    return false;
  }

  // Create a weak pointer and pass that to the callback to prevent an invalid
  // access later. Since this object is managed using Android's StrongPointer
  // (sp) we are using a wp here rather than std::weak_ptr.
  android::wp<InterfaceWithInstancesBase> weak_ptr_to_this(this);

  bluetooth::BluetoothInstanceFactory::RegisterCallback cb = [weak_ptr_to_this](
      bluetooth::BLEStatus status, const bluetooth::UUID& in_uuid,
      std::unique_ptr<bluetooth::BluetoothInstance> instance) {
    // If the weak pointer was invalidated then there is nothing we can do.
    android::sp<InterfaceWithInstancesBase> strong_ptr_to_this =
        weak_ptr_to_this.promote();
    if (!strong_ptr_to_this.get()) {
      VLOG(2) << "InterfaceWithInstancesBase was deleted while instance was"
              << " being registered";
      return;
    }

    strong_ptr_to_this->OnRegisterInstance(status, in_uuid,
                                           std::move(instance));
  };

  if (factory->RegisterInstance(app_uuid, cb)) return true;

  LOG(ERROR) << "Failed to register instance";
  pending_callbacks_.Remove(app_uuid);

  return false;
}

void InterfaceWithInstancesBase::UnregisterInstanceBase(int instance_id) {
  VLOG(2) << __func__;
  std::lock_guard<std::mutex> lock(maps_lock_);

  id_to_cb_.Remove(instance_id);
  id_to_instance_.erase(instance_id);
}

void InterfaceWithInstancesBase::UnregisterAllBase() {
  VLOG(2) << __func__;
  std::lock_guard<std::mutex> lock(maps_lock_);

  id_to_cb_.Clear();
  id_to_instance_.clear();
}

android::sp<IInterface> InterfaceWithInstancesBase::GetCallback(
    int instance_id) {
  return id_to_cb_.Get(instance_id);
}

std::shared_ptr<bluetooth::BluetoothInstance>
InterfaceWithInstancesBase::GetInstance(int instance_id) {
  auto iter = id_to_instance_.find(instance_id);
  if (iter == id_to_instance_.end())
    return std::shared_ptr<bluetooth::BluetoothInstance>();
  return iter->second;
}

void InterfaceWithInstancesBase::OnRegisterInstance(
    bluetooth::BLEStatus status, const bluetooth::UUID& uuid,
    std::unique_ptr<bluetooth::BluetoothInstance> instance) {
  VLOG(2) << __func__ << " - status: " << status;

  // Simply remove the callback from |pending_callbacks_| as it no longer
  // belongs in there.
  sp<IInterface> callback = pending_callbacks_.Remove(uuid);

  // |callback| might be NULL if it was removed from the pending list, e.g. the
  // remote process that owns the callback died.
  if (!callback.get()) {
    VLOG(1) << "Callback was removed before the call to \"RegisterInstance\" "
            << "returned; unregistering instance";
    return;
  }

  if (status != bluetooth::BLE_STATUS_SUCCESS) {
    // The call wasn't successful. Notify the implementation and return.
    LOG(ERROR) << "Failed to register instance: " << status;
    OnRegisterInstanceImpl(status, callback, nullptr);
    return;
  }

  std::lock_guard<std::mutex> lock(maps_lock_);
  int instance_id = instance->GetInstanceId();
  if (!id_to_cb_.Register(instance_id, callback, this)) {
    LOG(ERROR) << "Failed to store callback";
    OnRegisterInstanceImpl(bluetooth::BLE_STATUS_FAILURE, callback, nullptr);
    return;
  }

  VLOG(1) << "Registered BluetoothInstance - ID: " << instance_id;

  auto shared_instance =
      std::shared_ptr<bluetooth::BluetoothInstance>(instance.release());
  id_to_instance_[instance_id] = shared_instance;

  OnRegisterInstanceImpl(status, callback, shared_instance.get());
}

void InterfaceWithInstancesBase::OnRemoteCallbackRemoved(const int& key) {
  VLOG(2) << __func__ << " instance_id: " << key;
  std::lock_guard<std::mutex> lock(maps_lock_);

  // No need to remove from the callback map as the entry should be already
  // removed when this callback gets called.
  id_to_instance_.erase(key);
}

}  // namespace binder
}  // namespace ipc
