/******************************************************************************
 *
 *  Copyright (C) 2014 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 <signal.h>
#include <time.h>

#include "base.h"
#include "support/hal.h"

static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data);
static int acquire_wake_lock(const char *lock_name);
static int release_wake_lock(const char *lock_name);

static const bluetooth_device_t *bt_device;

static bt_os_callouts_t callouts = {
  sizeof(bt_os_callouts_t),
  set_wake_alarm,
  acquire_wake_lock,
  release_wake_lock,
};

bool hal_open(bt_callbacks_t *callbacks) {
  hw_module_t *module;
  if (hw_get_module(BT_STACK_MODULE_ID, (hw_module_t const **)&module)) {
    return false;
  }

  hw_device_t *device;
  if (module->methods->open(module, BT_STACK_MODULE_ID, &device)) {
    return false;
  }

  bt_device = (bluetooth_device_t *)device;
  bt_interface = bt_device->get_bluetooth_interface();
  if (!bt_interface) {
    bt_device->common.close((hw_device_t *)&bt_device->common);
    bt_device = NULL;
    return false;
  }

  bool success = (bt_interface->init(callbacks) == BT_STATUS_SUCCESS);
  success = success && (bt_interface->set_os_callouts(&callouts) == BT_STATUS_SUCCESS);
  return success;
}

void hal_close() {
  if (bt_interface) {
    bt_interface->cleanup();
    bt_interface = NULL;
  }

  if (bt_device) {
    bt_device->common.close((hw_device_t *)&bt_device->common);
    bt_device = NULL;
  }
}

static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data) {
  static timer_t timer;
  static bool timer_created;

  if (!timer_created) {
    struct sigevent sigevent;
    memset(&sigevent, 0, sizeof(sigevent));
    sigevent.sigev_notify = SIGEV_THREAD;
    sigevent.sigev_notify_function = (void (*)(union sigval))cb;
    sigevent.sigev_value.sival_ptr = data;
    timer_create(CLOCK_MONOTONIC, &sigevent, &timer);
    timer_created = true;
  }

  struct itimerspec new_value;
  new_value.it_value.tv_sec = delay_millis / 1000;
  new_value.it_value.tv_nsec = (delay_millis % 1000) * 1000 * 1000;
  new_value.it_interval.tv_sec = 0;
  new_value.it_interval.tv_nsec = 0;
  timer_settime(timer, 0, &new_value, NULL);

  return true;
}

static int acquire_wake_lock(const char *lock_name) {
  return BT_STATUS_SUCCESS;
}

static int release_wake_lock(const char *lock_name) {
  return BT_STATUS_SUCCESS;
}
