#include "vm_apply.h"

namespace NSc::NUt {

#define Y_GEN_TRY_OP(op) \
    if (!op) { return false; }

#define Y_GEN_SRC_OP(op, arg, st, act) \
    switch (act.GetSrc(arg).Type) { \
    case TSrc::T_LREF__POS: \
        op(st.LRef(act.GetSrc(arg).Pos)); \
        break; \
    case TSrc::T_CREF__POS: \
        op(st.CRef(act.GetSrc(arg).Pos)); \
        break; \
    case TSrc::T_RREF__POS: \
        op(st.RRef(act.GetSrc(arg).Pos)); \
        break; \
    default: \
        Y_ABORT(); \
    }


#define Y_GEN_SRC_TRY_OP(op, arg, st, act) \
    if (st.CRef(act.GetSrc(arg).Pos).IsSameOrAncestorOf(st.Current())) { \
        return false; \
    } \
    Y_GEN_SRC_OP(op, arg, st, act);


#define Y_GEN_DST_OP(op, arg, st, act) \
    switch (act.GetDst(arg).Type) { \
    case TDst::T_CREATE_BACK_LREF: \
        Y_GEN_TRY_OP(st.TryPushBack(op)) \
        break; \
    case TDst::T_CREATE_BACK_CREF: \
        Y_GEN_TRY_OP(st.TryPushBack(std::as_const(op))) \
        break; \
    case TDst::T_CREATE_BACK_RREF: \
        Y_GEN_TRY_OP(st.TryPushBack(std::move(op))) \
        break; \
    case TDst::T_CREATE_FRONT_LREF: \
        Y_GEN_TRY_OP(st.TryPushFront(op)) \
        break; \
    case TDst::T_CREATE_FRONT_CREF: \
        Y_GEN_TRY_OP(st.TryPushFront(std::as_const(op))) \
        break; \
    case TDst::T_CREATE_FRONT_RREF: \
        Y_GEN_TRY_OP(st.TryPushFront(std::move(op))) \
        break; \
    case TDst::T_LREF__POS: \
        st.LRef(act.GetDst(arg).Pos) = op; \
        break; \
    case TDst::T_CREF__POS: \
        st.LRef(act.GetDst(arg).Pos) = std::as_const(op); \
        break; \
    case TDst::T_RREF__POS: \
        st.LRef(act.GetDst(arg).Pos) = std::move(op); \
        break; \
    default: \
        Y_ABORT(); \
    }


#define Y_GEN_REF_OP(op, arg, st, act) \
    switch (act.GetRef(arg).Type) { \
    case TRef::T_CREATE_BACK: \
        Y_GEN_TRY_OP(st.TryPushBack(op)) \
        break; \
    case TRef::T_CREATE_FRONT: \
        Y_GEN_TRY_OP(st.TryPushFront(op)) \
        break; \
    case TRef::T_REF__POS: \
        st.LRef(act.GetRef(arg).Pos) = op; \
        break; \
    default: \
        Y_ABORT(); \
    }

#define Y_GEN_PTR_OP(op, arg, st, act) \
    if (auto* r = (op)) { \
        switch (act.GetRef(arg).Type) { \
        case TRef::T_CREATE_BACK: \
            Y_GEN_TRY_OP(st.TryPushBack(*r)) \
            break; \
        case TRef::T_CREATE_FRONT: \
            Y_GEN_TRY_OP(st.TryPushFront(*r)) \
            break; \
        case TRef::T_REF__POS: \
            st.LRef(act.GetRef(arg).Pos) = *r; \
            break; \
        default: \
            Y_ABORT(); \
        } \
    }

