/*
 * Copyright (c) 2024 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 "location_log.h"
#include "cj_lambda.h"
#include "locator_callback.h"

namespace OHOS {
namespace GeoLocationManager {
LocatorCallback::LocatorCallback()
{
    fixNumber_ = 0;
    inHdArea_ = true;
    singleLocation_ = nullptr;
    callbackValid_ = false;
    locationPriority_ = 0;
    InitLatch();
}

LocatorCallback::LocatorCallback(int64_t callbackId)
{
    fixNumber_ = 0;
    inHdArea_ = true;
    singleLocation_ = nullptr;
    callbackValid_ = false;
    locationPriority_ = 0;
    InitLatch();
    this->callbackId_ = callbackId;
    auto cFunc = reinterpret_cast<void(*)(CJLocation location)>(callbackId);
    callback_ = [ lambda = CJLambda::Create(cFunc)](const std::unique_ptr<Location::Location>& location) ->
        void { lambda(NativeLocationToCJLocation(*location)); };
}

void LocatorCallback::InitLatch()
{
    latch_ = new Location::CountDownLatch();
    if (latch_ == nullptr) {
        LBSLOGE(Location::LOCATOR_CALLBACK, "latch_ is nullptr.");
        return;
    }
    latch_->SetCount(1);
}

LocatorCallback::~LocatorCallback()
{
    delete latch_;
}

int LocatorCallback::OnRemoteRequest(uint32_t code,
    MessageParcel& data, MessageParcel& reply, MessageOption& option)
{
    if (data.ReadInterfaceToken() != GetDescriptor()) {
        LBSLOGE(Location::LOCATOR_CALLBACK, "invalid token.");
        return -1;
    }

    switch (code) {
        case Location::ILocatorCallback::RECEIVE_LOCATION_INFO_EVENT: {
            std::unique_ptr<Location::Location> location = Location::Location::UnmarshallingMakeUnique(data);
            LocatorCallback::OnLocationReport(location);
            if (location->GetLocationSourceType() == Location::LocationSourceType::NETWORK_TYPE &&
                location->GetAdditionsMap()["inHdArea"] != "") {
                inHdArea_ = (location->GetAdditionsMap()["inHdArea"] == "true");
            }
            if (NeedSetSingleLocation(location)) {
                SetSingleLocation(location);
            }
            if (IfReportAccuracyLocation()) {
                CountDown();
            }
            break;
        }
        case Location::ILocatorCallback::RECEIVE_LOCATION_STATUS_EVENT: {
            int status = data.ReadInt32();
            OnLocatingStatusChange(status);
            break;
        }
        case Location::ILocatorCallback::RECEIVE_ERROR_INFO_EVENT: {
            int errorCode = data.ReadInt32();
            LBSLOGI(Location::LOCATOR_STANDARD, "CallbackSutb receive ERROR_EVENT. errorCode:%{public}d", errorCode);
            if (errorCode == Location::LOCATING_FAILED_INTERNET_ACCESS_FAILURE) {
                inHdArea_ = false;
                if (GetSingleLocation() != nullptr) {
                    CountDown();
                }
            } else {
                OnErrorReport(errorCode);
            }
            break;
        }
        default: {
            IPCObjectStub::OnRemoteRequest(code, data, reply, option);
            break;
        }
    }
    return 0;
}

void LocatorCallback::OnLocationReport(const std::unique_ptr<Location::Location>& location)
{
    std::unique_lock<std::mutex> guard(mutex_);
    if (callback_ != nullptr) {
        callback_(location);
    }
}

void LocatorCallback::OnLocatingStatusChange(const int status)
{
}

void LocatorCallback::OnErrorReport(const int errorCode)
{
}

bool LocatorCallback::IsSingleLocationRequest()
{
    return (fixNumber_ == 1);
}

void LocatorCallback::CountDown()
{
    if (IsSingleLocationRequest() && latch_ != nullptr) {
        latch_->CountDown();
    }
}

void LocatorCallback::Wait(int time)
{
    if (IsSingleLocationRequest() && latch_ != nullptr) {
        latch_->Wait(time);
    }
}

int LocatorCallback::GetCount()
{
    if (IsSingleLocationRequest() && latch_ != nullptr) {
        return latch_->GetCount();
    }
    return 0;
}

void LocatorCallback::SetCount(int count)
{
    if (IsSingleLocationRequest() && latch_ != nullptr) {
        return latch_->SetCount(count);
    }
}

bool LocatorCallback::NeedSetSingleLocation(const std::unique_ptr<Location::Location>& location)
{
    if (locationPriority_ == Location::LOCATION_PRIORITY_ACCURACY &&
        singleLocation_ != nullptr &&
        location->GetLocationSourceType() == Location::LocationSourceType::NETWORK_TYPE) {
        return false;
    } else {
        return true;
    }
}

bool LocatorCallback::IfReportAccuracyLocation()
{
    if (locationPriority_ == Location::LOCATION_PRIORITY_ACCURACY &&
        (((singleLocation_->GetLocationSourceType() == Location::LocationSourceType::GNSS_TYPE ||
        singleLocation_->GetLocationSourceType() == Location::LocationSourceType::RTK_TYPE) && inHdArea_) ||
        singleLocation_->GetLocationSourceType() == Location::LocationSourceType::NETWORK_TYPE)) {
        return false;
    } else {
        return true;
    }
}

void LocatorCallback::SetSingleLocation(const std::unique_ptr<Location::Location>& location)
{
    std::unique_lock<std::mutex> guard(mutex_);
    singleLocation_ = std::make_shared<Location::Location>(*location);
}
}
}