/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * 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 "vibrator_thread.h"

#include <sys/prctl.h>

#include "custom_vibration_matcher.h"
#include "sensors_errors.h"

#undef LOG_TAG
#define LOG_TAG "VibratorThread"

namespace OHOS {
namespace Sensors {
namespace {
const std::string VIBRATE_CONTROL_THREAD_NAME = "OS_VibControl";
constexpr int32_t DELAY_TIME1 = 5;    /** ms */
constexpr int32_t DELAY_TIME2 = 10;   /** ms */
constexpr size_t RETRY_NUMBER = 4;
constexpr size_t COMPOSITE_EFFECT_PART = 128;
}  // namespace

bool VibratorThread::Run()
{
    CALL_LOG_ENTER;
    prctl(PR_SET_NAME, VIBRATE_CONTROL_THREAD_NAME.c_str());
    VibrateInfo info = GetCurrentVibrateInfo();
    if (info.mode == VIBRATE_TIME) {
        int32_t ret = PlayOnce(info);
        if (ret != SUCCESS) {
            MISC_HILOGE("Play once vibration fail, package:%{public}s", info.packageName.c_str());
            return false;
        }
    } else if (info.mode == VIBRATE_PRESET) {
        int32_t ret = PlayEffect(info);
        if (ret != SUCCESS) {
            MISC_HILOGE("Play effect vibration fail, package:%{public}s", info.packageName.c_str());
            return false;
        }
    } else if (info.mode == VIBRATE_CUSTOM_HD) {
        int32_t ret = PlayCustomByHdHptic(info);
        if (ret != SUCCESS) {
            MISC_HILOGE("Play custom vibration by hd haptic fail, package:%{public}s", info.packageName.c_str());
            return false;
        }
    } else if (info.mode == VIBRATE_CUSTOM_COMPOSITE_EFFECT || info.mode == VIBRATE_CUSTOM_COMPOSITE_TIME) {
        int32_t ret = PlayCustomByCompositeEffect(info);
        if (ret != SUCCESS) {
            MISC_HILOGE("Play custom vibration by composite effect fail, package:%{public}s", info.packageName.c_str());
            return false;
        }
    }
    return false;
}

int32_t VibratorThread::PlayOnce(const VibrateInfo &info)
{
    std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
    int32_t ret = VibratorDevice.StartOnce(static_cast<uint32_t>(info.duration));
    if (ret != SUCCESS) {
        MISC_HILOGE("StartOnce fail, duration:%{public}d", info.duration);
        return ERROR;
    }
    cv_.wait_for(vibrateLck, std::chrono::milliseconds(info.duration), [this] { return exitFlag_.load(); });
    VibratorDevice.Stop(HDF_VIBRATOR_MODE_ONCE);
    if (exitFlag_) {
        MISC_HILOGD("Stop duration:%{public}d, package:%{public}s", info.duration, info.packageName.c_str());
        return SUCCESS;
    }
    return SUCCESS;
}

void VibratorThread::HandleMultipleVibrations()
{
    if (VibratorDevice.IsVibratorRunning()) {
        VibratorDevice.Stop(HDF_VIBRATOR_MODE_PRESET);
        for (size_t i = 0; i < RETRY_NUMBER; i++) {
            if (!VibratorDevice.IsVibratorRunning()) {
                MISC_HILOGI("No running vibration");
                return;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_TIME1));
        }
        MISC_HILOGW("Unstopped vibration");
    }
}

int32_t VibratorThread::PlayEffect(const VibrateInfo &info)
{
    std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
    for (int32_t i = 0; i < info.count; ++i) {
        std::string effect = info.effect;
        int32_t duration = info.duration;
        if (i >= 1) { /**Multiple vibration treatment*/
            HandleMultipleVibrations();
            duration += DELAY_TIME2;
        }
        int32_t ret = VibratorDevice.StartByIntensity(effect, info.intensity);
        if (ret != SUCCESS) {
            MISC_HILOGE("Vibrate effect %{public}s failed, ", effect.c_str());
            return ERROR;
        }
        cv_.wait_for(vibrateLck, std::chrono::milliseconds(duration), [this] { return exitFlag_.load(); });
        VibratorDevice.Stop(HDF_VIBRATOR_MODE_PRESET);
        if (exitFlag_) {
            MISC_HILOGD("Stop effect:%{public}s, package:%{public}s", effect.c_str(), info.packageName.c_str());
            return SUCCESS;
        }
    }
    return SUCCESS;
}

int32_t VibratorThread::PlayCustomByHdHptic(const VibrateInfo &info)
{
    std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
    const std::vector<VibratePattern> &patterns = info.package.patterns;
    size_t patternSize = patterns.size();
    for (size_t i = 0; i < patternSize; ++i) {
        int32_t delayTime;
        if (i == 0) {
            delayTime = patterns[i].startTime;
        } else {
            delayTime = patterns[i].startTime - patterns[i - 1].startTime;
        }
        cv_.wait_for(vibrateLck, std::chrono::milliseconds(delayTime), [this] { return exitFlag_.load(); });
        if (exitFlag_) {
            VibratorDevice.Stop(HDF_VIBRATOR_MODE_HDHAPTIC);
            MISC_HILOGD("Stop hd haptic, package:%{public}s", info.packageName.c_str());
            return SUCCESS;
        }
        int32_t ret = VibratorDevice.PlayPattern(patterns[i]);
        if (ret != SUCCESS) {
            MISC_HILOGE("Vibrate hd haptic failed");
            return ERROR;
        }
    }
    return SUCCESS;
}

int32_t VibratorThread::PlayCustomByCompositeEffect(const VibrateInfo &info)
{
    auto &matcher = CustomVibrationMatcher::GetInstance();
    HdfCompositeEffect hdfCompositeEffect;
    if (info.mode == VIBRATE_CUSTOM_COMPOSITE_EFFECT) {
        hdfCompositeEffect.type = HDF_EFFECT_TYPE_PRIMITIVE;
        int32_t ret = matcher.TransformEffect(info.package, hdfCompositeEffect.compositeEffects);
        if (ret != SUCCESS) {
            MISC_HILOGE("Transform pattern to predefined wave fail");
            return ERROR;
        }
    } else if (info.mode == VIBRATE_CUSTOM_COMPOSITE_TIME) {
        hdfCompositeEffect.type = HDF_EFFECT_TYPE_TIME;
        int32_t ret = matcher.TransformTime(info.package, hdfCompositeEffect.compositeEffects);
        if (ret != SUCCESS) {
            MISC_HILOGE("Transform pattern to time series fail");
            return ERROR;
        }
    }
    return PlayCompositeEffect(info, hdfCompositeEffect);
}

int32_t VibratorThread::PlayCompositeEffect(const VibrateInfo &info, const HdfCompositeEffect &hdfCompositeEffect)
{
    std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
    HdfCompositeEffect effectsPart;
    effectsPart.type = hdfCompositeEffect.type;
    size_t effectSize = hdfCompositeEffect.compositeEffects.size();
    int32_t delayTime = 0;
    for (size_t i = 0; i < effectSize; ++i) {
        effectsPart.compositeEffects.push_back(hdfCompositeEffect.compositeEffects[i]);
        if (effectsPart.type == HDF_EFFECT_TYPE_TIME) {
            delayTime += hdfCompositeEffect.compositeEffects[i].timeEffect.delay;
        } else if (effectsPart.type == HDF_EFFECT_TYPE_PRIMITIVE) {
            delayTime += hdfCompositeEffect.compositeEffects[i].primitiveEffect.delay;
        } else {
            MISC_HILOGE("Effect type is valid");
            return ERROR;
        }
        if ((effectsPart.compositeEffects.size() >= COMPOSITE_EFFECT_PART) || (i == (effectSize - 1))) {
            int32_t ret = VibratorDevice.EnableCompositeEffect(effectsPart);
            if (ret != SUCCESS) {
                MISC_HILOGE("EnableCompositeEffect failed");
                return ERROR;
            }
            cv_.wait_for(vibrateLck, std::chrono::milliseconds(delayTime), [this] { return exitFlag_.load(); });
            delayTime = 0;
            effectsPart.compositeEffects.clear();
        }
        if (exitFlag_) {
            VibratorDevice.Stop(HDF_VIBRATOR_MODE_PRESET);
            MISC_HILOGD("Stop composite effect part, package:%{public}s", info.packageName.c_str());
            return SUCCESS;
        }
    }
    return SUCCESS;
}

void VibratorThread::UpdateVibratorEffect(const VibrateInfo &info)
{
    std::unique_lock<std::mutex> lck(currentVibrationMutex_);
    currentVibration_ = info;
}

VibrateInfo VibratorThread::GetCurrentVibrateInfo()
{
    std::unique_lock<std::mutex> lck(currentVibrationMutex_);
    return currentVibration_;
}

void VibratorThread::SetExitStatus(bool status)
{
    exitFlag_.store(status);
}

void VibratorThread::WakeUp()
{
    MISC_HILOGD("Notify the vibratorThread");
    cv_.notify_one();
}
}  // namespace Sensors
}  // namespace OHOS