/*
 * 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 "geofence_interface_impl.h"

#include <hdf_base.h>
#include <iproxy_broker.h>
#include <mutex>
#include <unordered_map>
#include "geofence_hdf_log.h"
#include "idevmgr_hdi.h"
#include "securec.h"
#include "location_vendor_interface.h"
#include "location_vendor_lib.h"
#include "parameters.h"
namespace OHOS {
namespace HDI {
namespace Location {
namespace Geofence {
namespace V2_0 {
namespace {
using GeofenceCallBackMap = std::unordered_map<IRemoteObject*, sptr<IGeofenceCallback>>;
using GeofenceDeathRecipientMap = std::unordered_map<IRemoteObject*, sptr<IRemoteObject::DeathRecipient>>;
using OHOS::HDI::DeviceManager::V1_0::IDeviceManager;
sptr<IGeofenceCallback> g_geofenceCallBack;
GeofenceDeathRecipientMap g_geofenceCallBackDeathRecipientMap;
std::mutex g_mutex;
std::mutex g_deathMutex;
static const std::string SYSPARAM_HIGHPOWER_FENCE_SUPPORT  = "const.location.support_high_power_fence";
} // namespace
extern "C" IGeofenceInterface *GeofenceInterfaceImplGetInstance(void)
{
    return new (std::nothrow) GeofenceInterfaceImpl();
}

GeofenceInterfaceImpl::~GeofenceInterfaceImpl()
{
    ResetGeofenceDeathRecipient();
}

void OnGeofenceAvailabilityChange(bool isAvailable)
{
    HDF_LOGI("%{public}s.", __func__);
    std::unique_lock<std::mutex> lock(g_mutex);
    if (g_geofenceCallBack != nullptr) {
        g_geofenceCallBack->ReportGeofenceAvailability(isAvailable);
    }
}

void OnGeofenceEventChange(int32_t geofenceId,  GnssLocation* location, int32_t event, int64_t timestamp)
{
    HDF_LOGI("%{public}s.", __func__);
    std::unique_lock<std::mutex> lock(g_mutex);
    if (location == nullptr) {
        HDF_LOGE("OnGeofenceEventChange: location is null.");
        return;
    }
    LocationInfo locationNew;
    locationNew.fieldValidity = location->fieldValidity;
    locationNew.latitude = location->latitude;
    locationNew.longitude = location->longitude;
    locationNew.altitude = location->altitude;
    locationNew.horizontalAccuracy = location->horizontalAccuracy;
    locationNew.speed = location->speed;
    locationNew.bearing = location->bearing;
    locationNew.verticalAccuracy = location->verticalAccuracy;
    locationNew.speedAccuracy = location->speedAccuracy;
    locationNew.bearingAccuracy = location->bearingAccuracy;
    locationNew.timeForFix = location->timeForFix;
    locationNew.timeSinceBoot = (int64_t)location->timeSinceBoot;
    locationNew.timeUncertainty = (int64_t)location->timeUncertainty;
    if (g_geofenceCallBack != nullptr) {
        g_geofenceCallBack->ReportGeofenceEvent(geofenceId, locationNew, static_cast<GeofenceEvent>(event), timestamp);
    }
}

void OnGeofenceOperateResultChange(int32_t geofenceId, int32_t operateCategory, int32_t result)
{
    HDF_LOGI("%{public}s.", __func__);
    std::unique_lock<std::mutex> lock(g_mutex);
    if (g_geofenceCallBack != nullptr) {
        g_geofenceCallBack->ReportGeofenceOperateResult(geofenceId, static_cast<GeofenceOperateType>(operateCategory),
            static_cast<GeofenceOperateResult>(result));
    }
}

void GetGeofenceCallbackMethods(GeofenceCallbackIfaces* device)
{
    if (device == nullptr) {
        return;
    }
    device->size = sizeof(GeofenceCallbackIfaces);
    device->geofenceAvailabilityUpdate = OnGeofenceAvailabilityChange;
    device->geofenceEventUpdate = OnGeofenceEventChange;
    device->geofenceOperateResultUpdate = OnGeofenceOperateResultChange;
}

int32_t GeofenceInterfaceImpl::SetGeofenceCallback(const sptr<IGeofenceCallback>& callbackObj)
{
    if (!IsSupportHighPowerFence()) {
        HDF_LOGI("%{public}s:not support high power fence", __func__);
        return HDF_SUCCESS;
    }
    HDF_LOGI("%{public}s.", __func__);
    if (callbackObj == nullptr) {
        HDF_LOGE("%{public}s:invalid callbackObj", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    const sptr<IRemoteObject>& remote = OHOS::HDI::hdi_objcast<IGeofenceCallback>(callbackObj);
    if (remote == nullptr) {
        HDF_LOGE("%{public}s:invalid remote", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    std::unique_lock<std::mutex> lock(g_mutex);
    static GeofenceCallbackIfaces geofencecallback;
    GetGeofenceCallbackMethods(&geofencecallback);
    int moduleType = static_cast<int>(GnssModuleIfaceCategory::GNSS_GEOFENCING_MODULE_INTERFACE);
    LocationVendorInterface* interface = LocationVendorInterface::GetInstance();
    auto geofenceInterface =
        static_cast<const GeofenceModuleInterface*>(interface->GetModuleInterface(moduleType));
    if (geofenceInterface == nullptr) {
        HDF_LOGE("%{public}s:can not get geofenceInterface.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    bool ret = geofenceInterface->setCallback(&geofencecallback);
    if (!ret) {
        HDF_LOGE("setGeofenceCallback failed.");
        return HDF_FAILURE;
    }
    HDF_LOGI("setGeofenceCallback success.");
    AddGeofenceDeathRecipient(callbackObj);
    g_geofenceCallBack = callbackObj;
    return HDF_SUCCESS;
}

int32_t GeofenceInterfaceImpl::AddGnssGeofence(const GeofenceInfo& fence, int monitorEvent)
{
    if (!IsSupportHighPowerFence()) {
        HDF_LOGI("%{public}s:not support high power fence", __func__);
        return HDF_SUCCESS;
    }
    int moduleType = static_cast<int>(GnssModuleIfaceCategory::GNSS_GEOFENCING_MODULE_INTERFACE);
    LocationVendorInterface* interface = LocationVendorInterface::GetInstance();
    auto geofenceInterface =
        static_cast<const GeofenceModuleInterface*>(interface->GetModuleInterface(moduleType));
    if (geofenceInterface == nullptr) {
        HDF_LOGE("%{public}s:can not get geofenceInterface.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    bool ret = geofenceInterface->addGnssGeofence(
        fence.fenceIndex, fence.latitude, fence.longitude, fence.radius, monitorEvent);
    if (!ret) {
        HDF_LOGE("AddGnssGeofence failed.");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

int32_t GeofenceInterfaceImpl::DeleteGnssGeofence(int32_t fenceIndex)
{
    if (!IsSupportHighPowerFence()) {
        HDF_LOGI("%{public}s:not support high power fence", __func__);
        return HDF_SUCCESS;
    }
    int moduleType = static_cast<int>(GnssModuleIfaceCategory::GNSS_GEOFENCING_MODULE_INTERFACE);
    LocationVendorInterface* interface = LocationVendorInterface::GetInstance();
    auto geofenceInterface =
        static_cast<const GeofenceModuleInterface*>(interface->GetModuleInterface(moduleType));
    if (geofenceInterface == nullptr) {
        HDF_LOGE("%{public}s:can not get geofenceInterface.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    bool ret = geofenceInterface->deleteGnssGeofence(fenceIndex);
    if (!ret) {
        HDF_LOGE("DeleteGnssGeofence failed.");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

int32_t GeofenceInterfaceImpl::AddGeofenceDeathRecipient(const sptr<IGeofenceCallback>& callbackObj)
{
    sptr<IRemoteObject::DeathRecipient> death(new (std::nothrow) GeofenceCallBackDeathRecipient(this));
    const sptr<IRemoteObject>& remote = OHOS::HDI::hdi_objcast<IGeofenceCallback>(callbackObj);
    if (remote == nullptr) {
        HDF_LOGE("%{public}s:invalid remote", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    bool result = remote->AddDeathRecipient(death);
    if (!result) {
        HDF_LOGE("%{public}s: GeofenceInterfaceImpl add deathRecipient fail", __func__);
        return HDF_FAILURE;
    }
    std::unique_lock<std::mutex> lock(g_deathMutex);
    g_geofenceCallBackDeathRecipientMap[remote.GetRefPtr()] = death;
    return HDF_SUCCESS;
}

int32_t GeofenceInterfaceImpl::RemoveGeofenceDeathRecipient(const sptr<IGeofenceCallback>& callbackObj)
{
    std::unique_lock<std::mutex> lock(g_deathMutex);
    const sptr<IRemoteObject>& remote = OHOS::HDI::hdi_objcast<IGeofenceCallback>(callbackObj);
    if (remote == nullptr) {
        HDF_LOGE("%{public}s:invalid remote", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    auto iter = g_geofenceCallBackDeathRecipientMap.find(remote.GetRefPtr());
    if (iter == g_geofenceCallBackDeathRecipientMap.end()) {
        HDF_LOGE("%{public}s: GeofenceInterfaceImpl can not find deathRecipient", __func__);
        return HDF_FAILURE;
    }
    auto recipient = iter->second;
    if (recipient == nullptr) {
        HDF_LOGE("%{public}s: death recipient is null", __func__);
        g_geofenceCallBackDeathRecipientMap.erase(iter);
        return HDF_FAILURE;
    }
    bool result = remote->RemoveDeathRecipient(recipient);
    g_geofenceCallBackDeathRecipientMap.erase(iter);
    if (!result) {
        HDF_LOGE("%{public}s: GeofenceInterfaceImpl remove deathRecipient fail", __func__);
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

void GeofenceInterfaceImpl::ResetGeofenceDeathRecipient()
{
    std::unique_lock<std::mutex> lock(g_mutex);
    if (g_geofenceCallBack != nullptr) {
        RemoveGeofenceDeathRecipient(g_geofenceCallBack);
    }
}

void GeofenceInterfaceImpl::ResetGeofence()
{
    HDF_LOGI("%{public}s called.", __func__);
    ResetGeofenceDeathRecipient();
    std::unique_lock<std::mutex> lock(g_mutex);
    g_geofenceCallBack = nullptr;
}

bool GeofenceInterfaceImpl::IsSupportHighPowerFence()
{
    return OHOS::system::GetBoolParameter(SYSPARAM_HIGHPOWER_FENCE_SUPPORT, false);
}
} // V2_0
} // Geofence
} // Location
} // HDI
} // OHOS