// Copyright (c) 2011 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 CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_HARNESS_H_
#define CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_HARNESS_H_
#pragma once

#include <string>
#include <vector>

#include "base/basictypes.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_observer.h"
#include "chrome/browser/sync/syncable/model_type.h"

class Profile;

namespace browser_sync {
  namespace sessions {
    struct SyncSessionSnapshot;
  }
}

// An instance of this class is basically our notion of a "sync client" for
// automation purposes. It harnesses the ProfileSyncService member of the
// profile passed to it on construction and automates certain things like setup
// and authentication. It provides ways to "wait" adequate periods of time for
// several clients to get to the same state.
class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
 public:
  ProfileSyncServiceHarness(Profile* profile,
                            const std::string& username,
                            const std::string& password,
                            int id);

  virtual ~ProfileSyncServiceHarness() {}

  // Creates a ProfileSyncServiceHarness object and attaches it to |profile|, a
  // profile that is assumed to have been signed into sync in the past. Caller
  // takes ownership.
  static ProfileSyncServiceHarness* CreateAndAttach(Profile* profile);

  // Sets the GAIA credentials with which to sign in to sync.
  void SetCredentials(const std::string& username, const std::string& password);

  // Returns true if sync has been enabled on |profile_|.
  bool IsSyncAlreadySetup();

  // Creates a ProfileSyncService for the profile passed at construction and
  // enables sync for all available datatypes. Returns true only after sync has
  // been fully initialized and authenticated, and we are ready to process
  // changes.
  bool SetupSync();

  // Same as the above method, but enables sync only for the datatypes contained
  // in |synced_datatypes|.
  bool SetupSync(const syncable::ModelTypeSet& synced_datatypes);

  // ProfileSyncServiceObserver implementation.
  virtual void OnStateChanged();

  // Blocks the caller until this harness has completed a single sync cycle
  // since the previous one.  Returns true if a sync cycle has completed.
  bool AwaitSyncCycleCompletion(const std::string& reason);

  // Blocks the caller until this harness has observed that the sync engine
  // has downloaded all the changes seen by the |partner| harness's client.
  bool WaitUntilTimestampMatches(
      ProfileSyncServiceHarness* partner, const std::string& reason);

  // Calling this acts as a barrier and blocks the caller until |this| and
  // |partner| have both completed a sync cycle.  When calling this method,
  // the |partner| should be the passive responder who responds to the actions
  // of |this|.  This method relies upon the synchronization of callbacks
  // from the message queue. Returns true if two sync cycles have completed.
  // Note: Use this method when exactly one client makes local change(s), and
  // exactly one client is waiting to receive those changes.
  bool AwaitMutualSyncCycleCompletion(ProfileSyncServiceHarness* partner);

  // Blocks the caller until |this| completes its ongoing sync cycle and every
  // other client in |partners| have achieved identical download progresses.
  // Note: Use this method when exactly one client makes local change(s),
  // and more than one client is waiting to receive those changes.
  bool AwaitGroupSyncCycleCompletion(
      std::vector<ProfileSyncServiceHarness*>& partners);

  // Blocks the caller until every client in |clients| completes its ongoing
  // sync cycle and all the clients' timestamps match.  Note: Use this method
  // when more than one client makes local change(s), and more than one client
  // is waiting to receive those changes.
  static bool AwaitQuiescence(
      std::vector<ProfileSyncServiceHarness*>& clients);

  // If a SetPassphrase call has been issued with a valid passphrase, this
  // will wait until the passphrase has been accepted.
  bool AwaitPassphraseAccepted();

  // Returns the ProfileSyncService member of the the sync client.
  ProfileSyncService* service() { return service_; }

  // Returns the status of the ProfileSyncService member of the the sync client.
  ProfileSyncService::Status GetStatus();

  // See ProfileSyncService::ShouldPushChanges().
  bool ServiceIsPushingChanges() { return service_->ShouldPushChanges(); }

  // Enables sync for a particular sync datatype.
  void EnableSyncForDatatype(syncable::ModelType datatype);

  // Disables sync for a particular sync datatype.
  void DisableSyncForDatatype(syncable::ModelType datatype);

  // Enables sync for all sync datatypes.
  void EnableSyncForAllDatatypes();

  // Disables sync for all sync datatypes.
  void DisableSyncForAllDatatypes();

  // Returns a snapshot of the current sync session.
  const browser_sync::sessions::SyncSessionSnapshot*
      GetLastSessionSnapshot() const;

  // Encrypt the datatype |type|. This method will block while the sync backend
  // host performs the encryption or a timeout is reached. Returns false if
  // encryption failed, else true.
  // Note: this method does not currently support tracking encryption status
  // while other sync activities are being performed. Sync should be fully
  // synced when this is called.
  bool EnableEncryptionForType(syncable::ModelType type);

  // Check if |type| is encrypted.
  bool IsTypeEncrypted(syncable::ModelType type);

 private:
  friend class StateChangeTimeoutEvent;

  enum WaitState {
    // The sync client has just been initialized.
    INITIAL_WAIT_STATE = 0,

    // The sync client awaits the OnBackendInitialized() callback.
    WAITING_FOR_ON_BACKEND_INITIALIZED,

    // The sync client is waiting for the first sync cycle to complete.
    WAITING_FOR_INITIAL_SYNC,

    // The sync client is waiting for an ongoing sync cycle to complete.
    WAITING_FOR_SYNC_TO_FINISH,

    // The sync client anticipates incoming updates leading to a new sync cycle.
    WAITING_FOR_UPDATES,

    // The sync client is waiting for its passphrase to be accepted by the
    // cryptographer.
    WAITING_FOR_PASSPHRASE_ACCEPTED,

    // The sync client anticipates encryption of new datatypes.
    WAITING_FOR_ENCRYPTION,

    // The sync client cannot reach the server.
    SERVER_UNREACHABLE,

    // The sync client is fully synced and there are no pending updates.
    FULLY_SYNCED,

    // Syncing is disabled for the client.
    SYNC_DISABLED,

    NUMBER_OF_STATES,
  };

  // Called from the observer when the current wait state has been completed.
  void SignalStateCompleteWithNextState(WaitState next_state);

  // Indicates that the operation being waited on is complete.
  void SignalStateComplete();

  // Finite state machine for controlling state.  Returns true only if a state
  // change has taken place.
  bool RunStateChangeMachine();

  // Returns true if a status change took place, false on timeout.
  bool AwaitStatusChangeWithTimeout(int timeout_milliseconds,
                                    const std::string& reason);

  // Returns true if the sync client has no unsynced items.
  bool IsSynced();

  // Returns true if this client has downloaded all the items that the
  // other client has.
  bool MatchesOtherClient(ProfileSyncServiceHarness* partner);

  // Logs message with relevant info about client's sync state (if available).
  void LogClientInfo(const std::string& message);

  // Gets the current progress indicator of the current sync session
  // for a particular datatype.
  std::string GetUpdatedTimestamp(syncable::ModelType model_type);

  // When in WAITING_FOR_ENCRYPTION state, we check to see if this type is now
  // encrypted to determine if we're done.
  syncable::ModelType waiting_for_encryption_type_;

  WaitState wait_state_;

  Profile* profile_;
  ProfileSyncService* service_;

  // The harness of the client whose update progress marker we're expecting
  // eventually match.
  ProfileSyncServiceHarness* timestamp_match_partner_;

  // Credentials used for GAIA authentication.
  std::string username_;
  std::string password_;

  // Client ID, used for logging purposes.
  int id_;

  DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceHarness);
};

#endif  // CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_HARNESS_H_