/*
 * Copyright (c) 2025 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.
 */

#ifndef ECMASCRIPT_ENUM_CACHE_H
#define ECMASCRIPT_ENUM_CACHE_H
 
#include "ecmascript/ecma_macros.h"
#include "ecmascript/mem/barriers.h"

// EnumCacheKind is only used in for-in.
enum class EnumCacheKind : uint8_t {
    NONE,          // No Cache, go slow path
    SIMPLE,        // Simple enum cache
    PROTOCHAIN,    // Prototype chain info enum cache
};

// +---------------+-------------------------------------------+-------------------------------------------------+
// | EnumCacheKind | Status Description                        | Valid Conditions                                |
// +---------------+-------------------------------------------+-------------------------------------------------+
// | None          | - Uninitialized or in SlowPath mode.      | - None (No additional conditions required)      |
// |               |   No active cache optimization.           |                                                 |
// +---------------+-------------------------------------------+-------------------------------------------------+
// | SIMPLE        | - Fast path for simple enum cache check.  | - Receiver's proto is NOT a HeapObject,         |
// |               |   Indicates:                              |   OR                                            |
// |               |   • Properties exist ONLY on the object   | - Receiver.proto.ProtoChainInfoEnumCache ==     |
// |               |   itself (no prototype chain properties)  |   Undefined. (Ensure no keys on prototype chain)|
// |               |   • NO elements (indexed properties) on   |                                                 |
// |               |     self/proto                            |                                                 |
// +---------------+-------------------------------------------+-------------------------------------------------+
// | PROTOCHAIN    | - Fast path for protochain enum cache.    | - Receiver's proto is a HeapObject, AND         |
// |               |   Indicates:                              | - Receiver.ProtoChainInfoEnumCache ==           |
// |               |   • Properties exist on the object itself |   receiver.proto.EnumCacheAll (Cache matches    |
// |               |   or prototype chain                      |   prototype's full enum cache), AND             |
// |               |   • NO elements (indexed properties) on   | - Receiver.ProtoChainInfoEnumCache != Null      |
// |               |     self/proto                            |   (Valid cache exists).                         |
// +---------------+-------------------------------------------+-------------------------------------------------+

namespace panda {
namespace ecmascript {
class EnumCache : public TaggedObject {
public:
    CAST_CHECK(EnumCache, IsEnumCache);

    static constexpr size_t ENUM_CACHE_OWN_OFFSET = TaggedObjectSize();
    
    static bool CheckSelfAndProtoEnumCache(const JSThread *thread, JSTaggedValue enumCacheOwn,
        JSTaggedValue enumCacheProto)
    {
        if (!enumCacheOwn.IsEnumCache() || !enumCacheProto.IsEnumCache()) {
            return false;
        }
        auto keyOwn =  EnumCache::Cast(enumCacheOwn.GetTaggedObject())->GetProtoChainInfoEnumCache(thread);
        auto keyProto = EnumCache::Cast(enumCacheProto.GetTaggedObject())->GetEnumCacheAll(thread);
        if (keyOwn != keyProto || keyOwn == JSTaggedValue::Null()) {
            return false;
        }
        return true;
    }

    inline bool IsEnumCacheAllValid(const JSThread *thread) const
    {
        return GetEnumCacheAll(thread) != JSTaggedValue::Null();
    }

    inline bool IsEnumCacheOwnValid(const JSThread *thread) const
    {
        return GetEnumCacheOwn(thread) != JSTaggedValue::Null();
    }

    inline bool IsEnumCacheProtoInfoUndefined(const JSThread *thread) const
    {
        return GetProtoChainInfoEnumCache(thread) == JSTaggedValue::Undefined();
    }

    inline void SetInvalidState(const JSThread *thread)
    {
        SetEnumCacheAll(thread, JSTaggedValue::Null());
        SetProtoChainInfoEnumCache(thread, JSTaggedValue::Null());
    }

    // EnumCacheOwn holds enum keys on itself.
    ACCESSORS(EnumCacheOwn, ENUM_CACHE_OWN_OFFSET, ENUM_CACHE_ALL_OFFSET);

    // EnumCacheAll holds all enum keys on the prototype chain, including its own keys.
    ACCESSORS(EnumCacheAll, ENUM_CACHE_ALL_OFFSET, PROTO_CHAIN_INFO_ENUM_CACHE_OFFSET);
    
    // ProtoChainInfoEnumCache holds all enum keys on the prototype chain, excluding its own keys.
    ACCESSORS(ProtoChainInfoEnumCache, PROTO_CHAIN_INFO_ENUM_CACHE_OFFSET, ENUM_CACHE_KIND_OFFSET);
    ACCESSORS_PRIMITIVE_FIELD(EnumCacheKind, uint32_t, ENUM_CACHE_KIND_OFFSET, LAST_OFFSET);

    DEFINE_ALIGN_SIZE(LAST_OFFSET);
    
    DECL_VISIT_OBJECT(ENUM_CACHE_OWN_OFFSET, ENUM_CACHE_KIND_OFFSET)

    DECL_DUMP()
};
}  // namespace ecmascript
}  // namespace panda
 
#endif  // ECMASCRIPT_IC_ENUM_CACHE_H