#include "util.h"

#include <util/string/subst.h>

int TVirtualArgs::SplitArgsLikeGdb(int argc, char* argv[], TString& s) {
    int splitPos = 0;
    for (int i = 1; i < argc; ++i) {
        if (TStringBuf(argv[i]) == "--args") {
            splitPos = i;
            break;
        }
    }

    if (splitPos) {
        TStringStream ss;
        for (int i = splitPos + 1; i < argc; ++i) {
            if (!ss.empty()) {
                ss << " ";
            }

            TString part = argv[i];
            SubstGlobal(part, "\\", "\\\\");
            SubstGlobal(part, " ", "\\ ");
            ss << part;
        }

        argc = splitPos;
        s = ss.Str();
    } else {
        s.clear();
    }
    return argc;
}

void TVirtualArgs::Unescape(TStringStream& o, const TStringBuf part) {
    const size_t len = part.size();
    size_t begin = 0;
    size_t end = part.find('\\', begin);
    for (; end < len; end = part.find('\\', begin)) {
        if (end == TString::npos)
            break; // never happens

        if (end + 1 >= len)
            continue; // ignore trailing '/'

        if (end > begin)
            o << part.SubStr(begin, end - begin);

        begin = end + 1;
        o << part[begin];
        begin++;
    }

    if (end == TString::npos)
        end = len;

    if (end > begin)
        o << part.SubStr(begin, end - begin);
}

TStringBuf TVirtualArgs::NextTokEscaped(TStringBuf& escapedVar) {
    const size_t len = escapedVar.size();
    bool afterEsc = false;
    size_t pos = 0;
    for (; pos < len; ++pos) {
        if (afterEsc) {
            afterEsc = false;
        } else if (escapedVar[pos] == '\\') {
            afterEsc = true;
        } else if (escapedVar[pos] == ' ') {
            break;
        }
    }
    TStringBuf result = escapedVar.SubString(0, pos);
    escapedVar = escapedVar.Skip(pos + 1);
    return result;
}

TVirtualArgs::TVirtualArgs(TStringBuf escapedVar) {
    TStringStream ss;
    TStringBuf part;

    ArgsStorage.push_back(TString()); // argv[0] is empty
    while ((part = NextTokEscaped(escapedVar))) {
        ss.clear();
        Unescape(ss, part);
        ArgsStorage.push_back(std::move(ss.Str()));
    }

    for (const TString& s : ArgsStorage) {
        ArgV.push_back(s.data());
    }
}

void TVirtualArgs::GetArgs(int& argc, const char**& argv) const {
    argc = ArgV.size();
    argv = const_cast<const char**>(&*ArgV.begin());
}
