#include "vm_parse.h"

namespace NSc::NUt {
#define Y_TRY_READ_BITS(state, out, bits, res) do { if (!state.Input.Read(out, bits)) { return res; } } while (false)
#define Y_TRY_READ_BITS_BOOL(state, out, bits) Y_TRY_READ_BITS(state, out, bits, false)
#define Y_TRY_READ_BITS_MAYBE(state, out, bits) Y_TRY_READ_BITS(state, out, bits, Nothing())

    TMaybe<TIdx> ParseIdx(TVMState& st) {
        static_assert(IsPowerOf2(TIdx::ValueCount));
        static const auto bits = GetCountWidth(TIdx::ValueCount);

        TIdx idx;
        if (!st.Input.Read(idx.Idx, bits)) {
            return Nothing();
        }
        return idx;
    }

    namespace {
        bool DoParsePos(TVMState& st, TPos& pos) {
            static const auto bits = GetCountWidth(TPos::ValueCount);
            const ui32 sz = st.Memory.size();

            ui32 neg = -1;
            Y_TRY_READ_BITS_BOOL(st, neg, 1);

            ui32 delta = -1;
            Y_TRY_READ_BITS_BOOL(st, delta, bits);

            if (neg) {
                if (st.Pos < delta) {
                    return false;
                }
                pos.Pos = st.Pos - delta;
            } else {
                if (st.Pos + delta >= sz) {
                    return false;
                }
                pos.Pos = st.Pos + delta;
            }

            return true;
        }

        template <class T>
        bool DoParseType(TVMState& state, T& res) {
            static const auto bits = GetCountWidth(T::TypeCount);

            ui32 type = -1;
            if (state.Input.Read(type, bits) && type < T::TypeCount) {
                res.Type = (typename T::EType)(type);
                return true;
            } else {
                return false;
            }
        }
    }

    TMaybe<TPos> ParsePos(TVMState& state) {
        TPos res;
        if (DoParsePos(state, res)) {
            return res;
        }
        return Nothing();
    }

    TMaybe<TRef> ParseRef(TVMState& state) {
        TRef ref;
        if (!DoParseType(state, ref)) {
            return Nothing();
        }

        switch (ref.Type) {
        case TRef::T_REF__POS:
            if (!DoParsePos(state, ref)) {
                return Nothing();
            }
            [[fallthrough]];
        case TRef::T_CREATE_FRONT:
        case TRef::T_CREATE_BACK:
            return ref;
        default:
            Y_FAIL();
        }
    }

    TMaybe<TSrc> ParseSrc(TVMState& state) {
        TSrc src;
        if (!DoParseType(state, src)) {
            return Nothing();
        }

        switch (src.Type) {
        case TSrc::T_LREF__POS:
        case TSrc::T_CREF__POS:
        case TSrc::T_RREF__POS:
            if (!DoParsePos(state, src)) {
                return Nothing();
            }
            return src;
        default:
            Y_FAIL();
        }
    }

    TMaybe<TDst> ParseDst(TVMState& state) {
        TDst dst;
        if (!DoParseType(state, dst)) {
            return Nothing();
        }

        switch (dst.Type) {
        case TDst::T_LREF__POS:
        case TDst::T_CREF__POS:
        case TDst::T_RREF__POS:
            if (!DoParsePos(state, dst)) {
                return Nothing();
            }
            [[fallthrough]];
        case TDst::T_CREATE_FRONT_LREF:
        case TDst::T_CREATE_FRONT_CREF:
        case TDst::T_CREATE_FRONT_RREF:
        case TDst::T_CREATE_BACK_LREF:
        case TDst::T_CREATE_BACK_CREF:
        case TDst::T_CREATE_BACK_RREF:
            return dst;
        default:
            Y_FAIL();
        }
    }

    TMaybe<TPath> ParsePath(TVMState& state) {
        static const ui32 bits = GetCountWidth(TPath::MaxLength);
        TPath path;

        ui32 len = -1;
        Y_TRY_READ_BITS_MAYBE(state, len, bits);
        while (len--) {
            ui8 c;
            Y_TRY_READ_BITS_MAYBE(state, c, 8);
            path.Path.push_back(c);
        }
        return path;
    }

    TMaybe<TVMAction> ParseNextAction(TVMState& state) {
        TVMAction res;

        if (!DoParseType(state, res)) {
            return Nothing();
        }

        switch (res.Type) {
        case VMA_CREATE_BACK:
        case VMA_CREATE_FRONT:
        case VMA_DESTROY_BACK:
        case VMA_DESTROY_FRONT:

        case VMA_SET_DICT:
        case VMA_SET_ARRAY:
        case VMA_SET_NULL:
        case VMA_GET_JSON:

        case VMA_ARRAY_CLEAR:
        case VMA_ARRAY_PUSH:
        case VMA_DICT_CLEAR:
            return res;

        case VMA_JMP__POS:
        case VMA_MERGE_UPDATE__POS:
        case VMA_MERGE_REVERSE__POS:
        case VMA_MERGE_COPY_FROM__POS:
        case VMA_SWAP__POS:
            if (res.SetArg(ParsePos(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_ARRAY_POP__REF:
            if (res.SetArg(ParseRef(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_SET_STRING__IDX:
        case VMA_SET_INT_NUMBER__IDX:
        case VMA_ARRAY_INSERT__IDX:
        case VMA_ARRAY_GET_OR_ADD__IDX:
        case VMA_DICT_GET_OR_ADD__IDX:
            if (res.SetArg(ParseIdx(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_CREATE_BACK__SRC:
        case VMA_CREATE_FRONT__SRC:
        case VMA_ASSIGN__SRC:
        case VMA_ARRAY_PUSH__SRC:
            if (res.SetArg(ParseSrc(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_ARRAY_PUSH__DST:
            if (res.SetArg(ParseDst(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_ARRAY_DELETE__IDX_REF:
        case VMA_ARRAY_GET__IDX_REF:
        case VMA_ARRAY_GET_NO_ADD__IDX_REF:
        case VMA_DICT_DELETE__IDX_REF:
        case VMA_DICT_GET__IDX_REF:
        case VMA_DICT_GET_NO_ADD__IDX_REF:
            if (res.SetArg(ParseIdx(state)) && res.SetArg(ParseRef(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_ARRAY_INSERT__IDX_SRC:
        case VMA_ARRAY_GET_OR_ADD__IDX_SRC:
        case VMA_DICT_GET_OR_ADD__IDX_SRC:
            if (res.SetArg(ParseIdx(state)) && res.SetArg(ParseSrc(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_ARRAY_INSERT__IDX_DST:
        case VMA_ARRAY_GET_OR_ADD__IDX_DST:
        case VMA_DICT_GET_OR_ADD__IDX_DST:
            if (res.SetArg(ParseIdx(state)) && res.SetArg(ParseDst(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_EQUAL__POS_POS:
            if (res.SetArg(ParsePos(state)) && res.SetArg(ParsePos(state))) {
                return res;
            } else {
                return Nothing();
            }

        case VMA_SELECT_NO_ADD__PATH_REF:
        case VMA_SELECT_OR_ADD__PATH_REF:
        case VMA_SELECT_AND_DELETE__PATH_REF:
            if (res.SetArg(ParsePath(state)) && res.SetArg(ParseRef(state))) {
                return res;
            } else {
                return Nothing();
            }

        default:
            return Nothing();
        }
    }
}