package com.atlassian.bitbucket.mesh.repository;

import com.google.common.base.MoreObjects;

import javax.annotation.Nonnull;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
 * An observation of the replica state on this node at a particular point in time.
 */
public class ReplicaStateObservation implements Comparable<ReplicaStateObservation> {

    private final boolean force;
    private final ReplicaState state;
    private final long timestamp;
    private final long version;

    public ReplicaStateObservation(@Nonnull ReplicaState state, long timestamp, long version) {
        this(state, timestamp, version, false);
    }

    public ReplicaStateObservation(@Nonnull ReplicaState state, long timestamp, long version, boolean force) {
        this.force = force;
        this.state = requireNonNull(state, "state");
        this.timestamp = timestamp;
        this.version = version;
    }

    @Override
    public int compareTo(@Nonnull ReplicaStateObservation other) {
        int result = Long.compare(version, other.version);
        return result == 0 ? Long.compare(timestamp, other.timestamp) : result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ReplicaStateObservation that = (ReplicaStateObservation) o;
        return timestamp == that.timestamp &&
                version == that.version &&
                state == that.state;
    }

    /**
     * @return the logical timestamp at which the state was observed (on this node)
     */
    public long getLogicalTimestamp() {
        return timestamp;
    }

    /**
     * @return the state of the replica on this node
     */
    @Nonnull
    public ReplicaState getState() {
        return state;
    }

    /**
     * @return the version that this node's replica is (in)consistent with
     */
    public long getVersion() {
        return version;
    }

    @Override
    public int hashCode() {
        return Objects.hash(state, timestamp, version);
    }

    /**
     * @return {@code true} if this observation should override any existing observation, regardless of version
     */
    public boolean isForce() {
        return force;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("force", force)
                .add("state", state)
                .add("timestamp", timestamp)
                .add("version", version)
                .toString();
    }
}
