#include "processing_tree.h"

#include <util/charset/unidata.h>
#include <util/string/cast.h>

using TProcessingTree = NBalancerYt::NProto::TProcessingTree;
using TPayload = NBalancerYt::NProto::TProcessingTree::TPayload;

bool TryParsePayload(TStringBuf s, size_t& offset, TPayload& payload);

bool TryParseValue(TStringBuf s, size_t& offset, TStringBuf& value) {
    size_t size = 0;
    while (offset + size < s.length()) {
        if (IsSpace(s[offset + size]) || s[offset + size] == ']') {
            break;
        }
        ++size;
    }
    if (size) {
        value = s.SubStr(offset, size);
        offset += size;
        return true;
    }
    return false;
}

bool TryParseTree(TStringBuf s, size_t& offset, TProcessingTree& tree) {
    if (offset >= s.length() || s[offset] != '[') {
        return false;
    }
    ++offset;
    TStringBuf moduleName;
    if (!TryParseValue(s, offset, moduleName)) {
        return false;
    }
    tree.SetModuleName(ToString(moduleName));
    while (offset < s.length() && s[offset] != ']') {
        if (!TryParsePayload(s, offset, *tree.AddPayload())) {
            tree.MutablePayload()->DeleteSubrange(tree.PayloadSize() - 1, 1);
            break;
        }
    }
    if (offset < s.length() && s[offset] == ']') {
        ++offset;
        return true;
    } else {
        return false;
    }
}

bool TryParsePayload(TStringBuf s, size_t& offset, TPayload& payload) {
    while (offset < s.length()) {
        if (IsSpace(s[offset])) {
            ++offset;
            continue;
        }
        if (s[offset] == '[') {
            return TryParseTree(s, offset, *payload.MutableTree());
        } else {
            TStringBuf tmp;
            if (TryParseValue(s, offset, tmp)) {
                payload.SetValue(ToString(tmp));
                return true;
            }
            return false;
        }
    }

    return false;
}

bool NBalancerYt::TryParseProcessingTree(TStringBuf s, TProcessingTree& tree) {
    TPayload payload;
    size_t offset = 0;
    if (!TryParsePayload(s, offset, payload) || offset != s.length() || !payload.HasTree()) {
        return false;
    }
    tree.Swap(payload.MutableTree());
    return true;
}
