/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.
 */
package org.apache.causeway.applib.services.repository;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;

/**
 * Enumerates the state of an entity.
 *
 * @apiNote use the provided predicates rather then directly referencing the enum names
 *
 * @since 2.0 {@index}
 */
@RequiredArgsConstructor
public enum EntityState {

    /**
     * Object with this state is not an entity (for example it might be a view
     * model, value type or a domain service).
     */
    NOT_PERSISTABLE(false),
    /**
     * Object with this state is an entity that has no session.
     */
    SNAPSHOT(true),
    /**
     * Object with this state is an entity that has no session and has no OID yet. On pre-store.
     */
    SNAPSHOT_NO_OID(false),
    /**
     * Object with this state is an entity that is attached to a persistence
     * session, in other words changes to the entity will be flushed back to
     * the database.
     */
    ATTACHED(true),
    /**
     * Is attached, has no OID yet. On pre-store.
     */
    ATTACHED_NO_OID(false),
    /**
     * Is detached, hence (per definition) has an OID.
     * <p>
     * Supported by both JDO and JPA. However, historically never used by the framework for JDO.
     */
    DETACHED(true),
    /**
     * <h1>JDO specific</h1>
     * Object with this state is an entity that no longer attached to a
     * persistence session and cannot be re-attached.
     * In other words: changes to the entity will <i>not</i>
     * be tracked nor flushed back to the database.
     * <p>
     * JDO distinguishes between DETACHED and HOLLOW,
     * by virtue of {@code javax.jdo.option.detachAllOnCommit=false}.
     * <p>
     * (Unfortunately, we have not found a way to recover _OIDs_ from _hollow_ entities, as used for serialization post commit.
     * We have instead implemented a workaround using the <code>DnStateManagerForHollow</code> class).
     *
     * @see "https://www.datanucleus.org/products/accessplatform_6_0/jdo/persistence.html#lifecycle"
     */
    HOLLOW(true),
    /**
     * Object with this state is an entity that is transient
     * or has been removed from the database.
     * Objects in this state may no longer be interacted with.
     */
    TRANSIENT_OR_REMOVED(false),
    /**
     * <h1>JDO specific</h1>
     * Not supported by JPA. (Cannot distinguish between TRANSIENT and REMOVED.)
     */
    REMOVED(false)
    ;

    // -- PREDICATES

    @Getter @Accessors(fluent=true) private final boolean hasOid;

    /**
     * Object is an entity, hence is persistable to the database.
     */
    public boolean isPersistable() { return this != NOT_PERSISTABLE; }
    /** @see #ATTACHED */
    public boolean isAttached() { return this == ATTACHED; }
    /** @see #ATTACHED_NO_OID */
    public boolean isAttachedNoOid() { return this == ATTACHED_NO_OID; }
    /** @see #DETACHED */
    public boolean isDetached() { return this == DETACHED; }
    /** @see #HOLLOW */
    public boolean isHollow() { return this == HOLLOW; }
    /** @see #TRANSIENT_OR_REMOVED
     *  @see #REMOVED */
    public boolean isTransientOrRemoved() { return this == TRANSIENT_OR_REMOVED
            || this == REMOVED; }
    /** @see #REMOVED */
    public boolean isRemoved() { return this == REMOVED; }

    // -- SPECIAL STATES

    public boolean isAttachedOrRemoved() {
        return isAttached()
                || isTransientOrRemoved();
    }

}
