/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.bookkeeper.proto;

import io.netty.util.ReferenceCounted;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.bookkeeper.client.api.WriteFlag;
import org.apache.bookkeeper.net.BookieId;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.BatchedReadEntryCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ForceLedgerCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GetBookieInfoCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadLacCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteLacCallback;
import org.apache.bookkeeper.util.AvailabilityOfEntriesOfLedger;
import org.apache.bookkeeper.util.ByteBufList;

/**
 * Low level client for talking to bookies.
 */
public interface BookieClient {
    long PENDINGREQ_NOTWRITABLE_MASK = 0x01L << 62;

    /**
     * Get the list of bookies which have exhibited more error responses
     * than a configured threshold.
     *
     * @return the list of faulty bookies
     */
    List<BookieId> getFaultyBookies();

    /**
     * Check whether the channel used to write to a bookie channel is writable.
     * A channel becomes non-writable when its buffer become full, and will stay
     * non-writable until some of the buffer is cleared.
     *
     * <p>This can be used to apply backpressure. If a channel is not writable,
     * requests will end up queuing.
     *
     * <p>As as we use pooling, there may be multiple channels per bookie, so
     * we also pass the ledger ID to check the writability of the correct
     * channel.
     *
     * <p>This has nothing to do with the bookie read-only status.
     *
     * @param address the address of the bookie
     * @param ledgerId the ledger we wish to send a request to
     *
     */
    boolean isWritable(BookieId address, long ledgerId);

    /**
     * Get the number of outstanding requests on the channel used to connect
     * to a bookie at {@code address} for a ledger with {@code ledgerId}.
     * It is necessary to specify the ledgerId as there may be multiple
     * channels for a single bookie if pooling is in use.
     * If the bookie is not {@link #isWritable(BookieId,long) writable},
     * then the {@link #PENDINGREQ_NOTWRITABLE_MASK} will be logically or'd with
     * the returned value.
     *
     * @param address the address of the bookie
     * @param ledgerId the ledger whose channel we wish to query
     * @return the number of requests currently outstanding
     */
    long getNumPendingRequests(BookieId address, long ledgerId);

    /**
     * Send a force request to the server. When complete all entries which have
     * been written for {@code ledgerId} to this bookie will be persisted on disk.
     * This is for use with {@link org.apache.bookkeeper.client.api.WriteFlag#DEFERRED_SYNC}.
     *
     * @param address the address of the bookie
     * @param ledgerId the ledger whose entries we want persisted
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     */
    void forceLedger(BookieId address, long ledgerId,
                     ForceLedgerCallback cb, Object ctx);

    /**
     * Read the last add confirmed for ledger {@code ledgerId} from the bookie at
     * {@code address}.
     *
     * @param address the address of the bookie
     * @param ledgerId the ledger whose last add confirm we wish to know
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     */
    void readLac(BookieId address, long ledgerId, ReadLacCallback cb, Object ctx);

    /**
     * Explicitly write the last add confirmed for ledger {@code ledgerId} to the bookie at
     * {@code address}.
     *
     * @param address the address of the bookie
     * @param ledgerId the ledger whose last add confirm we wish to know
     * @param masterKey the master key of the ledger
     * @param lac the last add confirmed we wish to write
     * @param toSend a buffer also containing the lac, along with a digest
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     */
    void writeLac(BookieId address, long ledgerId, byte[] masterKey,
                  long lac, ByteBufList toSend, WriteLacCallback cb, Object ctx);

    /**
     * Add an entry for ledger {@code ledgerId} on the bookie at address {@code address}.
     *
     * @param address the address of the bookie
     * @param ledgerId the ledger to which we wish to add the entry
     * @param entryId the id of the entry we wish to add
     * @param toSend a buffer containing the entry and its digest
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     * @param options a bit mask of flags from BookieProtocol.FLAG_*
     *                {@link org.apache.bookkeeper.proto.BookieProtocol}
     * @param allowFastFail fail the add immediately if the channel is non-writable
     *                      {@link #isWritable(BookieId,long)}
     * @param writeFlags a set of write flags
     *                   {@link org.apache.bookkeeper.client.api.WriteFlag}
     */
    void addEntry(BookieId address, long ledgerId, byte[] masterKey,
                  long entryId, ReferenceCounted toSend, WriteCallback cb, Object ctx,
                  int options, boolean allowFastFail, EnumSet<WriteFlag> writeFlags);

    /**
     * Read entry with a null masterkey, disallowing failfast.
     * @see #readEntry(BookieId,long,long,ReadEntryCallback,Object,int,byte[],boolean)
     */
    default void readEntry(BookieId address, long ledgerId, long entryId,
                           ReadEntryCallback cb, Object ctx, int flags) {
        readEntry(address, ledgerId, entryId, cb, ctx, flags, null);
    }

