package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.exception.UnsupportedArgumentException;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGitObjectType;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static com.atlassian.bitbucket.mesh.git.GitNodeType.*;
import static com.atlassian.bitbucket.mesh.rpc.v1.RpcGitObjectType.*;
import static com.atlassian.bitbucket.mesh.rpc.v1.RpcUnsupportedArgument.Reason.UNSUPPORTED_OBJECT_TYPE;

/**
 * Defines the types of objects which may be returned by {@code git cat-file -t}.
 */
public enum GitObjectType {

    /**
     * A blob, which is a {@link GitNodeType#FILE file}.
     */
    BLOB(OBJECT_BLOB, FILE, "blob"),
    /**
     * A commit, which is a {@link GitNodeType#SUBMODULE submodule}.
     */
    COMMIT(OBJECT_COMMIT, SUBMODULE, "commit"),
    /**
     * A tag, which is a reference to a specific {@link #COMMIT commit} and may be annotated, signed or lightweight.
     */
    TAG(OBJECT_TAG, "tag"),
    /**
     * A tree, which is a {@link GitNodeType#DIRECTORY directory}.
     */
    TREE(OBJECT_TREE, DIRECTORY, "tree");

    private final GitNodeType nodeType;
    private final String objectType;
    private final RpcGitObjectType rpc;

    GitObjectType(@Nonnull RpcGitObjectType rpc, @Nonnull String objectType) {
        this(rpc, null, objectType);
    }

    GitObjectType(@Nonnull RpcGitObjectType rpc, @Nullable GitNodeType nodeType, @Nonnull String objectType) {
        this.nodeType = nodeType;
        this.objectType = objectType;
        this.rpc = rpc;
    }

    /**
     * Determines the {@code GitObjectType} for the provided {@code git cat-file -t} object type.
     *
     * @param objectType the {@code git} object type
     * @return the {@code GitObjectType} associated with the provided {@code git} object type
     * @throws UnsupportedArgumentException if no {@code GitObjectType} matches the provided {@code git} object type
     */
    @Nonnull
    public static GitObjectType fromObjectType(@Nonnull String objectType) {
        for (GitObjectType value : values()) {
            if (objectType.equals(value.getObjectType())) {
                return value;
            }
        }

        throw new UnsupportedArgumentException(UNSUPPORTED_OBJECT_TYPE, objectType);
    }

    @Nonnull
    public static GitObjectType fromRpc(@Nonnull RpcGitObjectType rpc) {
        for (GitObjectType type : values()) {
            if (type.rpc == rpc) {
                return type;
            }
        }

        throw new UnsupportedArgumentException(UNSUPPORTED_OBJECT_TYPE, rpc);
    }

    /**
     * Retrieves the {@link GitNodeType} for this object type, which may be {@code null} for object types that are not
     * readily expressed in as content nodes.
     *
     * @return the node type for this object type, or {@code null} if this object should not be shown in the tree
     */
    @Nullable
    public GitNodeType getNodeType() {
        return nodeType;
    }

    /**
     * Retrieves the type used by {@code git cat-file -t} to represent this object type.
     *
     * @return the {@code git} object type
     */
    @Nonnull
    public String getObjectType() {
        return objectType;
    }

    @Nonnull
    public RpcGitObjectType toRpc() {
        return rpc;
    }
}
