/*
 * 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 "fusion_controller.h"
#include "system_ability_definition.h"
#include "common_utils.h"
#include "constant_definition.h"
#include "location_log.h"
#include "hook_utils.h"
#ifdef FEATURE_NETWORK_SUPPORT
#include "network_ability_proxy.h"
#endif

namespace OHOS {
namespace Location {
const uint32_t FUSION_DEFAULT_FLAG = 0;
const uint32_t FUSION_BASE_FLAG = 1;
const uint32_t REPORT_FUSED_LOCATION_FLAG = FUSION_BASE_FLAG;

#ifdef FEATURE_NETWORK_SUPPORT
const uint32_t QUICK_FIX_FLAG = FUSION_BASE_FLAG << 1;
#endif
const long NANOS_PER_MILLI = 1000000L;
const long MAX_GNSS_LOCATION_COMPARISON_MS = 30 * MILLI_PER_SEC;
const long MAX_INDOOR_LOCATION_COMPARISON_MS = 5 * MILLI_PER_SEC;
const double MAX_INDOOR_LOCATION_SPEED = 3.0;

FusionController* FusionController::GetInstance()
{
    static FusionController data;
    return &data;
}

void FusionController::ActiveFusionStrategies(int type)
{
    if (needReset_) {
        fusedFlag_ = FUSION_DEFAULT_FLAG;
        needReset_ = false;
    }
    switch (type) {
        case SCENE_NAVIGATION:
        case SCENE_TRAJECTORY_TRACKING:
            break;
        case PRIORITY_FAST_FIRST_FIX:
            fusedFlag_ = fusedFlag_ | REPORT_FUSED_LOCATION_FLAG;
            break;
        default:
            break;
    }
}

std::unique_ptr<Location> FusionController::chooseBestLocation(const std::unique_ptr<Location>& location,
    const std::unique_ptr<Location>& lastFuseLocation)
{
    if (location == nullptr) {
        return nullptr;
    }
    if (lastFuseLocation == nullptr) {
        return std::make_unique<Location>(*location);
    }
    LocationFusionInfo fusionInfo;
    fusionInfo.location = *location;
    fusionInfo.lastFuseLocation = *lastFuseLocation;
    fusionInfo.resultLocation = fusionInfo.location;
    if (fusionInfo.location.GetLocationSourceType() == LocationSourceType::NETWORK_TYPE) {
        if (CheckIfLastGnssLocationValid(location, std::make_unique<Location>(fusionInfo.lastFuseLocation))) {
            fusionInfo.resultLocation = fusionInfo.lastFuseLocation;
        }
    }
    HookUtils::ExecuteHook(
        LocationProcessStage::FUSION_REPORT_PROCESS, (void *)&fusionInfo, nullptr);
    return std::make_unique<Location>(fusionInfo.resultLocation);
}

bool FusionController::CheckIfLastGnssLocationValid(const std::unique_ptr<Location>& location,
    const std::unique_ptr<Location>& lastFuseLocation)
{
    if ((lastFuseLocation->GetLocationSourceType() == LocationSourceType::GNSS_TYPE ||
        lastFuseLocation->GetLocationSourceType() == LocationSourceType::RTK_TYPE) &&
        ((location->GetTimeSinceBoot() / NANOS_PER_MILLI -
        lastFuseLocation->GetTimeSinceBoot() / NANOS_PER_MILLI) < MAX_GNSS_LOCATION_COMPARISON_MS)) {
        return true;
    } else {
        return false;
    }
}

std::unique_ptr<Location> FusionController::GetFuseLocation(const std::unique_ptr<Location>& location,
    const sptr<Location>& lastFuseLocation)
{
    LBSLOGD(FUSION_CONTROLLER, " GetFuseLocation enter");
    auto bestLocation = chooseBestLocation(location, std::make_unique<Location>(*lastFuseLocation));
    if (bestLocation == nullptr) {
        return nullptr;
    }
    return std::make_unique<Location>(*bestLocation);
}
} // namespace Location
} // namespace OHOS