    bool ApplyNextAction(TVMState& st, TVMAction act) {
        switch (act.Type) {
        case VMA_JMP__POS:
            st.Pos = act.GetPos(0);
            return true;

        case VMA_CREATE_BACK:
            Y_GEN_TRY_OP(st.TryPushBack())
            return true;

        case VMA_CREATE_BACK__SRC:
            switch (act.GetSrc(0).Type) {
            case TSrc::T_LREF__POS:
                Y_GEN_TRY_OP(st.TryPushBack(st.LRef(act.GetSrc(0).Pos)))
                break;
            case TSrc::T_CREF__POS:
                Y_GEN_TRY_OP(st.TryPushBack(st.CRef(act.GetSrc(0).Pos)))
                break;
            case TSrc::T_RREF__POS:
                Y_GEN_TRY_OP(st.TryPushBack(st.RRef(act.GetSrc(0).Pos)))
                break;
            default:
                Y_ABORT();
            }

            return true;

        case VMA_CREATE_FRONT:
            Y_GEN_TRY_OP(st.TryPushFront())
            return true;

        case VMA_CREATE_FRONT__SRC:
            switch (act.GetSrc(0).Type) {
            case TSrc::T_LREF__POS:
                Y_GEN_TRY_OP(st.TryPushFront(st.LRef(act.GetSrc(0).Pos)))
                break;
            case TSrc::T_CREF__POS:
                Y_GEN_TRY_OP(st.TryPushFront(st.CRef(act.GetSrc(0).Pos)))
                break;
            case TSrc::T_RREF__POS:
                Y_GEN_TRY_OP(st.TryPushFront(st.RRef(act.GetSrc(0).Pos)))
                break;
            default:
                Y_ABORT();
            }
            return true;

        case VMA_DESTROY_BACK:
            return st.TryPopBack();

        case VMA_DESTROY_FRONT:
            return st.TryPopFront();

        case VMA_ASSIGN__SRC:
            Y_GEN_SRC_OP(st.Current() = , 0, st, act);
            return true;

        case VMA_SET_STRING__IDX:
            st.Current().SetString(act.GetString(0));
            return true;

        case VMA_SET_INT_NUMBER__IDX:
            st.Current().SetIntNumber(act.GetIntNumber(0));
            return true;

        case VMA_SET_DICT:
            st.Current().SetDict();
            return true;

        case VMA_SET_ARRAY:
            st.Current().SetArray();
            return true;

        case VMA_SET_NULL:
            st.Current().SetNull();
            return true;

        case VMA_GET_JSON:
            st.Current().ToJson();
            return true;

        case VMA_ARRAY_CLEAR:
            st.Current().ClearArray();
            return true;

        case VMA_ARRAY_PUSH:
            st.Current().Push();
            return true;

        case VMA_ARRAY_PUSH__SRC:
            Y_GEN_SRC_TRY_OP(st.Current().Push, 0, st, act);
            return true;

        case VMA_ARRAY_PUSH__DST:
            Y_GEN_DST_OP(st.Current().Push(), 0, st, act);
            return true;

        case VMA_ARRAY_POP__REF:
            Y_GEN_REF_OP(st.Current().Pop(), 0, st, act);
            return true;

        case VMA_ARRAY_INSERT__IDX:
            st.Current().Insert(act.GetIdx(0));
            return true;

        case VMA_ARRAY_INSERT__IDX_SRC:
            Y_GEN_SRC_TRY_OP(st.Current().Insert(act.GetIdx(0)) = , 1, st, act);
            return true;

        case VMA_ARRAY_INSERT__IDX_DST:
            Y_GEN_DST_OP(st.Current().Insert(act.GetIdx(0)), 1, st, act);
            return true;

        case VMA_ARRAY_DELETE__IDX_REF:
            Y_GEN_REF_OP(st.Current().Delete(act.GetIdx(0)), 1, st, act);
            return true;

        case VMA_ARRAY_GET__IDX_REF:
            Y_GEN_REF_OP(st.Current().Get(act.GetIdx(0)), 1, st, act);
            return true;

        case VMA_ARRAY_GET_OR_ADD__IDX:
            st.Current().GetOrAdd(act.GetIdx(0));
            return true;

        case VMA_ARRAY_GET_OR_ADD__IDX_SRC:
            Y_GEN_SRC_TRY_OP(st.Current().GetOrAdd(act.GetIdx(0)) = , 1, st, act);
            return true;

        case VMA_ARRAY_GET_OR_ADD__IDX_DST:
            Y_GEN_DST_OP(st.Current().GetOrAdd(act.GetIdx(0)), 1, st, act);
            return true;

        case VMA_ARRAY_GET_NO_ADD__IDX_REF:
            Y_GEN_PTR_OP(st.Current().GetNoAdd(act.GetIdx(0)), 1, st, act);
            return true;

        case VMA_DICT_CLEAR:
            st.Current().ClearDict();
            return true;

        case VMA_DICT_DELETE__IDX:
            st.Current().Delete(act.GetKey(0));
            return true;

        case VMA_DICT_DELETE__IDX_REF:
            Y_GEN_REF_OP(st.Current().Delete(act.GetKey(0)), 1, st, act);
            return true;

        case VMA_DICT_GET__IDX_REF:
            Y_GEN_REF_OP(st.Current().Get(act.GetKey(0)), 1, st, act);
            return true;

        case VMA_DICT_GET_OR_ADD__IDX:
            st.Current().GetOrAdd(act.GetKey(0));
            return true;

        case VMA_DICT_GET_OR_ADD__IDX_SRC:
            Y_GEN_SRC_TRY_OP(st.Current().GetOrAdd(act.GetKey(0)) = , 1, st, act);
            return true;

        case VMA_DICT_GET_OR_ADD__IDX_DST:
            Y_GEN_DST_OP(st.Current().GetOrAdd(act.GetKey(0)), 1, st, act);
            return true;

        case VMA_DICT_GET_NO_ADD__IDX_REF:
            Y_GEN_PTR_OP(st.Current().GetNoAdd(act.GetKey(0)), 1, st, act);
            return true;

        case VMA_MERGE_UPDATE__POS:
            st.Current().MergeUpdate(st.LRef(act.GetPos(0)));
            return true;

        case VMA_MERGE_REVERSE__POS:
            st.Current().ReverseMerge(st.LRef(act.GetPos(0)));
            return true;

        case VMA_MERGE_COPY_FROM__POS:
            st.Current().CopyFrom(st.LRef(act.GetPos(0)));
            return true;

        case VMA_SWAP__POS:
            st.Current().Swap(st.LRef(act.GetPos(0)));
            return true;

        case VMA_EQUAL__POS_POS:
            TValue::Equal(st.CRef(act.GetPos(0)), st.CRef(act.GetPos(1)));
            return true;

        case VMA_SELECT_NO_ADD__PATH_REF:
            Y_GEN_REF_OP(st.Current().TrySelect(act.GetPath(0)), 1, st, act);
            return true;

        case VMA_SELECT_OR_ADD__PATH_REF:
            Y_GEN_PTR_OP(st.Current().TrySelectOrAdd(act.GetPath(0)), 1, st, act);
            return true;

        case VMA_SELECT_AND_DELETE__PATH_REF:
            Y_GEN_REF_OP(st.Current().TrySelectAndDelete(act.GetPath(0)), 1, st, act);
            return true;

        default:
            Y_ABORT();
        }
    }
}