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

#ifndef COMPONENTS_SYNC_DRIVER_DATA_TYPE_MANAGER_IMPL_H__
#define COMPONENTS_SYNC_DRIVER_DATA_TYPE_MANAGER_IMPL_H__

#include "components/sync_driver/data_type_manager.h"

#include <map>
#include <queue>
#include <vector>

#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/sync_driver/backend_data_type_configurer.h"
#include "components/sync_driver/model_association_manager.h"

namespace syncer {
struct DataTypeConfigurationStats;
class DataTypeDebugInfoListener;
template <typename T> class WeakHandle;
}

namespace sync_driver {

class DataTypeController;
class DataTypeEncryptionHandler;
class DataTypeManagerObserver;

// List of data types grouped by priority and ordered from high priority to
// low priority.
typedef std::queue<syncer::ModelTypeSet> TypeSetPriorityList;

class DataTypeManagerImpl : public DataTypeManager,
                            public ModelAssociationManagerDelegate {
 public:
  DataTypeManagerImpl(
      const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
          debug_info_listener,
      const DataTypeController::TypeMap* controllers,
      const DataTypeEncryptionHandler* encryption_handler,
      BackendDataTypeConfigurer* configurer,
      DataTypeManagerObserver* observer);
  ~DataTypeManagerImpl() override;

  // DataTypeManager interface.
  void Configure(syncer::ModelTypeSet desired_types,
                 syncer::ConfigureReason reason) override;
  void ReenableType(syncer::ModelType type) override;
  void ResetDataTypeErrors() override;

  // Needed only for backend migration.
  void PurgeForMigration(syncer::ModelTypeSet undesired_types,
                         syncer::ConfigureReason reason) override;

  void Stop() override;
  State state() const override;

  // |ModelAssociationManagerDelegate| implementation.
  void OnAllDataTypesReadyForConfigure() override;
  void OnSingleDataTypeAssociationDone(
      syncer::ModelType type,
      const syncer::DataTypeAssociationStats& association_stats) override;
  void OnModelAssociationDone(
      const DataTypeManager::ConfigureResult& result) override;
  void OnSingleDataTypeWillStop(syncer::ModelType type,
                                const syncer::SyncError& error) override;

  // Used by unit tests. TODO(sync) : This would go away if we made
  // this class be able to do Dependency injection. crbug.com/129212.
  ModelAssociationManager* GetModelAssociationManagerForTesting() {
    return &model_association_manager_;
  }

 private:
  // Helper enum for identifying which types within a priority group to
  // associate.
  enum AssociationGroup {
    // Those types that were already downloaded and didn't have an error at
    // configuration time. Corresponds with AssociationTypesInfo's
    // |ready_types|. These types can start associating as soon as the
    // ModelAssociationManager is not busy.
    READY_AT_CONFIG,
    // All other types, including first time sync types and those that have
    // encountered an error. These types must wait until the syncer has done
    // any db changes and/or downloads before associating.
    UNREADY_AT_CONFIG,
  };

  friend class TestDataTypeManager;

  // Abort configuration and stop all data types due to configuration errors.
  void Abort(ConfigureStatus status);

  // Returns the priority types (control + priority user types).
  // Virtual for overriding during tests.
  virtual syncer::ModelTypeSet GetPriorityTypes() const;

  // Divide |types| into sets by their priorities and return the sets from
  // high priority to low priority.
  TypeSetPriorityList PrioritizeTypes(const syncer::ModelTypeSet& types);

  // Post a task to reconfigure when no downloading or association are running.
  void ProcessReconfigure();

  void Restart(syncer::ConfigureReason reason);
  void DownloadReady(syncer::ModelTypeSet types_to_download,
                     syncer::ModelTypeSet first_sync_types,
                     syncer::ModelTypeSet failed_configuration_types);

  // Notification from the SBH that download failed due to a transient
  // error and it will be retried.
  void OnDownloadRetry();
  void NotifyStart();
  void NotifyDone(const ConfigureResult& result);

  // Add to |configure_time_delta_| the time since we last called
  // Restart().
  void AddToConfigureTime();

  void ConfigureImpl(syncer::ModelTypeSet desired_types,
                     syncer::ConfigureReason reason);

  // Calls data type controllers of requested types to register with backend.
  void RegisterTypesWithBackend();

  BackendDataTypeConfigurer::DataTypeConfigStateMap
  BuildDataTypeConfigStateMap(
      const syncer::ModelTypeSet& types_being_configured) const;

  // Start download of next set of types in |download_types_queue_| (if
  // any exist, does nothing otherwise).
  // Will kick off association of any new ready types.
  void StartNextDownload(syncer::ModelTypeSet high_priority_types_before);

  // Start association of next batch of data types after association of
  // previous batch finishes. |group| controls which set of types within
  // an AssociationTypesInfo to associate. Does nothing if model associator
  // is busy performing association.
  void StartNextAssociation(AssociationGroup group);

  void StopImpl();

  BackendDataTypeConfigurer* configurer_;
  // Map of all data type controllers that are available for sync.
  // This list is determined at startup by various command line flags.
  const DataTypeController::TypeMap* controllers_;
  State state_;
  syncer::ModelTypeSet last_requested_types_;

  // Whether an attempt to reconfigure was made while we were busy configuring.
  // The |last_requested_types_| will reflect the newest set of requested types.
  bool needs_reconfigure_;

  // The reason for the last reconfigure attempt. Note: this will be set to a
  // valid value only when |needs_reconfigure_| is set.
  syncer::ConfigureReason last_configure_reason_;

  // The last time Restart() was called.
  base::Time last_restart_time_;

  // The accumulated time spent between calls to Restart() and going
  // to the DONE state.
  base::TimeDelta configure_time_delta_;

  // Sync's datatype debug info listener, which we pass model association
  // statistics to.
  const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>
      debug_info_listener_;

  // The manager that handles the model association of the individual types.
  ModelAssociationManager model_association_manager_;

  // DataTypeManager must have only one observer -- the ProfileSyncService that
  // created it and manages its lifetime.
  DataTypeManagerObserver* const observer_;

  // For querying failed data types (having unrecoverable error) when
  // configuring backend.
  DataTypeStatusTable data_type_status_table_;

  // Types waiting to be downloaded.
  TypeSetPriorityList download_types_queue_;

  // Types waiting for association and related time tracking info.
  struct AssociationTypesInfo {
    AssociationTypesInfo();
    AssociationTypesInfo(const AssociationTypesInfo& other);
    ~AssociationTypesInfo();

    // Types to associate.
    syncer::ModelTypeSet types;
    // Types that have just been downloaded and are being associated for the
    // first time. This includes types that had previously encountered an error
    // and had to be purged/unapplied from the sync db.
    // This is a subset of |types|.
    syncer::ModelTypeSet first_sync_types;
    // Types that were already ready for association at configuration time.
    syncer::ModelTypeSet ready_types;
    // Time at which |types| began downloading.
    base::Time download_start_time;
    // Time at which |types| finished downloading.
    base::Time download_ready_time;
    // Time at which the association for |read_types| began.
    base::Time ready_association_request_time;
    // Time at which the association for |types| began (not relevant to
    // |ready_types|.
    base::Time full_association_request_time;
    // The set of types that are higher priority (and were therefore blocking)
    // the association of |types|.
    syncer::ModelTypeSet high_priority_types_before;
    // The subset of |types| that were successfully configured.
    syncer::ModelTypeSet configured_types;
  };
  std::queue<AssociationTypesInfo> association_types_queue_;

  // The encryption handler lets the DataTypeManager know the state of sync
  // datatype encryption.
  const DataTypeEncryptionHandler* encryption_handler_;

  // Association and time stats of data type configuration.
  std::vector<syncer::DataTypeConfigurationStats> configuration_stats_;

  // True iff we are in the process of catching up datatypes.
  bool catch_up_in_progress_;

  // Configuration process is started when ModelAssociationManager notifies
  // DataTypeManager that all types are ready for configure.
  // This flag ensures that this process is started only once.
  bool download_started_;

  base::WeakPtrFactory<DataTypeManagerImpl> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(DataTypeManagerImpl);
};

}  // namespace sync_driver

#endif  // COMPONENTS_SYNC_DRIVER_DATA_TYPE_MANAGER_IMPL_H__
