/* common/KMOptions.cpp
 *
 * Copyright (C) 2023 OpenKylin, Inc
 */

#include "KMOptions.h"

#include <iostream>

KMOptions::KMOptions()
{
}

KMOptions::~KMOptions()
{
}

int KMOptions::getPositionalOptionIndex(int numOfPositionalOption)
{
    int num = 0;
    for (int i = 1; i < m_argc; ++i)
    {
        if (m_argv[i][0] != '-')
        {
            ++num;
        }

        if (num == numOfPositionalOption)
        {
            return i;
        }
    }

    return 0;
}

void KMOptions::parseCommandLine(int argc, char **argv)
{
    m_argc = argc;
    m_argv = argv;

    // m_optionsDescription所拥有的对象必需在子类中构建出来，因为其usage说明只能在构建时指明；可以放到preParseHook()中在子类中处理
    // m_optionsDescription = std::make_unique<opt::options_description>(_("\nUsage:\n \tkaiming [OPTION…] SUBCOMMAND ... \n\nOptions"));
    m_optionsPositionalDescription = std::make_unique<opt::positional_options_description>();

    preParseHook();

    try
    {
        opt::store(opt::command_line_parser(m_argc, m_argv).options(*m_optionsDescription).positional(*m_optionsPositionalDescription).allow_unregistered().run(), m_varsMap);
    }
    catch (const opt::error &e)
    {
        // 处理异常，例如输出错误信息或退出程序
        std::cerr << "Error occurred during parsing commandline, error massage : " << e.what() << std::endl;
        showUsage();
        exit(EXIT_FAILURE);
    }
    catch (const std::exception &e)
    {
        // 处理其他异常类型，例如内存不足等
        std::cerr << "Error occurred during parsing commandline, error massage : " << e.what() << std::endl;
        showUsage();
        exit(EXIT_FAILURE);
    }
    catch (...)
    {
        std::cerr << "Unknown error." << std::endl;
        showUsage();
        exit(EXIT_FAILURE);
    }
    //参数解析完成后，通知variables_map去更新所有的外部变量
    opt::notify(m_varsMap);

    postParseHook();
}

void KMOptions::checkOption(const opt::variables_map &varsMap, const char *longOption, const char *shortOption, bool &isSet)
{
    if (longOption != nullptr && varsMap.count(longOption) || shortOption != nullptr && varsMap.count(shortOption))
    {
        isSet = true;
    }
}

void KMOptions::showCmdline() const
{
    std::cout << "cmdline:";
    for (int i = 0; i < m_argc; ++i)
    {
        std::cout << " ";
        std::cout << m_argv[i];
    }
    std::cout << std::endl;
}

void KMOptions::showUsage() const
{
    if (m_optionsShowDescription)
    {
        std::cout << *m_optionsShowDescription << std::endl;
    }
    else if (m_optionsDescription)
    {
        std::cout << *m_optionsDescription << std::endl;
    }
    std::cout << std::endl;
}
