/*
 * Copyright (c) 2008-2014 MongoDB, Inc.
 *
 * Licensed 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 com.mongodb;

import com.mongodb.TaggableReadPreference.NearestReadPreference;
import com.mongodb.TaggableReadPreference.PrimaryPreferredReadPreference;
import com.mongodb.TaggableReadPreference.SecondaryPreferredReadPreference;
import com.mongodb.TaggableReadPreference.SecondaryReadPreference;
import com.mongodb.annotations.Immutable;
import com.mongodb.connection.ClusterDescription;
import com.mongodb.connection.ServerDescription;
import org.bson.BsonDocument;
import org.bson.BsonString;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.mongodb.assertions.Assertions.notNull;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
 * A class that represents preferred replica set members to which a query or command can be sent.
 *
 * @mongodb.driver.manual core/read-preference  Read Preference
 */
@Immutable
public abstract class ReadPreference {

    ReadPreference() {
    }

    /**
     * True if this read preference allows reading from a secondary member of a replica set.
     *
     * @return if reading from a secondary is ok
     */
    public abstract boolean isSlaveOk();

    /**
     * Gets the name of this read preference.
     *
     * @return the name
     */
    public abstract String getName();

    /**
     * Gets a document representing this read preference in the wire protocol.
     *
     * @return the document
     */
    public abstract BsonDocument toDocument();

    /**
     * Chooses the servers from the given cluster than match this read preference.
     *
     * @param clusterDescription the cluster description
     * @return a list of matching server descriptions, which may be empty but may not be null
     */
    public final List<ServerDescription> choose(final ClusterDescription clusterDescription) {
        switch (clusterDescription.getType()) {
            case REPLICA_SET:
                return chooseForReplicaSet(clusterDescription);
            case SHARDED:
            case STANDALONE:
                return chooseForNonReplicaSet(clusterDescription);
            case UNKNOWN:
                return Collections.emptyList();
            default:
                throw new UnsupportedOperationException("Unsupported cluster type: " + clusterDescription.getType());
        }
    }

    protected abstract List<ServerDescription> chooseForNonReplicaSet(ClusterDescription clusterDescription);

    protected abstract List<ServerDescription> chooseForReplicaSet(ClusterDescription clusterDescription);

    /**
     * Gets a read preference that forces read to the primary.
     *
     * @return ReadPreference which reads from primary only
     */
    public static ReadPreference primary() {
        return PRIMARY;
    }

    /**
     * Gets a read preference that forces reads to the primary if available, otherwise to a secondary.
     *
     * @return ReadPreference which reads primary if available.
     */
    public static ReadPreference primaryPreferred() {
        return PRIMARY_PREFERRED;
    }

    /**
     * Gets a read preference that forces reads to a secondary.
     *
     * @return ReadPreference which reads secondary.
     */
    public static ReadPreference secondary() {
        return SECONDARY;
    }

    /**
     * Gets a read preference that forces reads to a secondary if one is available, otherwise to the primary.
     *
     * @return ReadPreference which reads secondary if available, otherwise from primary.
     */
    public static ReadPreference secondaryPreferred() {
        return SECONDARY_PREFERRED;
    }

    /**
     * Gets a read preference that forces reads to a primary or a secondary.
     *
     * @return ReadPreference which reads nearest
     */
    public static ReadPreference nearest() {
        return NEAREST;
    }

