// Copyright 2016 The Android Open Source Project
//
// 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.
//

// Implementation of brillo_audio_manager.h.

#include "include/brillo_audio_manager.h"

#include <memory>
#include <stdlib.h>

#include "audio_service_callback.h"
#include "brillo_audio_client.h"
#include "brillo_audio_client_helpers.h"
#include "brillo_audio_device_info_def.h"
#include "brillo_audio_device_info_internal.h"

using brillo::AudioServiceCallback;
using brillo::BrilloAudioClient;
using brillo::BrilloAudioClientHelpers;

struct BAudioManager {
  std::weak_ptr<BrilloAudioClient> client_;
};

BAudioManager* BAudioManager_new() {
  auto client = BrilloAudioClient::GetClientInstance();
  if (!client.lock())
    return nullptr;
  BAudioManager* bam = new BAudioManager;
  bam->client_ = client;
  return bam;
}

int BAudioManager_getDevices(
    const BAudioManager* brillo_audio_manager, int flag,
    BAudioDeviceInfo* device_array[], unsigned int size,
    unsigned int* num_devices) {
  if (!brillo_audio_manager || !num_devices ||
      (flag != GET_DEVICES_INPUTS && flag != GET_DEVICES_OUTPUTS))
    return EINVAL;
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    *num_devices = 0;
    return ECONNABORTED;
  }
  std::vector<int> devices;
  auto rc = client->GetDevices(flag, devices);
  if (rc) {
    *num_devices = 0;
    return rc;
  }
  unsigned int num_elems = (devices.size() < size) ? devices.size() : size;
  for (size_t i = 0; i < num_elems; i++) {
    device_array[i] = new BAudioDeviceInfo;
    device_array[i]->internal_ = std::unique_ptr<BAudioDeviceInfoInternal>(
        BAudioDeviceInfoInternal::CreateFromAudioDevicesT(devices[i]));
  }
  *num_devices = devices.size();
  return 0;
}

int BAudioManager_setInputDevice(const BAudioManager* brillo_audio_manager,
                                 const BAudioDeviceInfo* device) {
  if (!brillo_audio_manager || !device)
    return EINVAL;
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    return ECONNABORTED;
  }
  return client->SetDevice(AUDIO_POLICY_FORCE_FOR_RECORD,
                           device->internal_->GetConfig());
}

int BAudioManager_setOutputDevice(
    const BAudioManager* brillo_audio_manager, const BAudioDeviceInfo* device,
    BAudioUsage usage) {
  if (!brillo_audio_manager || !device)
    return EINVAL;
  auto client = brillo_audio_manager->client_.lock();
  if (!client)
    return ECONNABORTED;
  return client->SetDevice(BrilloAudioClientHelpers::GetForceUse(usage),
                           device->internal_->GetConfig());
}

int BAudioManager_getMaxVolumeSteps(const BAudioManager* brillo_audio_manager,
                                    BAudioUsage usage,
                                    int* max_steps) {
  if (!brillo_audio_manager || !max_steps)
    return EINVAL;
  auto client = brillo_audio_manager->client_.lock();
  if (!client)
    return ECONNABORTED;
  return client->GetMaxVolumeSteps(usage, max_steps);
}

int BAudioManager_setMaxVolumeSteps(const BAudioManager* brillo_audio_manager,
                                    BAudioUsage usage,
                                    int max_steps) {
  if (!brillo_audio_manager || max_steps < 0 || max_steps > 100)
    return EINVAL;
  auto client = brillo_audio_manager->client_.lock();
  if (!client)
    return ECONNABORTED;
  return client->SetMaxVolumeSteps(usage, max_steps);
}

int BAudioManager_setVolumeIndex(const BAudioManager* brillo_audio_manager,
                                 BAudioUsage usage,
                                 const BAudioDeviceInfo* device,
                                 int index) {
  if (!brillo_audio_manager || !device) {
    return EINVAL;
  }
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    return ECONNABORTED;
  }
  return client->SetVolumeIndex(
      usage, device->internal_->GetAudioDevicesT(), index);
}

int BAudioManager_getVolumeIndex(const BAudioManager* brillo_audio_manager,
                                 BAudioUsage usage,
                                 const BAudioDeviceInfo* device,
                                 int* index) {
  if (!brillo_audio_manager || !device || !index) {
    return EINVAL;
  }
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    return ECONNABORTED;
  }
  return client->GetVolumeIndex(
      usage, device->internal_->GetAudioDevicesT(), index);
}

int BAudioManager_getVolumeControlUsage(
    const BAudioManager* brillo_audio_manager, BAudioUsage* usage) {
  if (!brillo_audio_manager || !usage) {
    return EINVAL;
  }
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    return ECONNABORTED;
  }
  return client->GetVolumeControlStream(usage);
}

int BAudioManager_setVolumeControlUsage(
    const BAudioManager* brillo_audio_manager, BAudioUsage usage) {
  if (!brillo_audio_manager) {
    return EINVAL;
  }
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    return ECONNABORTED;
  }
  return client->SetVolumeControlStream(usage);
}

int BAudioManager_incrementVolume(const BAudioManager* brillo_audio_manager) {
  if (!brillo_audio_manager) {
    return EINVAL;
  }
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    return ECONNABORTED;
  }
  return client->IncrementVolume();
}

int BAudioManager_decrementVolume(const BAudioManager* brillo_audio_manager) {
  if (!brillo_audio_manager) {
    return EINVAL;
  }
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    return ECONNABORTED;
  }
  return client->DecrementVolume();
}

int BAudioManager_registerAudioCallback(
    const BAudioManager* brillo_audio_manager, const BAudioCallback* callback,
    void* user_data, int* callback_id) {
  if (!brillo_audio_manager || !callback || !callback_id)
    return EINVAL;
  auto client = brillo_audio_manager->client_.lock();
  if (!client) {
    *callback_id = 0;
    return ECONNABORTED;
  }
  // This copies the BAudioCallback into AudioServiceCallback so the
  // BAudioCallback can be safely deleted.
  return client->RegisterAudioCallback(
      new AudioServiceCallback(callback, user_data), callback_id);
}

int BAudioManager_unregisterAudioCallback(
    const BAudioManager* brillo_audio_manager, int callback_id) {
  if (!brillo_audio_manager)
    return EINVAL;
  auto client = brillo_audio_manager->client_.lock();
  if (!client)
    return ECONNABORTED;
  return client->UnregisterAudioCallback(callback_id);
}

int BAudioManager_delete(BAudioManager* brillo_audio_manager) {
  if (!brillo_audio_manager)
    return EINVAL;
  delete brillo_audio_manager;
  return 0;
}