/*
 * 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 <chrono>
#include "vendor_manager.h"
#include "vendor_helper.h"
#include "vendor_bsuni_driver.h"
#include "vendor_ipp_everywhere.h"
#include "vendor_ppd_driver.h"
#include "vendor_wlan_group.h"
#include "print_log.h"
#include "print_utils.h"

using namespace OHOS::Print;
namespace {
const std::string VENDOR_MANAGER_PREFIX = "fwk.";
const std::string GLOBAL_ID_DELIMITER = ":";
const int MONITOR_CHECK_INTERVAL_MS = 1000;
const size_t IP_LENGTH_MIN = 7;
}

VendorManager::VendorManager()
{
    wlanGroupDriver = std::make_shared<VendorWlanGroup>(this);
}

VendorManager::~VendorManager()
{
    UnInit();
}

std::string VendorManager::GetGlobalVendorName(const std::string &vendorName)
{
    return VENDOR_MANAGER_PREFIX + vendorName;
}
std::string VendorManager::GetGlobalPrinterId(const std::string &globalVendorName, const std::string &printerId)
{
    return globalVendorName + GLOBAL_ID_DELIMITER + printerId;
}
std::string VendorManager::ExtractVendorName(const std::string &globalVendorName)
{
    auto pos = globalVendorName.find(VENDOR_MANAGER_PREFIX);
    if (pos != 0 || globalVendorName.length() <= VENDOR_MANAGER_PREFIX.length()) {
        return "";
    }
    return globalVendorName.substr(VENDOR_MANAGER_PREFIX.length());
}

std::string VendorManager::ExtractGlobalVendorName(const std::string &globalPrinterId)
{
    auto pos = globalPrinterId.find(GLOBAL_ID_DELIMITER);
    if (pos == std::string::npos) {
        return "";
    }
    return globalPrinterId.substr(0, pos);
}

std::string VendorManager::ExtractPrinterId(const std::string &globalPrinterId)
{
    auto pos = globalPrinterId.find(GLOBAL_ID_DELIMITER);
    if (pos == std::string::npos || globalPrinterId.length() <= pos + 1) {
        return globalPrinterId;
    }
    return globalPrinterId.substr(pos + 1);
}

bool VendorManager::Init(IPrintServiceAbility *sa, bool loadDefault)
{
    PRINT_HILOGI("Init enter");
    printServiceAbility = sa;
    if (!loadDefault) {
        return true;
    }
    bool expectLoaded = false;
    if (!defaultLoaded.compare_exchange_strong(expectLoaded, true)) {
        PRINT_HILOGI("load already");
        return true;
    }
    PRINT_HILOGI("load default vendor...");
    if (wlanGroupDriver != nullptr) {
        wlanGroupDriver->Init(this);
        wlanGroupDriver->OnCreate();
    }
    auto vendorBsUni = std::make_shared<VendorBsuniDriver>();
    if (!LoadVendorDriver(vendorBsUni)) {
        PRINT_HILOGW("BsUni driver load fail");
    }
    auto vendorIppEverywhere = std::make_shared<VendorIppEveryWhere>();
    if (!LoadVendorDriver(vendorIppEverywhere)) {
        PRINT_HILOGW("IppEverywhere driver load fail");
    }
    auto vendorPpdDriver = std::make_shared<VendorPpdDriver>();
    if (!LoadVendorDriver(vendorPpdDriver)) {
        PRINT_HILOGW("ppd driver load fail");
    }
    PRINT_HILOGI("Init quit");
    return true;
}

void VendorManager::UnInit()
{
    PRINT_HILOGI("UnInit enter");
    StopStatusMonitor();
    std::lock_guard<std::mutex> lock(vendorMapMutex);
    for (auto const &pair : vendorMap) {
        PRINT_HILOGD("UnInit %{public}s", pair.first.c_str());
        if (pair.second == nullptr) {
            PRINT_HILOGW("vendor extension is null");
            continue;
        }
        pair.second->OnDestroy();
        pair.second->UnInit();
    }
    vendorMap.clear();
    if (wlanGroupDriver != nullptr) {
        wlanGroupDriver->OnDestroy();
        wlanGroupDriver->UnInit();
    }
    printServiceAbility = nullptr;
    defaultLoaded = false;
    PRINT_HILOGI("UnInit quit");
}

bool VendorManager::LoadVendorDriver(std::shared_ptr<VendorDriverBase> vendorDriver)
{
    if (vendorDriver == nullptr) {
        PRINT_HILOGW("vendorDriver is null");
        return false;
    }
    if (!vendorDriver->Init(this)) {
        PRINT_HILOGW("vendorDriver init fail");
        return false;
    }
    std::lock_guard<std::mutex> lock(vendorMapMutex);
    vendorMap.insert(std::make_pair(vendorDriver->GetVendorName(), vendorDriver));
    vendorDriver->OnCreate();
    return true;
}
bool VendorManager::UnloadVendorDriver(const std::string &vendorName)
{
    std::lock_guard<std::mutex> lock(vendorMapMutex);
    auto iter = vendorMap.find(vendorName);
    if (iter == vendorMap.end()) {
        return false;
    }
    auto vendorDriver = iter->second;
    vendorMap.erase(iter);
    if (vendorDriver != nullptr) {
        vendorDriver->OnDestroy();
        vendorDriver->UnInit();
    }
    return true;
}

bool VendorManager::ConnectPrinter(const std::string &globalPrinterId)
{
    PRINT_HILOGI("ConnectPrinter enter");
    std::string printerId = ExtractPrinterId(globalPrinterId);
    if (printerId.empty()) {
        PRINT_HILOGW("empty printer id");
        return false;
    }
    auto vendorDriver = FindDriverByPrinterId(globalPrinterId);
    if (vendorDriver == nullptr) {
        PRINT_HILOGW("vendorDriver is null");
        return false;
    }
    PRINT_HILOGD("OnQueryCapability: %{public}s", printerId.c_str());
    return vendorDriver->OnQueryCapability(printerId, 0);
}

bool VendorManager::ConnectPrinterByIp(const std::string &printerIp, const std::string &protocol)
{
    PRINT_HILOGI("ConnectPrinterByIp enter");
    if (printerIp.size() < IP_LENGTH_MIN) {
        PRINT_HILOGW("ip length incorrect");
        return false;
    }
    if (wlanGroupDriver == nullptr) {
        PRINT_HILOGE("no driver to connect printer by ip");
        return false;
    }
    return wlanGroupDriver->OnQueryCapabilityByIp(printerIp, protocol);
}

bool VendorManager::QueryPrinterInfo(const std::string &globalPrinterId, int timeout)
{
    PRINT_HILOGI("QueryPrinterInfo enter");
    std::string printerId = ExtractPrinterId(globalPrinterId);
    if (printerId.empty()) {
        PRINT_HILOGW("empty printer id");
        return false;
    }
    auto vendorDriver = FindDriverByPrinterId(globalPrinterId);
    if (vendorDriver == nullptr) {
        PRINT_HILOGW("vendorDriver is null");
        return false;
    }
    PRINT_HILOGD("OnQueryCapability: %{public}s", printerId.c_str());
    vendorDriver->OnQueryCapability(printerId, timeout);
    PRINT_HILOGI("QueryPrinterInfo quit");
    return true;
}

void VendorManager::StartDiscovery()
{
    PRINT_HILOGI("StartDiscovery enter");
    std::lock_guard<std::mutex> lock(vendorMapMutex);
    for (auto const &pair : vendorMap) {
        PRINT_HILOGD("StartDiscovery %{public}s", pair.first.c_str());
        if (pair.second == nullptr) {
            PRINT_HILOGW("vendor extension is null");
            continue;
        }
        pair.second->OnStartDiscovery();
    }
    PRINT_HILOGI("StartDiscovery quit");
}
void VendorManager::StopDiscovery()
{
    PRINT_HILOGI("StopDiscovery enter");
    std::lock_guard<std::mutex> lock(vendorMapMutex);
    for (auto const &pair : vendorMap) {
        if (pair.second == nullptr) {
            PRINT_HILOGW("vendor extension is null");
            continue;
        }
        pair.second->OnStopDiscovery();
    }
    PRINT_HILOGI("StopDiscovery quit");
}

int32_t VendorManager::AddPrinterToDiscovery(const std::string &vendorName, const PrinterInfo &printerInfo)
{
    PRINT_HILOGI("AddPrinterToDiscovery enter");
    if (vendorName == VENDOR_BSUNI_DRIVER && wlanGroupDriver != nullptr) {
        return wlanGroupDriver->OnPrinterDiscovered(vendorName, printerInfo);
    }
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    if (!printServiceAbility->AddVendorPrinterToDiscovery(GetGlobalVendorName(vendorName), printerInfo)) {
        PRINT_HILOGW("AddPrinterToDiscovery fail");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    PRINT_HILOGI("AddPrinterToDiscovery quit");
    return EXTENSION_ERROR_NONE;
}

int32_t VendorManager::UpdatePrinterToDiscovery(const std::string &vendorName, const PrinterInfo &printerInfo)
{
    PRINT_HILOGI("UpdatePrinterToDiscovery enter");
    if (vendorName == VENDOR_BSUNI_DRIVER && wlanGroupDriver != nullptr) {
        return wlanGroupDriver->OnUpdatePrinterToDiscovery(vendorName, printerInfo);
    }
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    auto targetVendorName = IsWlanGroupDriver(printerInfo.GetPrinterId()) ? VENDOR_WLAN_GROUP : vendorName;
    std::string globalVendorName = GetGlobalVendorName(targetVendorName);
    if (!printServiceAbility->UpdateVendorPrinterToDiscovery(globalVendorName, printerInfo)) {
        PRINT_HILOGW("UpdatePrinterToDiscovery fail");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    PRINT_HILOGI("UpdatePrinterToDiscovery quit");
    return EXTENSION_ERROR_NONE;
}
int32_t VendorManager::RemovePrinterFromDiscovery(const std::string &vendorName, const std::string &printerId)
{
    PRINT_HILOGI("RemovePrinterFromDiscovery enter");
    if (vendorName == VENDOR_BSUNI_DRIVER && wlanGroupDriver != nullptr) {
        return wlanGroupDriver->OnPrinterRemoved(vendorName, printerId);
    }
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    if (!printServiceAbility->RemoveVendorPrinterFromDiscovery(GetGlobalVendorName(vendorName), printerId)) {
        PRINT_HILOGW("RemovePrinterFromDiscovery fail");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    PRINT_HILOGI("RemovePrinterFromDiscovery quit");
    return EXTENSION_ERROR_NONE;
}

int32_t VendorManager::AddPrinterToCupsWithPpd(const std::string &vendorName, const std::string &printerId,
                                               const std::string &ppdData)
{
    PRINT_HILOGI("AddPrinterToCupsWithPpd enter");
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    auto targetVendorName = IsWlanGroupDriver(ExtractPrinterId(printerId)) ? VENDOR_WLAN_GROUP : vendorName;
    std::string globalVendorName = GetGlobalVendorName(targetVendorName);
    if (IsPrivatePpdDriver(vendorName)) {
        PRINT_HILOGD("AddPrinterToCupsWithPpd vendorName=%{public}s", vendorName.c_str());
        PRINT_HILOGD("AddPrinterToCupsWithPpd printerId=%{public}s", printerId.c_str());
        if (!printServiceAbility->AddVendorPrinterToCupsWithSpecificPpd(globalVendorName,
            VendorManager::ExtractPrinterId(printerId), ppdData)) {
            PRINT_HILOGW("AddPrinterToCupsWithPpd fail");
            return EXTENSION_ERROR_CALLBACK_FAIL;
        }
    } else {
        if (!printServiceAbility->AddVendorPrinterToCupsWithPpd(globalVendorName, printerId, ppdData)) {
            PRINT_HILOGW("AddPrinterToCupsWithPpd fail");
            return EXTENSION_ERROR_CALLBACK_FAIL;
        }
    }
    PRINT_HILOGI("AddPrinterToCupsWithPpd quit");
    return EXTENSION_ERROR_NONE;
}

int32_t VendorManager::RemovePrinterFromCups(const std::string &vendorName, const std::string &printerId)
{
    PRINT_HILOGI("RemovePrinterFromCups enter");
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    auto targetVendorName = IsWlanGroupDriver(printerId) ? VENDOR_WLAN_GROUP : vendorName;
    std::string globalVendorName = GetGlobalVendorName(targetVendorName);
    if (!printServiceAbility->RemoveVendorPrinterFromCups(globalVendorName, printerId)) {
        PRINT_HILOGW("RemovePrinterFromCups fail");
        return EXTENSION_ERROR_CALLBACK_FAIL;
    }
    PRINT_HILOGI("RemovePrinterFromCups quit");
    return EXTENSION_ERROR_NONE;
}

bool VendorManager::OnPrinterPpdQueried(const std::string &vendorName, const std::string &printerId,
                                        const std::string &ppdData)
{
    PRINT_HILOGI("OnPrinterPpdQueried enter");
    if (vendorName == VENDOR_BSUNI_DRIVER && wlanGroupDriver != nullptr) {
        return wlanGroupDriver->OnPrinterPpdQueried(vendorName, printerId, ppdData);
    }
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return false;
    }
    std::string globalVendorName = GetGlobalVendorName(vendorName);
    std::string globalPrinterId = GetGlobalPrinterId(globalVendorName, printerId);
    PRINT_HILOGD("global printer id %{public}s", globalPrinterId.c_str());
    if (!IsConnectingPrinter(globalPrinterId, "")) {
        PRINT_HILOGW("not connecting");
        return false;
    }
    if (!printServiceAbility->AddVendorPrinterToCupsWithPpd(globalVendorName, printerId, ppdData)) {
        PRINT_HILOGW("AddPrinterToCupsWithPpd fail");
        return false;
    }
    PRINT_HILOGI("OnPrinterPpdQueried quit");
    return true;
}

bool VendorManager::OnPrinterStatusChanged(const std::string &vendorName, const std::string &printerId,
                                           const PrinterVendorStatus &status)
{
    auto targetVendorName = IsWlanGroupDriver(printerId) ? VENDOR_WLAN_GROUP : vendorName;
    std::string globalVendorName = GetGlobalVendorName(targetVendorName);
    if (printServiceAbility != nullptr) {
        return printServiceAbility->OnVendorStatusUpdate(globalVendorName, printerId, status);
    }
    return true;
}

std::shared_ptr<VendorDriverBase> VendorManager::FindDriverByPrinterId(const std::string &globalPrinterId)
{
    std::string globalVendorName = ExtractGlobalVendorName(globalPrinterId);
    std::string vendorName = ExtractVendorName(globalVendorName);
    if (vendorName.empty()) {
        PRINT_HILOGW("Invalid printer id");
        return nullptr;
    }
    return FindDriverByVendorName(vendorName);
}

std::shared_ptr<VendorDriverBase> VendorManager::FindDriverByVendorName(const std::string &vendorName)
{
    std::lock_guard<std::mutex> lock(vendorMapMutex);
    if (vendorName == VENDOR_WLAN_GROUP) {
        return wlanGroupDriver;
    }
    auto iter = vendorMap.find(vendorName);
    if (iter == vendorMap.end()) {
        PRINT_HILOGW("cannot find vendor extension: %{public}s", vendorName.c_str());
        return nullptr;
    }
    return iter->second;
}

void VendorManager::StartStatusMonitor()
{
    PRINT_HILOGI("StartStatusMonitor Enter");
    {
        std::unique_lock<std::mutex> lock(statusMonitorMutex);
        if (statusMonitorOn) {
            PRINT_HILOGW("already on");
            return;
        }
        statusMonitorOn = true;
    }
    PRINT_HILOGI("StartStatusMonitor Now");
    statusMonitorThread = std::thread(&VendorManager::StatusMonitorProcess, this);
    PRINT_HILOGI("StartStatusMonitor Quit");
}

void VendorManager::StopStatusMonitor()
{
    PRINT_HILOGI("StopStatusMonitor Enter");
    {
        std::unique_lock<std::mutex> lock(statusMonitorMutex);
        statusMonitorOn = false;
    }
    statusMonitorCondition.notify_one();
    if (statusMonitorThread.joinable()) {
        statusMonitorThread.join();
    }
    PRINT_HILOGI("StopStatusMonitor Quit");
}

void VendorManager::StatusMonitorProcess()
{
    PRINT_HILOGI("StatusMonitorProcess Enter");
    while (WaitNext()) {
        UpdateAllPrinterStatus();
    }
    PRINT_HILOGI("StatusMonitorProcess Quit");
}

void VendorManager::UpdateAllPrinterStatus()
{
    std::lock_guard<std::mutex> lock(vendorMapMutex);
    for (auto const &pair : vendorMap) {
        if (pair.second == nullptr) {
            PRINT_HILOGW("vendor extension is null");
            continue;
        }
        pair.second->UpdateAllPrinterStatus();
    }
}

bool VendorManager::WaitNext()
{
    std::unique_lock<std::mutex> lock(statusMonitorMutex);
    if (!statusMonitorOn) {
        return false;
    }
    statusMonitorCondition.wait_for(lock, std::chrono::milliseconds(MONITOR_CHECK_INTERVAL_MS));
    if (!statusMonitorOn) {
        return false;
    }
    return true;
}

bool VendorManager::IsPrivatePpdDriver(const std::string &vendorName)
{
    return vendorName == VENDOR_PPD_DRIVER;
}

bool VendorManager::MonitorPrinterStatus(const std::string &globalPrinterId, bool on)
{
    std::string globalVendorName = ExtractGlobalVendorName(globalPrinterId);
    std::string printerId = ExtractPrinterId(globalPrinterId);
    if (globalVendorName.empty() || printerId.empty()) {
        PRINT_HILOGW("invalid printer id: %{private}s", globalPrinterId.c_str());
        return false;
    }
    std::string vendorName = ExtractVendorName(globalVendorName);
    if (vendorName.empty()) {
        PRINT_HILOGW("vendor name empty");
        return false;
    }
    auto vendorDriver = FindDriverByVendorName(vendorName);
    if (vendorDriver == nullptr) {
        PRINT_HILOGW("vendor driver is null");
        return false;
    }
    return vendorDriver->MonitorPrinterStatus(printerId, on);
}

bool VendorManager::IsConnectingPrinter(const std::string &globalPrinterIdOrIp, const std::string &uri)
{
    if (globalPrinterIdOrIp.find(VENDOR_BSUNI_DRIVER) != std::string::npos && wlanGroupDriver != nullptr) {
        return wlanGroupDriver->IsConnectingPrinter(globalPrinterIdOrIp, uri);
    }
    std::lock_guard<std::mutex> lock(simpleObjectMutex);
    if (isConnecting && !connectingPrinter.empty()) {
        if (connectingMethod == ID_AUTO) {
            return globalPrinterIdOrIp == connectingPrinter;
        } else {
            return uri.find(connectingPrinter) != std::string::npos;
        }
    }
    return false;
}

void VendorManager::SetConnectingPrinter(ConnectMethod method, const std::string &globalPrinterIdOrIp)
{
    if (globalPrinterIdOrIp.find(VENDOR_BSUNI_DRIVER) != std::string::npos && wlanGroupDriver != nullptr) {
        wlanGroupDriver->SetConnectingPrinter(method, globalPrinterIdOrIp);
        return;
    }
    std::lock_guard<std::mutex> lock(simpleObjectMutex);
    connectingMethod = method;
    connectingPrinter = globalPrinterIdOrIp;
    isConnecting = true;
}

void VendorManager::ClearConnectingPrinter()
{
    PRINT_HILOGD("ClearConnectingPrinter");
    std::lock_guard<std::mutex> lock(simpleObjectMutex);
    isConnecting = false;
}

bool VendorManager::QueryPrinterCapabilityByUri(const std::string &uri, PrinterCapability &printerCap)
{
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return false;
    }
    return printServiceAbility->QueryPrinterCapabilityByUri(uri, printerCap);
}

bool VendorManager::QueryPrinterStatusByUri(const std::string &uri, PrinterStatus &status)
{
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("printServiceAbility is null");
        return false;
    }
    return printServiceAbility->QueryPrinterStatusByUri(uri, status);
}

std::shared_ptr<PrinterInfo> VendorManager::QueryDiscoveredPrinterInfoById(const std::string &vendorName,
    const std::string &printerId)
{
    auto targetVendorName = IsWlanGroupDriver(printerId) ? VENDOR_WLAN_GROUP : vendorName;
    auto globalPrinterId = PrintUtils::GetGlobalId(VendorManager::GetGlobalVendorName(targetVendorName), printerId);
    return printServiceAbility->QueryDiscoveredPrinterInfoById(globalPrinterId);
}

int32_t VendorManager::QueryPrinterInfoByPrinterId(const std::string &vendorName, const std::string &printerId,
    PrinterInfo &info)
{
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("QueryPrinterInfoByPrinterId printServiceAbility is null");
        return false;
    }
    auto targetVendorName = IsWlanGroupDriver(printerId) ? VENDOR_WLAN_GROUP : vendorName;
    auto globalPrinterId = PrintUtils::GetGlobalId(VendorManager::GetGlobalVendorName(targetVendorName), printerId);
    return printServiceAbility->QueryPrinterInfoByPrinterId(globalPrinterId, info);
}

bool VendorManager::QueryPPDInformation(const char *makeModel, std::vector<std::string> &ppds)
{
    if (printServiceAbility == nullptr) {
        PRINT_HILOGW("QueryPPDInformation printServiceAbility is null");
        return false;
    }
    return printServiceAbility->QueryPPDInformation(makeModel, ppds);
}

bool VendorManager::IsWlanGroupDriver(const std::string &bothPrinterId)
{
    if (wlanGroupDriver == nullptr) {
        return false;
    }
    return wlanGroupDriver->IsGroupDriver(bothPrinterId);
}