/*
 * Copyright (C) 2025 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 <unistd.h>
#include "android_service.h"
#include "miscdevice_log.h"
#include "raw_file_descriptor.h"
#include "json_parser.h"
#include "vibrator_decoder_creator.h"

namespace OHOS {
namespace Sensors {

int32_t VibratorImpl::Vibrate(const VibratorIdentifierData &identifier, int32_t timeOut,
    int32_t usage, bool systemUsage)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    return VibratorJni::Vibrate(info, timeOut, usage, systemUsage);
}

int32_t VibratorImpl::PlayPrimitiveEffect(const VibratorIdentifierData& identifier,
    const std::string &effect, const PrimitiveEffectData& primitiveEffectIn)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    PrimitiveEffectTrans primitiveEffect;
    primitiveEffect.count = primitiveEffectIn.count;
    primitiveEffect.intensity = primitiveEffectIn.intensity;
    primitiveEffect.systemUsage = primitiveEffectIn.systemUsage;
    primitiveEffect.usage = primitiveEffectIn.usage;
    return VibratorJni::PlayPrimitiveEffect(info, effect, primitiveEffect);
}

void VibratorImpl::MergeVibratorParameters(const VibrateParameter &parameter, VibratePackage &package)
{
    if ((parameter.intensity == OH_INTENSITY_MAX) && (parameter.frequency == 0)) {
        MISC_HILOGD("The adjust parameter is not need to merge");
        return;
    }
    float intensityScale = static_cast<float>(parameter.intensity) / OH_INTENSITY_MAX;
    for (VibratePattern &pattern : package.patterns) {
        for (VibrateEvent &event : pattern.events) {
            if ((event.tag == EVENT_TAG_TRANSIENT) || (event.points.empty())) {
                MergeTransientEvent(event, intensityScale, parameter.frequency);
            } else {
                MergeCurvePointEvent(event, intensityScale, parameter.frequency);
            }
        }
    }
}

void VibratorImpl::MergeTransientEvent(VibrateEvent &event, float intensityScale, int32_t frequencyAdjust)
{
    event.intensity = static_cast<int32_t>(event.intensity * intensityScale);
    event.intensity = std::max(std::min(event.intensity, OH_INTENSITY_MAX), 0);
    event.frequency += frequencyAdjust;
    event.frequency = std::max(std::min(event.frequency, OH_INTENSITY_MAX), 0);
}

void VibratorImpl::MergeCurvePointEvent(VibrateEvent &event, float intensityScale, int32_t frequencyAdjust)
{
    for (VibrateCurvePoint &point : event.points) {
        point.intensity = static_cast<int32_t>(point.intensity * intensityScale);
        point.intensity = std::max(std::min(point.intensity, OH_INTENSITY_MAX), 0);
        point.frequency += frequencyAdjust;
        point.frequency = std::max(std::min(point.frequency, OH_INTENSITY_MAX), OH_INTENSITY_MIN);
    }
}

int32_t VibratorImpl::PlayVibratorCustom(const VibratorIdentifierData &identifier, int32_t fd, int64_t offset,
    int64_t length, const CustomHapticInfoData &customHapticInfo)
{
    RawFileDescriptor rawFd;
    rawFd.fd = fd;
    rawFd.offset = offset;
    rawFd.length = length;
    JsonParser parser(rawFd);
    VibratorDecoderCreator creator;
    std::unique_ptr<IVibratorDecoder> decoder(creator.CreateDecoder(parser));
    if (decoder == nullptr) {
        MISC_HILOGE("decoder is nullptr");
        return ERROR;
    }

    VibratePackage package;
    int32_t ret = decoder->DecodeEffect(rawFd, parser, package);
    if (ret != SUCCESS || package.patterns.empty()) {
        MISC_HILOGE("Decode effect error");
        return ERROR;
    }
    MergeVibratorParameters(customHapticInfo.parameter, package);
    if (package.patterns.size() > 1) {
        VibratePattern mergedPattern = MergePatterns(package.patterns);
        ret = PlayPattern(identifier, mergedPattern, customHapticInfo);
    } else {
        ret = PlayPattern(identifier, package.patterns[0], customHapticInfo);
    }
    return ret;
}

VibratePattern VibratorImpl::MergePatterns(const std::vector<VibratePattern>& patterns)
{
    VibratePattern merged;
    if (patterns.empty()) {
        return merged;
    }
    std::vector<VibratePattern> sortedPatterns = patterns;
    std::sort(sortedPatterns.begin(), sortedPatterns.end());
    merged.startTime = sortedPatterns[0].startTime;
    int32_t timeOffset = 0;
    for (const auto& pattern : sortedPatterns) {
        for (const auto& event : pattern.events) {
            VibrateEvent adjustedEvent = event;
            adjustedEvent.time = event.time - pattern.startTime + timeOffset + merged.startTime;
            merged.events.push_back(adjustedEvent);
        }
        timeOffset += pattern.patternDuration;
    }
    merged.patternDuration = timeOffset;
    return merged;
}

int32_t VibratorImpl::StopVibrator(const VibratorIdentifierData &identifier)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    return VibratorJni::StopVibrator(info);
}

int32_t VibratorImpl::StopVibratorByMode(const VibratorIdentifierData &identifier, const std::string &mode)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    return VibratorJni::StopVibratorByMode(info, mode);
}

int32_t VibratorImpl::IsSupportEffect(const VibratorIdentifierData &identifier, const std::string &effect, bool &state)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    return VibratorJni::IsSupportEffect(info, effect, state);
}

int32_t VibratorImpl::PlayPattern(const VibratorIdentifierData &identifier, const VibratePattern &pattern,
                                  const CustomHapticInfoData &customHapticInfo)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    VibratePatternTrans vibratePattern = ConvertPattern(pattern);
    CustomHapticInfoTrans hapticInfo;
    hapticInfo.usage = customHapticInfo.usage;
    hapticInfo.systemUsage = customHapticInfo.systemUsage;
    hapticInfo.parameter.frequency = customHapticInfo.parameter.frequency;
    hapticInfo.parameter.intensity = customHapticInfo.parameter.intensity;
    hapticInfo.parameter.reserved = customHapticInfo.parameter.reserved;
    hapticInfo.parameter.sessionId = customHapticInfo.parameter.sessionId;
    return VibratorJni::PlayPattern(info, vibratePattern, hapticInfo);
}

int32_t VibratorImpl::GetVibratorList(const VibratorIdentifierData &identifier,
                                      std::vector<VibratorInfo> &vibratorInfo)
{
    VibratorIdentifierTrans identifierInfo;
    identifierInfo.deviceId = identifier.deviceId;
    identifierInfo.isLocalVibrator = identifier.isLocalVibrator;
    identifierInfo.position = identifier.position;
    identifierInfo.vibratorId = identifier.vibratorId;
    std::vector<VibratorInfoTrans> vibratorInfoOut{};
    int32_t ret = VibratorJni::GetVibratorList(identifierInfo, vibratorInfoOut);
    vibratorInfo.reserve(vibratorInfoOut.size());
    for (auto &info : vibratorInfoOut) {
        VibratorInfo resInfo;
        resInfo.deviceId = info.deviceId;
        resInfo.vibratorId = info.vibratorId;
        resInfo.deviceName = info.deviceName;
        resInfo.isSupportHdHaptic = info.isSupportHdHaptic;
        resInfo.isLocalVibrator = info.isLocalVibrator;
        resInfo.position = info.position;
        vibratorInfo.push_back(resInfo);
    }
    return ret;
}

int32_t VibratorImpl::GetEffectInfo(const VibratorIdentifierData &identifier,
                                    const std::string &effectType, EffectInfoData &effectInfo)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    EffectInfoTrans effectInfoOut;
    int32_t ret = VibratorJni::GetEffectInfo(info, effectType, effectInfoOut);
    effectInfo.duration = effectInfoOut.duration;
    effectInfo.isSupportEffect = effectInfoOut.isSupportEffect;
    return ret;
}

int32_t VibratorImpl::GetVibratorCapacity(const VibratorIdentifierData &identifier, VibratorCapacity &capacity)
{
    VibratorIdentifierTrans info;
    info.deviceId = identifier.deviceId;
    info.isLocalVibrator = identifier.isLocalVibrator;
    info.position = identifier.position;
    info.vibratorId = identifier.vibratorId;
    VibratorCapacityTrans vibratorCapacity;
    int32_t ret = VibratorJni::GetVibratorCapacity(info, vibratorCapacity);
    capacity.isSupportHdHaptic = vibratorCapacity.isSupportHdHaptic;
    capacity.isSupportPresetMapping = vibratorCapacity.isSupportPresetMapping;
    capacity.isSupportTimeDelay = vibratorCapacity.isSupportTimeDelay;
    return ret;
}

VibrateCurvePointTrans VibratorImpl::ConvertCurvePoint(const VibrateCurvePoint &src)
{
    VibrateCurvePointTrans dest;
    dest.time = src.time;
    dest.intensity = src.intensity;
    dest.frequency = src.frequency;
    return dest;
}

VibrateEventTrans VibratorImpl::ConvertEvent(const VibrateEvent &src)
{
    VibrateEventTrans dest;
    dest.tag = static_cast<VibrateTagTrans>(src.tag);
    dest.time = src.time;
    dest.duration = src.duration;
    dest.intensity = src.intensity;
    dest.frequency = src.frequency;
    dest.index = src.index;
    dest.points.reserve(src.points.size());
    for (const auto& point : src.points) {
        dest.points.push_back(ConvertCurvePoint(point));
    }
    return dest;
}

VibratePatternTrans VibratorImpl::ConvertPattern(const VibratePattern &src)
{
    VibratePatternTrans dest;
    dest.startTime = src.startTime;
    dest.patternDuration = src.patternDuration;
    dest.events.reserve(src.events.size());
    for (const auto& event : src.events) {
        dest.events.push_back(ConvertEvent(event));
    }
    return dest;
}

std::vector<VibratePatternTrans> VibratorImpl::ConvertPatterns(const std::vector<VibratePattern>& srcPatterns)
{
    std::vector<VibratePatternTrans> destPatterns;
    destPatterns.reserve(srcPatterns.size());
    for (const auto& pattern : srcPatterns) {
        destPatterns.push_back(ConvertPattern(pattern));
    }
    return destPatterns;
}
}
}