package com.atlassian.bitbucket.mesh.git.builder.diff;

import com.atlassian.bitbucket.mesh.git.content.DiffSegmentType;
import org.apache.commons.lang3.StringUtils;

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

/**
 * A {@code git}-specific extension of the general {@link DiffSegmentType} enumeration bound more tightly to standard
 * diff core output.
 */
public enum GitDiffSegmentType {

    /**
     * A leading plus marks an added line.
     */
    ADDED(DiffSegmentType.ADDED, "+"),
    /**
     * A leading backslash marks observations {@code git} offers about files it diffs. For example,
     * <pre>
     * <code>
     * \ No newline at end of file
     * </code>
     * </pre>
     * Note the space after the slash, which is included as part of this prefix.
     */
    COMMENT("\\ "),
    /**
     * A leading space marks a context line.
     */
    CONTEXT(DiffSegmentType.CONTEXT, " "),
    /**
     * A leading hyphen marks a removed line.
     */
    REMOVED(DiffSegmentType.REMOVED, "-");

    private final String prefix;
    private final DiffSegmentType type;

    GitDiffSegmentType(String prefix) {
        this(null, prefix);
    }

    GitDiffSegmentType(DiffSegmentType type, String prefix) {
        this.prefix = prefix;
        this.type = type;
    }

    /**
     * Examines the provided line to determine its segment type. If the line does not start with a known prefix,
     * {@code null} is returned (to indicate the line does not belong to a segment).
     *
     * @param line the line to examine
     * @return the matching segment type, or {@code null}
     */
    @Nullable
    public static GitDiffSegmentType forLine(String line) {
        for (GitDiffSegmentType value : values()) {
            if (StringUtils.startsWith(line, value.getPrefix())) {
                return value;
            }
        }
        return null;
    }

    /**
     * Trims the prefix from the provided line. This helper function allows prefixes to be different lengths, which
     * supports the {@link #COMMENT} type.
     *
     * @param line the line to trim
     * @return the trimmed line
     */
    @Nonnull
    public String dropPrefix(@Nonnull String line) {
        return line.substring(prefix.length());
    }

    /**
     * Retrieves the prefix which identifies this segment type.
     * 
     * @return the prefix
     */
    @Nonnull
    public String getPrefix() {
        return prefix;
    }

    /**
     * Retrieves the general {@link DiffSegmentType} for this {@code git} segment type.
     *
     * @return the associated general segment type
     */
    @Nonnull
    public DiffSegmentType getType() {
        return type;
    }

    public boolean isValid() {
        return type != null;
    }
}
