// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_INDEX_H_
#define CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_INDEX_H_

#include <stddef.h>
#include <stdint.h>

#include <functional>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "base/hash/hash.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_interface.h"
#include "chrome/browser/sync_file_system/drive_backend/tracker_id_set.h"

namespace sync_file_system {
namespace drive_backend {

class FileMetadata;
class FileTracker;
class LevelDBWrapper;
class ServiceMetadata;

}  // namespace drive_backend
}  // namespace sync_file_system

namespace std {

template<> struct hash<sync_file_system::drive_backend::ParentIDAndTitle> {
  std::size_t operator()(
      const sync_file_system::drive_backend::ParentIDAndTitle& v) const {
    return base::HashInts64(v.parent_id, hash<std::string>()(v.title));
  }
};

}  // namespace std

namespace sync_file_system {
namespace drive_backend {

struct DatabaseContents {
  DatabaseContents();
  ~DatabaseContents();
  std::vector<std::unique_ptr<FileMetadata>> file_metadata;
  std::vector<std::unique_ptr<FileTracker>> file_trackers;
};

// Maintains indexes of MetadataDatabase on memory.
class MetadataDatabaseIndex : public MetadataDatabaseIndexInterface {
 public:
  MetadataDatabaseIndex(const MetadataDatabaseIndex&) = delete;
  MetadataDatabaseIndex& operator=(const MetadataDatabaseIndex&) = delete;

  ~MetadataDatabaseIndex() override;

  static std::unique_ptr<MetadataDatabaseIndex> Create(LevelDBWrapper* db);
  static std::unique_ptr<MetadataDatabaseIndex> CreateForTesting(
      DatabaseContents* contents,
      LevelDBWrapper* db);

  // MetadataDatabaseIndexInterface overrides.
  void RemoveUnreachableItems() override;
  bool GetFileMetadata(const std::string& file_id,
                       FileMetadata* metadata) const override;
  bool GetFileTracker(int64_t tracker_id, FileTracker* tracker) const override;
  void StoreFileMetadata(std::unique_ptr<FileMetadata> metadata) override;
  void StoreFileTracker(std::unique_ptr<FileTracker> tracker) override;
  void RemoveFileMetadata(const std::string& file_id) override;
  void RemoveFileTracker(int64_t tracker_id) override;
  TrackerIDSet GetFileTrackerIDsByFileID(
      const std::string& file_id) const override;
  int64_t GetAppRootTracker(const std::string& app_id) const override;
  TrackerIDSet GetFileTrackerIDsByParentAndTitle(
      int64_t parent_tracker_id,
      const std::string& title) const override;
  std::vector<int64_t> GetFileTrackerIDsByParent(
      int64_t parent_tracker_id) const override;
  std::string PickMultiTrackerFileID() const override;
  ParentIDAndTitle PickMultiBackingFilePath() const override;
  int64_t PickDirtyTracker() const override;
  void DemoteDirtyTracker(int64_t tracker_id) override;
  bool HasDemotedDirtyTracker() const override;
  void PromoteDemotedDirtyTracker(int64_t tracker_id) override;
  bool PromoteDemotedDirtyTrackers() override;
  size_t CountDirtyTracker() const override;
  size_t CountFileMetadata() const override;
  size_t CountFileTracker() const override;
  void SetSyncRootRevalidated() const override;
  void SetSyncRootTrackerID(int64_t sync_root_id) const override;
  void SetLargestChangeID(int64_t largest_change_id) const override;
  void SetNextTrackerID(int64_t next_tracker_id) const override;
  bool IsSyncRootRevalidated() const override;
  int64_t GetSyncRootTrackerID() const override;
  int64_t GetLargestChangeID() const override;
  int64_t GetNextTrackerID() const override;
  std::vector<std::string> GetRegisteredAppIDs() const override;

 private:
  typedef std::unordered_map<std::string, std::unique_ptr<FileMetadata>>
      MetadataByID;
  typedef std::unordered_map<int64_t, std::unique_ptr<FileTracker>> TrackerByID;
  typedef std::unordered_map<std::string, TrackerIDSet> TrackerIDsByFileID;
  typedef std::unordered_map<std::string, TrackerIDSet> TrackerIDsByTitle;
  typedef std::map<int64_t, TrackerIDsByTitle> TrackerIDsByParentAndTitle;
  typedef std::unordered_map<std::string, int64_t> TrackerIDByAppID;
  typedef std::unordered_set<std::string> FileIDSet;
  typedef std::unordered_set<ParentIDAndTitle> PathSet;
  typedef std::set<int64_t> DirtyTrackers;

  friend class MetadataDatabaseTest;

  explicit MetadataDatabaseIndex(LevelDBWrapper* db);
  void Initialize(std::unique_ptr<ServiceMetadata> service_metadata,
                  DatabaseContents* contents);

  // Maintains |app_root_by_app_id_|.
  void AddToAppIDIndex(const FileTracker& new_tracker);
  void UpdateInAppIDIndex(const FileTracker& old_tracker,
                          const FileTracker& new_tracker);
  void RemoveFromAppIDIndex(const FileTracker& tracker);

  // Maintains |trackers_by_file_id_| and |multi_tracker_file_ids_|.
  void AddToFileIDIndexes(const FileTracker& new_tracker);
  void UpdateInFileIDIndexes(const FileTracker& old_tracker,
                             const FileTracker& new_tracker);
  void RemoveFromFileIDIndexes(const FileTracker& tracker);

  // Maintains |trackers_by_parent_and_title_| and |multi_backing_file_paths_|.
  void AddToPathIndexes(const FileTracker& new_tracker);
  void UpdateInPathIndexes(const FileTracker& old_tracker,
                           const FileTracker& new_tracker1);
  void RemoveFromPathIndexes(const FileTracker& tracker);

  // Maintains |dirty_trackers_| and |demoted_dirty_trackers_|.
  void AddToDirtyTrackerIndexes(const FileTracker& new_tracker);
  void UpdateInDirtyTrackerIndexes(const FileTracker& old_tracker,
                                   const FileTracker& new_tracker);
  void RemoveFromDirtyTrackerIndexes(const FileTracker& tracker);

  std::unique_ptr<ServiceMetadata> service_metadata_;
  raw_ptr<LevelDBWrapper, DanglingUntriaged> db_;  // Not owned

  MetadataByID metadata_by_id_;
  TrackerByID tracker_by_id_;

  TrackerIDByAppID app_root_by_app_id_;

  TrackerIDsByFileID trackers_by_file_id_;
  FileIDSet multi_tracker_file_ids_;

  TrackerIDsByParentAndTitle trackers_by_parent_and_title_;
  PathSet multi_backing_file_paths_;

  DirtyTrackers dirty_trackers_;
  DirtyTrackers demoted_dirty_trackers_;
};

}  // namespace drive_backend
}  // namespace sync_file_system

#endif  // CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_INDEX_H_
