/*
 * Copyright (c) 2026 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 <cstring>
#include <getopt.h>
#include <iomanip>
#include <iostream>

#include "file.h"
#include "logger.h"
#include "option.h"

using namespace OHOS::Hardware;
static constexpr int HCS_COMPILER_VERSION_MAJOR = 00;
static constexpr int HCS_COMPILER_VERSION_MINOR = 7;
static constexpr int ARG_COUNT_MIN = 2;
static constexpr int SPACE_WIDTH = 12;

Option &Option::Instance()
{
    static Option option;
    return option;
}

static constexpr int OPTION_END = -1;
static constexpr const char *HCS_SUPPORT_ARGS = "o:ap:bditmvVhs";

Option &Option::Parse(int argc, char **argv)
{
    do {
        if (argc < ARG_COUNT_MIN) {
            showUsage_ = true;
            break;
        }

        if (!ParseOptions(argc, argv)) {
            break;
        }

        if (optind >= argc || optind < 0) {
            Logger().Error() << "Miss input file name";
            SetOptionError();
            break;
        }
        SetSourceOption(argv[optind]);
        return *this;
    } while (false);

    return *this;
}

bool Option::ParseOptions(int argc, char **argv)
{
    int32_t op = 0;
    while (op != OPTION_END) {
        op = getopt(argc, argv, HCS_SUPPORT_ARGS);
        SetOptionData(op);
        if (ShouldShowUsage() || ShouldShowVersion()) {
            return false;
        }
    }

    return true;
}

void Option::SetOptionData(char op)
{
    switch (op) {
        case 'o':
            outputName_ = optarg;
            break;
        case 'a':
            shouldAlign_ = true;
            break;
        case 'b':
            shouldGenByteCodeConfig_ = true;
            break;
        case 't':
            shouldGenTextConfig_ = true;
            shouldGenByteCodeConfig_ = false;
            shouldGenMacroConfig_ = false;
            break;
        case 'm':
            shouldGenTextConfig_ = false;
            shouldGenByteCodeConfig_ = false;
            shouldGenMacroConfig_ = true;
            break;
        case 'p':
            symbolNamePrefix_ = optarg;
            break;
        case 'i':
            showGenHexDump_ = true;
            break;
        case 'V':
            verboseLog_ = true;
            break;
        case 'd':
            shouldDecompile_ = true;
            break;
        case 'v':
            showVersion_ = true;
            break;
        case 'h': /* fall-through */
            showUsage_ = true;
            break;
        case 's':
            genStartCfg_ = true;
            shouldGenByteCodeConfig_ = false;
            break;
        case '?':
            SetOptionError();
            break;
        default:
            break;
    }
}

void Option::ShowUsage()
{
    Logger() << "Usage: hc-gen [Options] [File]\n"
             << "options:";
    ShowOption("-o <file>", "output file name, default same as input");
    ShowOption("-a", "hcb align with four bytes");
    ShowOption("-b", "output binary output, default enable");
    ShowOption("-t", "output config in C language source file style");
    ShowOption("-m", "output config in macro file style");
    ShowOption("-s", "output start config of host");
    ShowOption("-i", "output binary hex dump in C language source file style");
    ShowOption("-p <prefix>", "prefix of generated symbol name");
    ShowOption("-d", "decompile hcb to hcs");
    ShowOption("-V", "show verbose info");
    ShowOption("-v", "show version");
    ShowOption("-h", "show this help message");
}

void Option::ShowOption(const ::std::string &option, const ::std::string &helpInfo)
{
    Logger() << "  " << ::std::setw(SPACE_WIDTH) << ::std::left << option << "  " << helpInfo;
}

bool Option::ShouldShowUsage() const
{
    return showUsage_;
}

void Option::ShowVersion()
{
    Logger() << "Hcs compiler " << HCS_COMPILER_VERSION_MAJOR << "." << HCS_COMPILER_VERSION_MINOR;
    Logger() << "Copyright (c) 2020-2021 Huawei Device Co., Ltd.";
}

bool Option::OptionError() const
{
    return optionError_;
}

bool Option::ShouldShowVersion() const
{
    return showVersion_;
}

bool Option::ShouldAlign() const
{
    return shouldAlign_;
}

bool Option::ShouldGenTextConfig() const
{
    return shouldGenTextConfig_;
}

bool Option::ShouldGenMacroConfig() const
{
    return shouldGenMacroConfig_;
}

bool Option::ShouldGenBinaryConfig() const
{
    return shouldGenByteCodeConfig_;
}

bool Option::ShouldGenHexDump() const
{
    return showGenHexDump_;
}

bool Option::ShouldDecompile() const
{
    return shouldDecompile_;
}

bool Option::ShouldGenStartConfig() const
{
    return genStartCfg_;
}

std::string Option::GetSymbolPrefix() const
{
    return symbolNamePrefix_;
}

bool Option::VerboseLog() const
{
    return verboseLog_;
}

void Option::SetOptionError(bool shouldShowUsage)
{
    showUsage_ = shouldShowUsage;
    optionError_ = true;
}

std::string Option::GetSourceName() const
{
    return sourceName_;
}

std::string Option::GetSourceNameBase() const
{
    return sourceNameBase_;
}

std::string Option::GetOutputName() const
{
    return outputName_;
}

bool Option::SetSourceOption(const char *srcName)
{
    std::string srcAbsPath(Util::File::AbsPath(srcName));
    if (srcAbsPath.empty()) {
        Logger().Error() << "invalid source file: " << srcName << ", " << strerror(errno);
        SetOptionError(false);
        return false;
    }

    sourceName_ = srcAbsPath;
    sourceNameBase_ = Util::File::FileNameBase(srcAbsPath);
    return true;
}

void Option::GetVersion(uint32_t &minor, uint32_t &major)
{
    minor = HCS_COMPILER_VERSION_MINOR;
    major = HCS_COMPILER_VERSION_MAJOR;
}