/*
 * Copyright 2020 Bloomberg Finance LP
 *
 * 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.
 */

#ifndef INCLUDED_BUILDBOXCASD_FINDMISSINGBLOBSCLIENT_H
#define INCLUDED_BUILDBOXCASD_FINDMISSINGBLOBSCLIENT_H

#include <buildboxcasd_digestcache.h>

#include <buildboxcommon_casclient.h>
#include <buildboxcommon_protos.h>

#include <functional>
#include <memory>
#include <vector>

namespace buildboxcasd {

using buildboxcommon::Digest;

class FindMissingBlobsClient final {
  public:
    /*
     * Given a CAS client connected to a remote and a `FindMissingBlobs` cache
     * of digests, provides a `findMissingBlobs()` method that will query the
     * cache before issuing the `FindMissingBlobs()` request, and update the
     * cache with the information gathered (i.e. the blobs that the remote
     * has).
     *
     * If no cache is provided, all requests are transparently forwarded to the
     * remote.
     */
    FindMissingBlobsClient(buildboxcommon::CASClient &casClient,
                           DigestCache *cache = nullptr);

    // In order to simplify unit-testing, keep a reference to just the
    // client's `findMissingBlobs()` method (this allows to easily mock that
    // function).
    typedef std::function<std::vector<Digest>(const std::vector<Digest> &)>
        CasClientFindMissingBlobsFunction;

    FindMissingBlobsClient(
        const CasClientFindMissingBlobsFunction &findMissingBlobsFunction,
        DigestCache *cache = nullptr);

    // If caching is enabled, check which digests are assumed will still be in
    // a remote and issue a `FindMissingBlobs()` with the remaining ones.
    // Update the cache with the blobs that the server reports as not missing.
    //
    // If caching is not enabled, send a request with all the digests provided.
    //
    // Writes the list of digests that were reported as missing in the
    // `digestsFoundMissing` pointer.
    grpc::Status findMissingBlobs(const std::vector<Digest> &digests,
                                  std::vector<Digest> *digestsFoundMissing);

    bool cacheEnabled() const
    {
        return (d_digests_in_remote_cache != nullptr);
    }

  private:
    const CasClientFindMissingBlobsFunction
        d_cas_client_findmissingblobs_function; // NOLINT
                                                // (cppcoreguidelines-avoid-const-or-ref-data-members)

    DigestCache *d_digests_in_remote_cache;

    /*
     * Given a list of digests that was queried in a `FindMissingBlobs()`
     * call and the list of digests that was returned, adds to the cache
     * the blobs that are present in the remote. (That is, the ones in
     * `findMissingBlobsRequest` - `findMissingBlobsResponse`.)
     *
     * To perform the set difference efficiently, sorts both arguments
     * in-place.
     */
    void addBlobsPresentInRemote(
        std::vector<buildboxcommon::Digest> *queriedDigests,
        std::vector<buildboxcommon::Digest> *missingDigests);
};

} // namespace buildboxcasd

#endif
