/*

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



#include <algorithm>

#include <charconv>

#include <cstring>

#include <dirent.h>

#include <iomanip>

#include <iostream>

#include <map>



namespace OHOS {

namespace MMI {

namespace {

constexpr size_t EVENT_PREFIX_LENGTH = 5;

constexpr const char* EVENT_PREFIX = "event";

constexpr int32_t ID_WIDTH = 5;

constexpr int32_t PATH_WIDTH = 20;

constexpr int32_t TOTAL_WIDTH = 80;

constexpr int32_t INVALID_DEVICE_ID = -1;

constexpr const char* INPUT_DEVICE_DIR = "/dev/input";

}



int32_t DeviceManager::ExtractEventNumber(const std::string& fileName)

{

    if (fileName.length() < EVENT_PREFIX_LENGTH || fileName.substr(0, EVENT_PREFIX_LENGTH) != EVENT_PREFIX) {

        return INVALID_DEVICE_ID;

    }

    std::string numberPart = fileName.substr(EVENT_PREFIX_LENGTH);

    if (!std::all_of(numberPart.begin(), numberPart.end(), ::isdigit)) {

        return INVALID_DEVICE_ID;

    }

    int32_t eventNum;

    auto [ptr, ec] = std::from_chars(numberPart.data(), numberPart.data() + numberPart.size(), eventNum);

    if (ec == std::errc() &&

        ptr == numberPart.data() + numberPart.size() &&

        eventNum >= 0) {

        return eventNum;

    }

    return INVALID_DEVICE_ID;

}



std::vector<InputDevice> DeviceManager::DiscoverDevices()

{

    std::vector<InputDevice> devices;

    DIR* dir = opendir(INPUT_DEVICE_DIR);

    if (!dir) {

        PrintError("Cannot open /dev/input directory: %s", strerror(errno));

        return devices;

    }

    struct dirent* entry;

    while ((entry = readdir(dir)) != nullptr) {

        std::string fileName(entry->d_name);

        std::string path = BuildDevicePath(entry->d_name);

        int32_t eventNum = ExtractEventNumber(fileName);

        if (eventNum >= 0) {

            InputDevice device(path, static_cast<uint32_t>(eventNum));

            if (device.IsOpen()) {

                devices.push_back(std::move(device));

            }

        }

    }

    closedir(dir);

    std::sort(devices.begin(), devices.end(),

        [](const InputDevice& a, const InputDevice& b) {

            return a.GetId() < b.GetId();

        });

    return devices;

}



void DeviceManager::PrintDeviceList()

{

    auto devices = DiscoverDevices();

    if (devices.empty()) {

        std::cout << "No input devices found." << std::endl;

        return;

    }

    std::cout << "Available input devices:" << std::endl;

    std::cout << std::setw(ID_WIDTH) << "ID" << " | "

              << std::setw(PATH_WIDTH) << "Path" << " | "

              << "Name" << std::endl;

    std::cout << std::string(TOTAL_WIDTH, '-') << std::endl;

    for (const auto& device : devices) {

        std::cout << std::setw(ID_WIDTH) << device.GetId() << " | "

                  << std::setw(PATH_WIDTH) << device.GetPath() << " | "

                  << device.GetName() << std::endl;

    }

}



std::string DeviceManager::BuildDevicePath(const std::string& fileName) const

{

    return std::string(INPUT_DEVICE_DIR) + "/" + fileName;

}

} // namespace MMI

} // namespace OHOS