/*
 * Copyright (C) 2017 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.
 */

#include "ASensorEventQueue.h"

#include "ALooper.h"

#define LOG_TAG "libsensorndkbridge"
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>

using aidl::android::hardware::sensors::SensorInfo;
using android::BAD_VALUE;
using android::Mutex;
using android::OK;

ASensorEventQueue::ASensorEventQueue(ALooper* looper, ALooper_callbackFunc callback, void* data)
    : mLooper(looper),
      mCallback(callback),
      mData(data),
      mRequestAdditionalInfo(false),
      mValid(true) {}

void ASensorEventQueue::setImpl(const std::shared_ptr<IEventQueue>& queueImpl) {
    mQueueImpl = queueImpl;
}

int ASensorEventQueue::registerSensor(
        ASensorRef sensor,
        int32_t samplingPeriodUs,
        int64_t maxBatchReportLatencyUs) {
    ndk::ScopedAStatus ret =
        mQueueImpl->enableSensor(reinterpret_cast<const SensorInfo*>(sensor)->sensorHandle,
                                 samplingPeriodUs, maxBatchReportLatencyUs);

    if (!ret.isOk()) {
        return BAD_VALUE;
    }

    return OK;
}

int ASensorEventQueue::enableSensor(ASensorRef sensor) {
    static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;

    return registerSensor(
            sensor, SENSOR_DELAY_NORMAL, 0 /* maxBatchReportLatencyUs */);
}

int ASensorEventQueue::setEventRate(
        ASensorRef sensor, int32_t samplingPeriodUs) {
    // Technically this is not supposed to enable the sensor but using this
    // API without enabling the sensor first is a no-op, so...
    return registerSensor(
            sensor, samplingPeriodUs, 0 /* maxBatchReportLatencyUs */);
}

int ASensorEventQueue::requestAdditionalInfoEvents(bool enable) {
    mRequestAdditionalInfo = enable;
    return OK;
}

int ASensorEventQueue::disableSensor(ASensorRef sensor) {
    ndk::ScopedAStatus ret =
        mQueueImpl->disableSensor(reinterpret_cast<const SensorInfo*>(sensor)->sensorHandle);

    return ret.isOk() ? OK : BAD_VALUE;
}

ssize_t ASensorEventQueue::getEvents(ASensorEvent *events, size_t count) {
    // XXX Should this block if there aren't any events in the queue?

    Mutex::Autolock autoLock(mLock);

    static_assert(
            sizeof(ASensorEvent) == sizeof(sensors_event_t), "mismatched size");

    size_t copy = std::min(count, mQueue.size());
    for (size_t i = 0; i < copy; ++i) {
        reinterpret_cast<sensors_event_t *>(events)[i] = mQueue[i];
    }
    mQueue.erase(mQueue.begin(), mQueue.begin() + copy);

    LOG(VERBOSE) << "ASensorEventQueue::getEvents() returned " << copy << " events.";

    return copy;
}

int ASensorEventQueue::hasEvents() const {
    return !mQueue.empty();
}

ndk::ScopedAStatus ASensorEventQueue::onEvent(const Event& event) {
    LOG(VERBOSE) << "ASensorEventQueue::onEvent";

    if (static_cast<int32_t>(event.sensorType) != ASENSOR_TYPE_ADDITIONAL_INFO ||
            mRequestAdditionalInfo.load()) {

        {
            // Only lock the mutex in this block to avoid the following deadlock scenario:
            //
            // ASensorEventQueue::onEvent is called which grabs ASensorEventQueue::mLock followed
            // by ALooper::mLock via ALooper::signalSensorEvents.
            //
            // Meanwhile
            //
            // ASensorEventQueue::dispatchCallback is invoked from ALooper::pollOnce which has
            // has ALooper::mLock locked and the dispatched callback invokes
            // ASensorEventQueue::getEvents which would try to grab ASensorEventQueue::mLock
            // resulting in a deadlock.
            Mutex::Autolock autoLock(mLock);
            mQueue.emplace_back();
            sensors_event_t* sensorEvent = &mQueue[mQueue.size() - 1];
            android::hardware::sensors::implementation::convertToSensorEvent(event, sensorEvent);
        }

        Mutex::Autolock autoLock(mValidLock);
        if (mValid) {
            mLooper->signalSensorEvents(this->ref<ASensorEventQueue>());
        }
    }

    return ndk::ScopedAStatus::ok();
}

void ASensorEventQueue::dispatchCallback() {
    if (mCallback != NULL) {
        int res = (*mCallback)(-1 /* fd */, ALOOPER_EVENT_INPUT, mData);

        if (res == 0) {
            mCallback = NULL;
            mData = NULL;
        }
    }
}

void ASensorEventQueue::invalidate() {
    {
      // Only lock within this context to avoid locking while calling invalidateSensorQueue which
      // also holds a lock. This is safe to do because mValid can't be made true after it's false
      // so onEvent will never signal new sensor events after mValid is false.
      Mutex::Autolock autoLock(mValidLock);
      mValid = false;
    }
    mLooper->invalidateSensorQueue(this->ref<ASensorEventQueue>());
    setImpl(nullptr);
}

