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

#define LOG_TAG "VibratorManagerHalWrapper"

#include <utils/Log.h>

#include <vibratorservice/VibratorManagerHalWrapper.h>

namespace Aidl = android::hardware::vibrator;

namespace android {

namespace vibrator {

constexpr int32_t SINGLE_VIBRATOR_ID = 0;
const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";

HalResult<void> LegacyManagerHalWrapper::ping() {
    auto pingFn = [](HalWrapper* hal) { return hal->ping(); };
    return mController->doWithRetry<void>(pingFn, "ping");
}

void LegacyManagerHalWrapper::tryReconnect() {
    mController->tryReconnect();
}

HalResult<ManagerCapabilities> LegacyManagerHalWrapper::getCapabilities() {
    return HalResult<ManagerCapabilities>::ok(ManagerCapabilities::NONE);
}

HalResult<std::vector<int32_t>> LegacyManagerHalWrapper::getVibratorIds() {
    if (mController->init()) {
        return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>(1, SINGLE_VIBRATOR_ID));
    }
    // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
    return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>());
}

HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(int32_t id) {
    if (id == SINGLE_VIBRATOR_ID && mController->init()) {
        return HalResult<std::shared_ptr<HalController>>::ok(mController);
    }
    // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
    return HalResult<std::shared_ptr<HalController>>::failed(
            (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str());
}

HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
    return HalResult<void>::unsupported();
}

HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) {
    return HalResult<void>::unsupported();
}

HalResult<void> LegacyManagerHalWrapper::cancelSynced() {
    return HalResult<void>::unsupported();
}

// -------------------------------------------------------------------------------------------------

std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
        int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
    std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=, this]() {
        sp<Aidl::IVibrator> vibrator;
        auto result = this->getHal()->getVibrator(vibratorId, &vibrator);
        return HalResultFactory::fromStatus<sp<Aidl::IVibrator>>(result, vibrator);
    };
    auto result = reconnectFn();
    if (!result.isOk()) {
        return nullptr;
    }
    auto vibrator = result.value();
    if (!vibrator) {
        return nullptr;
    }
    return std::make_unique<AidlHalWrapper>(std::move(callbackScheduler), std::move(vibrator),
                                            reconnectFn);
}

HalResult<void> AidlManagerHalWrapper::ping() {
    return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder());
}

void AidlManagerHalWrapper::tryReconnect() {
    sp<Aidl::IVibratorManager> newHandle = checkVintfService<Aidl::IVibratorManager>();
    if (newHandle) {
        std::lock_guard<std::mutex> lock(mHandleMutex);
        mHandle = std::move(newHandle);
    }
}

HalResult<ManagerCapabilities> AidlManagerHalWrapper::getCapabilities() {
    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
    if (mCapabilities.has_value()) {
        // Return copy of cached value.
        return HalResult<ManagerCapabilities>::ok(*mCapabilities);
    }
    int32_t cap = 0;
    auto result = getHal()->getCapabilities(&cap);
    auto capabilities = static_cast<ManagerCapabilities>(cap);
    auto ret = HalResultFactory::fromStatus<ManagerCapabilities>(result, capabilities);
    if (ret.isOk()) {
        // Cache copy of returned value.
        mCapabilities.emplace(ret.value());
    }
    return ret;
}

HalResult<std::vector<int32_t>> AidlManagerHalWrapper::getVibratorIds() {
    std::lock_guard<std::mutex> lock(mVibratorsMutex);
    if (mVibratorIds.has_value()) {
        // Return copy of cached values.
        return HalResult<std::vector<int32_t>>::ok(*mVibratorIds);
    }
    std::vector<int32_t> ids;
    auto result = getHal()->getVibratorIds(&ids);
    auto ret = HalResultFactory::fromStatus<std::vector<int32_t>>(result, ids);
    if (ret.isOk()) {
        // Cache copy of returned value and the individual controllers.
        mVibratorIds.emplace(ret.value());
        for (auto& id : ids) {
            HalController::Connector connector = [&, id](auto scheduler) {
                return this->connectToVibrator(id, scheduler);
            };
            auto controller = std::make_unique<HalController>(mCallbackScheduler, connector);
            mVibrators[id] = std::move(controller);
        }
    }
    return ret;
}

HalResult<std::shared_ptr<HalController>> AidlManagerHalWrapper::getVibrator(int32_t id) {
    // Make sure we cache vibrator ids and initialize the individual controllers.
    getVibratorIds();
    std::lock_guard<std::mutex> lock(mVibratorsMutex);
    auto it = mVibrators.find(id);
    if (it != mVibrators.end()) {
        return HalResult<std::shared_ptr<HalController>>::ok(it->second);
    }
    return HalResult<std::shared_ptr<HalController>>::failed(
            (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str());
}

HalResult<void> AidlManagerHalWrapper::prepareSynced(const std::vector<int32_t>& ids) {
    auto ret = HalResultFactory::fromStatus(getHal()->prepareSynced(ids));
    if (ret.isOk()) {
        // Force reload of all vibrator controllers that were prepared for a sync operation here.
        // This will trigger calls to getVibrator(id) on each controller, so they can use the
        // latest service provided by this manager.
        std::lock_guard<std::mutex> lock(mVibratorsMutex);
        for (auto& id : ids) {
            auto it = mVibrators.find(id);
            if (it != mVibrators.end()) {
                it->second->tryReconnect();
            }
        }
    }
    return ret;
}

HalResult<void> AidlManagerHalWrapper::triggerSynced(
        const std::function<void()>& completionCallback) {
    HalResult<ManagerCapabilities> capabilities = getCapabilities();
    bool supportsCallback = capabilities.isOk() &&
            static_cast<int32_t>(capabilities.value() & ManagerCapabilities::TRIGGER_CALLBACK);
    auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
    return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
}

HalResult<void> AidlManagerHalWrapper::cancelSynced() {
    auto ret = HalResultFactory::fromStatus(getHal()->cancelSynced());
    if (ret.isOk()) {
        // Force reload of all vibrator controllers that were prepared for a sync operation before.
        // This will trigger calls to getVibrator(id) on each controller, so they can use the
        // latest service provided by this manager.
        std::lock_guard<std::mutex> lock(mVibratorsMutex);
        for (auto& entry : mVibrators) {
            entry.second->tryReconnect();
        }
    }
    return ret;
}

sp<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
    std::lock_guard<std::mutex> lock(mHandleMutex);
    return mHandle;
}

}; // namespace vibrator

}; // namespace android
