/*
 * Copyright 2018 Google LLC
 *
 * 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 FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_MAP_H_
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_MAP_H_

#include <utility>

#include "Firestore/core/src/firebase/firestore/immutable/sorted_map.h"
#include "Firestore/core/src/firebase/firestore/model/document.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
#include "absl/base/attributes.h"
#include "absl/types/optional.h"

namespace firebase {
namespace firestore {
namespace model {

/**
 * Convenience type for a map of keys to MaybeDocuments, since they are so
 * common.
 */
using MaybeDocumentMap = immutable::SortedMap<DocumentKey, MaybeDocument>;

using OptionalMaybeDocumentMap =
    immutable::SortedMap<DocumentKey, absl::optional<MaybeDocument>>;

/**
 * Convenience type for a map of keys to Documents, since they are so common.
 *
 * PORTING NOTE: unlike other platforms, in C++ `Foo<Derived*>` cannot be
 * converted to `Foo<Base*>`; consequently, if `DocumentMap` were simply an
 * alias similar to `MaybeDocumentMap`, it couldn't be passed to functions
 * expecting `MaybeDocumentMap`.
 *
 * To work around this, in C++ `DocumentMap` is a simple wrapper over a
 * `MaybeDocumentMap` that forwards all functions to the underlying map but with
 * added type safety (it only accepts `Document`, not `MaybeDocument`). Use
 * `DocumentMap` in functions creating and/or returning maps that only contain
 * `Document`; when the `DocumentMap` needs to be passed to a function accepting
 * a `MaybeDocumentMap`, use `underlying_map` function to get (read-only) access
 * to the representation. Also use `underlying_map` for iterating and searching.
 */
class DocumentMap {
 public:
  using key_type = DocumentKey;
  using mapped_type = Document;

  DocumentMap() = default;

  ABSL_MUST_USE_RESULT DocumentMap insert(const DocumentKey& key,
                                          const Document& value) const;

  ABSL_MUST_USE_RESULT DocumentMap erase(const DocumentKey& key) const;

  bool empty() const {
    return map_.empty();
  }
  MaybeDocumentMap::size_type size() const {
    return map_.size();
  }

  /** Use this function to "convert" `DocumentMap` to a `MaybeDocumentMap`. */
  const MaybeDocumentMap& underlying_map() const {
    return map_;
  }

 private:
  explicit DocumentMap(MaybeDocumentMap&& map) : map_{std::move(map)} {
  }

  MaybeDocumentMap map_;
};

}  // namespace model
}  // namespace firestore
}  // namespace firebase

#endif  // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_MAP_H_