    /**
     * Read entry, disallowing failfast.
     * @see #readEntry(BookieId,long,long,ReadEntryCallback,Object,int,byte[],boolean)
     */
    default void readEntry(BookieId address, long ledgerId, long entryId,
                           ReadEntryCallback cb, Object ctx, int flags, byte[] masterKey) {
        readEntry(address, ledgerId, entryId, cb, ctx, flags, masterKey, false);
    }

    /**
     * Read an entry from bookie at address {@code address}.
     *
     * @param address address of the bookie to read from
     * @param ledgerId id of the ledger the entry belongs to
     * @param entryId id of the entry we wish to read
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     * @param flags a bit mask of flags from BookieProtocol.FLAG_*
     *              {@link org.apache.bookkeeper.proto.BookieProtocol}
     * @param masterKey the master key of the ledger being read from. This is only required
     *                  if the FLAG_DO_FENCING is specified.
     * @param allowFastFail fail the read immediately if the channel is non-writable
     *                      {@link #isWritable(BookieId,long)}
     */
    void readEntry(BookieId address, long ledgerId, long entryId,
                   ReadEntryCallback cb, Object ctx, int flags, byte[] masterKey,
                   boolean allowFastFail);

    /**
     * Batch read entries with a null masterkey, disallowing failfast.
     * @see #batchReadEntries(BookieId,long,long,int,long,BatchedReadEntryCallback,Object,int,byte[],boolean)
     */
    default void batchReadEntries(BookieId address, long ledgerId, long startEntryId,
            int maxCount, long maxSize, BatchedReadEntryCallback cb, Object ctx,
            int flags) {
        batchReadEntries(address, ledgerId, startEntryId, maxCount, maxSize, cb, ctx, flags, null);
    }

    /**
     * Batch read entries, disallowing failfast.
     * @see #batchReadEntries(BookieId,long,long,int,long,BatchedReadEntryCallback,Object,int,byte[],boolean)
     */
    default void batchReadEntries(BookieId address, long ledgerId, long startEntryId,
            int maxCount, long maxSize, BatchedReadEntryCallback cb, Object ctx,
            int flags, byte[] masterKey) {
        batchReadEntries(address, ledgerId, startEntryId, maxCount, maxSize, cb, ctx, flags, masterKey, false);
    }

    /**
     * Batch read entries from bookie at address {@code address}.
     *
     * @param address address of the bookie to read from
     * @param ledgerId id of the ledger the entry belongs to
     * @param startEntryId id of the entry started
     * @param maxCount the total entries count in this batch
     * @param maxSize the total entries size in this batch
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     * @param flags a bit mask of flags from BookieProtocol.FLAG_*
     *              {@link org.apache.bookkeeper.proto.BookieProtocol}
     * @param masterKey the master key of the ledger being read from. This is only required
     *                  if the FLAG_DO_FENCING is specified.
     * @param allowFastFail fail the read immediately if the channel is non-writable
     *                      {@link #isWritable(BookieId,long)}
     */
    void batchReadEntries(BookieId address, long ledgerId, long startEntryId,
            int maxCount, long maxSize, BatchedReadEntryCallback cb, Object ctx,
            int flags, byte[] masterKey, boolean allowFastFail);

    /**
     * Send a long poll request to bookie, waiting for the last add confirmed
     * to be updated. The client can also request that the full entry is returned
     * with the new last add confirmed.
     *
     * @param address address of bookie to send the long poll address to
     * @param ledgerId ledger whose last add confirmed we are interested in
     * @param entryId the id of the entry we expect to read
     * @param previousLAC the previous lac value
     * @param timeOutInMillis number of millis to wait for LAC update
     * @param piggyBackEntry whether to read the requested entry when LAC is updated
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     */
    void readEntryWaitForLACUpdate(BookieId address,
                                   long ledgerId,
                                   long entryId,
                                   long previousLAC,
                                   long timeOutInMillis,
                                   boolean piggyBackEntry,
                                   ReadEntryCallback cb,
                                   Object ctx);

    /**
     * Read information about the bookie, from the bookie.
     *
     * @param address the address of the bookie to request information from
     * @param requested a bitset specifying which pieces of information to request
     *                  {@link org.apache.bookkeeper.proto.BookkeeperProtocol.GetBookieInfoRequest}
     * @param cb the callback notified when the request completes
     * @param ctx a context object passed to the callback on completion
     *
     * @see org.apache.bookkeeper.client.BookieInfoReader.BookieInfo
     */
    void getBookieInfo(BookieId address, long requested,
                       GetBookieInfoCallback cb, Object ctx);

    /**
     * Makes async request for getting list of entries of ledger from a bookie
     * and returns Future for the result.
     *
     * @param address
     *            BookieId of the bookie
     * @param ledgerId
     *            ledgerId
     * @return returns Future
     */
    CompletableFuture<AvailabilityOfEntriesOfLedger> getListOfEntriesOfLedger(BookieId address,
            long ledgerId);

    /**
     * @return whether bookie client object has been closed
     */
    boolean isClosed();

    /**
     * Close the bookie client object.
     */
    void close();
}