    /**
     * Gets a read preference that forces reads to the primary if available, otherwise to a secondary.
     *
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads primary if available.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static ReadPreference primaryPreferred(final long maxStaleness, final TimeUnit timeUnit) {
        return new PrimaryPreferredReadPreference(Collections.<TagSet>emptyList(), maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to a secondary that is less stale than the given maximum.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads secondary.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static ReadPreference secondary(final long maxStaleness, final TimeUnit timeUnit) {
        return new SecondaryReadPreference(Collections.<TagSet>emptyList(), maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to a secondary that is less stale than the given maximumm if one is available,
     * otherwise to the primary.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>     *
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads secondary if available, otherwise from primary.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static ReadPreference secondaryPreferred(final long maxStaleness, final TimeUnit timeUnit) {
        return new SecondaryPreferredReadPreference(Collections.<TagSet>emptyList(), maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to a primary or a secondary that is less stale than the given maximum.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads nearest
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static ReadPreference nearest(final long maxStaleness, final TimeUnit timeUnit) {
        return new NearestReadPreference(Collections.<TagSet>emptyList(), maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to the primary if available, otherwise to a secondary with the given set of tags.
     *
     * @param tagSet the set of tags to limit the list of secondaries to.
     * @return ReadPreference which reads primary if available, otherwise a secondary respective of tags.\
     * @since 2.13
     */
    public static TaggableReadPreference primaryPreferred(final TagSet tagSet) {
        return new PrimaryPreferredReadPreference(singletonList(tagSet), null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to a secondary with the given set of tags.
     *
     * @param tagSet the set of tags to limit the list of secondaries to
     * @return ReadPreference which reads secondary respective of tags.
     * @since 2.13
     */
    public static TaggableReadPreference secondary(final TagSet tagSet) {
        return new SecondaryReadPreference(singletonList(tagSet), null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to a secondary with the given set of tags, or the primary is none are available.
     *
     * @param tagSet the set of tags to limit the list of secondaries to
     * @return ReadPreference which reads secondary if available respective of tags, otherwise from primary irrespective of tags.
     * @since 2.13
     */
    public static TaggableReadPreference secondaryPreferred(final TagSet tagSet) {
        return new SecondaryPreferredReadPreference(singletonList(tagSet), null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to the primary or a secondary with the given set of tags.
     *
     * @param tagSet the set of tags to limit the list of secondaries to
     * @return ReadPreference which reads nearest node respective of tags.
     * @since 2.13
     */
    public static TaggableReadPreference nearest(final TagSet tagSet) {
        return new NearestReadPreference(singletonList(tagSet), null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to the primary if available, otherwise to a secondary with the given set of tags
     * that is less stale than the given maximum.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param tagSet the set of tags to limit the list of secondaries to.
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads primary if available, otherwise a secondary respective of tags.\
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference primaryPreferred(final TagSet tagSet,
                                                          final long maxStaleness, final TimeUnit timeUnit) {
        return new PrimaryPreferredReadPreference(singletonList(tagSet), maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to a secondary with the given set of tags that is less stale than the given maximum.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param tagSet the set of tags to limit the list of secondaries to
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads secondary respective of tags.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference secondary(final TagSet tagSet,
                                                   final long maxStaleness, final TimeUnit timeUnit) {
        return new SecondaryReadPreference(singletonList(tagSet), maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to a secondary with the given set of tags that is less stale than the given maximum,
     * or the primary is none are available.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>     *
     * @param tagSet the set of tags to limit the list of secondaries to
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads secondary if available respective of tags, otherwise from primary irrespective of tags.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference secondaryPreferred(final TagSet tagSet,
                                                            final long maxStaleness, final TimeUnit timeUnit) {
        return new SecondaryPreferredReadPreference(singletonList(tagSet), maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to the primary or a secondary with the given set of tags that is less stale than the
     * given maximum.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param tagSet the set of tags to limit the list of secondaries to
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads nearest node respective of tags.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference nearest(final TagSet tagSet,
                                                 final long maxStaleness, final TimeUnit timeUnit) {
        return new NearestReadPreference(singletonList(tagSet), maxStaleness, timeUnit);
    }


    /**
     * Gets a read preference that forces reads to the primary if available, otherwise to a secondary with one of the given sets of tags.
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or failing if no secondary can be found that matches any of the tag sets in the list.
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @return ReadPreference which reads primary if available, otherwise a secondary respective of tags.
     * @since 2.13
     */
    public static TaggableReadPreference primaryPreferred(final List<TagSet> tagSetList) {
        return new PrimaryPreferredReadPreference(tagSetList, null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to a secondary with one of the given sets of tags.
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or failing if no secondary can be found that matches any of the tag sets in the list.
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @return ReadPreference which reads secondary respective of tags.
     * @since 2.13
     */
    public static TaggableReadPreference secondary(final List<TagSet> tagSetList) {
        return new SecondaryReadPreference(tagSetList, null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to a secondary with one of the given sets of tags.
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or the primary if none are available.
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @return ReadPreference which reads secondary if available respective of tags, otherwise from primary irrespective of tags.
     * @since 2.13
     */
    public static TaggableReadPreference secondaryPreferred(final List<TagSet> tagSetList) {
        return new SecondaryPreferredReadPreference(tagSetList, null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to the primary or a secondary with one of the given sets of tags.
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or the primary if none are available.
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @return ReadPreference which reads nearest node respective of tags.
     * @since 2.13
     */
    public static TaggableReadPreference nearest(final List<TagSet> tagSetList) {
        return new NearestReadPreference(tagSetList, null, MILLISECONDS);
    }

    /**
     * Gets a read preference that forces reads to the primary if available, otherwise to a secondary with one of the given sets of tags
     * that is less stale than the given maximum.
     *
     * <p>
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or failing if no secondary can be found that matches any of the tag sets in the list.
     * </p>
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads primary if available, otherwise a secondary respective of tags.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference primaryPreferred(final List<TagSet> tagSetList,
                                                          final long maxStaleness, final TimeUnit timeUnit) {
        return new PrimaryPreferredReadPreference(tagSetList, maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to a secondary with one of the given sets of tags that is less stale than
     * the given maximum.
     *
     * <p>
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or failing if no secondary can be found that matches any of the tag sets in the list.
     * </p>
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads secondary respective of tags.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference secondary(final List<TagSet> tagSetList,
                                                   final long maxStaleness, final TimeUnit timeUnit) {
        return new SecondaryReadPreference(tagSetList, maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to a secondary with one of the given sets of tags that is less stale than
     * the given maximum.
     *
     * <p>
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or the primary if none are available.
     * </p>
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads secondary if available respective of tags, otherwise from primary irrespective of tags.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference secondaryPreferred(final List<TagSet> tagSetList,
                                                            final long maxStaleness, final TimeUnit timeUnit) {
        return new SecondaryPreferredReadPreference(tagSetList, maxStaleness, timeUnit);
    }

    /**
     * Gets a read preference that forces reads to the primary or a secondary with one of the given sets of tags that is less stale than
     * the given maximum.
     *
     * <p>
     * The driver will look for a secondary with each tag set in the given list, stopping after one is found,
     * or the primary if none are available.
     * </p>
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param tagSetList the list of tag sets to limit the list of secondaries to
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return ReadPreference which reads nearest node respective of tags.
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference nearest(final List<TagSet> tagSetList,
                                                 final long maxStaleness, final TimeUnit timeUnit) {
        return new NearestReadPreference(tagSetList, maxStaleness, timeUnit);
    }

    /**
     * Creates a read preference from the given read preference name.
     *
     * @param name the name of the read preference
     * @return the read preference
     */
    public static ReadPreference valueOf(final String name) {
        if (name == null) {
            throw new IllegalArgumentException();
        }

        String nameToCheck = name.toLowerCase();

        if (nameToCheck.equals(PRIMARY.getName().toLowerCase())) {
            return PRIMARY;
        }
        if (nameToCheck.equals(SECONDARY.getName().toLowerCase())) {
            return SECONDARY;
        }
        if (nameToCheck.equals(SECONDARY_PREFERRED.getName().toLowerCase())) {
            return SECONDARY_PREFERRED;
        }
        if (nameToCheck.equals(PRIMARY_PREFERRED.getName().toLowerCase())) {
            return PRIMARY_PREFERRED;
        }
        if (nameToCheck.equals(NEAREST.getName().toLowerCase())) {
            return NEAREST;
        }

        throw new IllegalArgumentException("No match for read preference of " + name);
    }

    /**
     * Creates a taggable read preference from the given read preference name and list of tag sets.
     *
     * @param name the name of the read preference
     * @param tagSetList the list of tag sets
     * @return the taggable read preference
     * @since 2.13
     */
    public static TaggableReadPreference valueOf(final String name, final List<TagSet> tagSetList) {
        return valueOf(name, tagSetList, null, MILLISECONDS);
    }

    /**
     * Creates a taggable read preference from the given read preference name, list of tag sets, and max allowable staleness of secondaries.
     *
     * <p>
     * The driver estimates the staleness of each secondary, based on lastWriteDate values provided in server isMaster responses,
     * and selects only those secondaries whose staleness is less than or equal to maxStaleness.
     * </p>
     *
     * @param name the name of the read preference
     * @param tagSetList the list of tag sets
     * @param maxStaleness the max allowable staleness of secondaries. The minimum value is either 90 seconds, or the heartbeat frequency
     *                     plus 10 seconds, whichever is greatest.
     * @param timeUnit the time unit of maxStaleness
     * @return the taggable read preference
     * @since 3.4
     * @see TaggableReadPreference#getMaxStaleness(TimeUnit)
     */
    public static TaggableReadPreference valueOf(final String name, final List<TagSet> tagSetList, final long maxStaleness,
                                                 final TimeUnit timeUnit) {
        return valueOf(name, tagSetList, (Long) maxStaleness, timeUnit);
    }

    private static TaggableReadPreference valueOf(final String name, final List<TagSet> tagSetList, final Long maxStaleness,
                                                  final TimeUnit timeUnit) {
        notNull("name", name);
        notNull("tagSetList", tagSetList);
        notNull("timeUnit", timeUnit);

        String nameToCheck = name.toLowerCase();

        if (nameToCheck.equals(PRIMARY.getName().toLowerCase())) {
            throw new IllegalArgumentException("Primary read preference can not also specify tag sets or max staleness");
        }

        if (nameToCheck.equals(SECONDARY.getName().toLowerCase())) {
            return new SecondaryReadPreference(tagSetList, maxStaleness, timeUnit);
        }
        if (nameToCheck.equals(SECONDARY_PREFERRED.getName().toLowerCase())) {
            return new SecondaryPreferredReadPreference(tagSetList, maxStaleness, timeUnit);
        }
        if (nameToCheck.equals(PRIMARY_PREFERRED.getName().toLowerCase())) {
            return new PrimaryPreferredReadPreference(tagSetList, maxStaleness, timeUnit);
        }
        if (nameToCheck.equals(NEAREST.getName().toLowerCase())) {
            return new NearestReadPreference(tagSetList, maxStaleness, timeUnit);
        }

        throw new IllegalArgumentException("No match for read preference of " + name);
    }

    /**
     * Preference to read from primary only. Cannot be combined with tags.
     */
    private static final class PrimaryReadPreference extends ReadPreference {
        private PrimaryReadPreference() {
        }

        @Override
        public boolean isSlaveOk() {
            return false;
        }

        @Override
        public String toString() {
            return getName();
        }

        @Override
        public boolean equals(final Object o) {
            return o != null && getClass() == o.getClass();
        }

        @Override
        public int hashCode() {
            return getName().hashCode();
        }

        public BsonDocument toDocument() {
            return new BsonDocument("mode", new BsonString(getName()));
        }

        @Override
        @SuppressWarnings("deprecation")
        protected List<ServerDescription> chooseForReplicaSet(final ClusterDescription clusterDescription) {
            return clusterDescription.getPrimaries();
        }

        @Override
        @SuppressWarnings("deprecation")
        protected List<ServerDescription> chooseForNonReplicaSet(final ClusterDescription clusterDescription) {
            return clusterDescription.getAny();
        }

        @Override
        public String getName() {
            return "primary";
        }
    }

    private static final ReadPreference PRIMARY;
    private static final ReadPreference SECONDARY;
    private static final ReadPreference SECONDARY_PREFERRED;
    private static final ReadPreference PRIMARY_PREFERRED;
    private static final ReadPreference NEAREST;

    static {
        PRIMARY = new PrimaryReadPreference();
        SECONDARY = new SecondaryReadPreference();
        SECONDARY_PREFERRED = new SecondaryPreferredReadPreference();
        PRIMARY_PREFERRED = new PrimaryPreferredReadPreference();
        NEAREST = new NearestReadPreference();
   }
}
