package ru.yandex.qe.dispenser.api.v1.request;

import java.util.EnumSet;
import java.util.Set;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import ru.yandex.qe.dispenser.api.util.SerializationUtils;
import ru.yandex.qe.dispenser.api.util.ValidationUtils;
import ru.yandex.qe.dispenser.api.v1.DiPerformer;
import ru.yandex.qe.dispenser.api.v1.DiQuotaChangeCause;

/**
 * Type of acquire/release action.
 * Either {@link DiEntity} or {@link ru.yandex.qe.dispenser.api.v1.DiResource} may be acquired/released.
 * {@link DiEntity} usage is either exclusive or shared.
 * Acquire/release semantic is used by Nirvana.
 */
public enum DiActionType {
    FORCE_ACQUIRE_RESOURCE(DiResourceAmount.class, true), RELEASE_RESOURCE(DiResourceAmount.class, true),
    CREATE_ENTITY(DiEntity.class, true), RELEASE_ENTITY(DiEntityReference.class, false),
    SHARE_ENTITY(DiEntityUsage.class, true), RELEASE_ENTITY_SHARING(DiEntityUsage.class, true), RELEASE_ALL_ENTITY_SHARINGS(DiEntityReference.class, true);

    private static final Set<DiActionType> ABOUT_ACQUIRE = EnumSet.of(FORCE_ACQUIRE_RESOURCE, CREATE_ENTITY, SHARE_ENTITY);
    private static final Set<DiActionType> ABOUT_RELEASE = EnumSet.of(RELEASE_RESOURCE, RELEASE_ENTITY, RELEASE_ENTITY_SHARING, RELEASE_ALL_ENTITY_SHARINGS);
    private static final Set<DiActionType> ABOUT_ENTITY = EnumSet.of(CREATE_ENTITY, RELEASE_ENTITY, SHARE_ENTITY, RELEASE_ENTITY_SHARING, RELEASE_ALL_ENTITY_SHARINGS);
    private static final Set<DiActionType> ABOUNT_ENTITY_SHARING = EnumSet.of(SHARE_ENTITY, RELEASE_ENTITY_SHARING, RELEASE_ALL_ENTITY_SHARINGS);

    @NotNull
    private final Class<?> dataClass;
    private final boolean performerRequired;

    DiActionType(@NotNull final Class<?> dataClass, final boolean performerRequired) {
        this.dataClass = dataClass;
        this.performerRequired = performerRequired;
    }

    public boolean isAboutAcquire() {
        return ABOUT_ACQUIRE.contains(this);
    }

    public boolean isAboutRelease() {
        return ABOUT_RELEASE.contains(this);
    }

    public boolean isAboutEntity() {
        return ABOUT_ENTITY.contains(this);
    }

    public boolean isAboutEntitySharing() {
        return ABOUNT_ENTITY_SHARING.contains(this);
    }

    @NotNull <T> DiOperation<T> convert(@NotNull final Object data, @Nullable final DiPerformer performer, @Nullable final DiQuotaChangeCause cause) {
        if (performerRequired) {
            ValidationUtils.requireNonNull(performer, "Performer required!");
        }
        return new DiOperation<>(this, SerializationUtils.convertValue(data, (Class<T>) dataClass), performer, cause);
    }
}
