#ifndef GOOGLE_APIS_GCM_GCM_CLIENT_IMPL_H_
#define GOOGLE_APIS_GCM_GCM_CLIENT_IMPL_H_
#include <map>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/stl_util.h"
#include "google_apis/gcm/base/mcs_message.h"
#include "google_apis/gcm/engine/gcm_store.h"
#include "google_apis/gcm/engine/mcs_client.h"
#include "google_apis/gcm/engine/registration_request.h"
#include "google_apis/gcm/engine/unregistration_request.h"
#include "google_apis/gcm/gcm_client.h"
#include "google_apis/gcm/protocol/android_checkin.pb.h"
#include "net/base/net_log.h"
#include "net/url_request/url_request_context_getter.h"
class GURL;
namespace base {
class Clock;
class FilePath;
}  
namespace net {
class HttpNetworkSession;
}  
namespace gcm {
class CheckinRequest;
class ConnectionFactory;
class GCMClientImplTest;
class GCM_EXPORT GCMInternalsBuilder {
 public:
  GCMInternalsBuilder();
  virtual ~GCMInternalsBuilder();
  virtual scoped_ptr<base::Clock> BuildClock();
  virtual scoped_ptr<GCMStore> BuildGCMStore(
      const base::FilePath& path,
      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
  virtual scoped_ptr<MCSClient> BuildMCSClient(
      const std::string& version,
      base::Clock* clock,
      ConnectionFactory* connection_factory,
      GCMStore* gcm_store);
  virtual scoped_ptr<ConnectionFactory> BuildConnectionFactory(
      const std::vector<GURL>& endpoints,
      const net::BackoffEntry::Policy& backoff_policy,
      scoped_refptr<net::HttpNetworkSession> network_session,
      net::NetLog* net_log);
};
class GCM_EXPORT GCMClientImpl : public GCMClient {
 public:
  explicit GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder);
  virtual ~GCMClientImpl();
  
  virtual void Initialize(
      const checkin_proto::ChromeBuildProto& chrome_build_proto,
      const base::FilePath& store_path,
      const std::vector<std::string>& account_ids,
      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
      const scoped_refptr<net::URLRequestContextGetter>&
          url_request_context_getter,
      Delegate* delegate) OVERRIDE;
  virtual void Load() OVERRIDE;
  virtual void Stop() OVERRIDE;
  virtual void CheckOut() OVERRIDE;
  virtual void Register(const std::string& app_id,
                        const std::vector<std::string>& sender_ids) OVERRIDE;
  virtual void Unregister(const std::string& app_id) OVERRIDE;
  virtual void Send(const std::string& app_id,
                    const std::string& receiver_id,
                    const OutgoingMessage& message) OVERRIDE;
  virtual GCMStatistics GetStatistics() const OVERRIDE;
 private:
  
  
  
  enum State {
    
    UNINITIALIZED,
    
    INITIALIZED,
    
    LOADING,
    
    INITIAL_DEVICE_CHECKIN,
    
    READY,
  };
  
  struct GCM_EXPORT CheckinInfo {
    CheckinInfo() : android_id(0), secret(0) {}
    bool IsValid() const { return android_id != 0 && secret != 0; }
    void Reset() {
      android_id = 0;
      secret = 0;
    }
    uint64 android_id;
    uint64 secret;
  };
  
  
  
  typedef std::map<std::string, RegistrationRequest*>
      PendingRegistrationRequests;
  
  
  
  typedef std::map<std::string, UnregistrationRequest*>
      PendingUnregistrationRequests;
  friend class GCMClientImplTest;
  
  std::string GetStateString() const;
  
  
  void OnMessageReceivedFromMCS(const gcm::MCSMessage& message);
  
  void OnMessageSentToMCS(int64 user_serial_number,
                          const std::string& app_id,
                          const std::string& message_id,
                          MCSClient::MessageSendStatus status);
  
  void OnMCSError();
  
  
  void OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result);
  
  void InitializeMCSClient(scoped_ptr<GCMStore::LoadResult> result);
  
  void OnFirstTimeDeviceCheckinCompleted(const CheckinInfo& checkin_info);
  
  void StartMCSLogin();
  
  void ResetState();
  
  
  void OnReady();
  
  void StartCheckin();
  
  
  
  void OnCheckinCompleted(uint64 android_id,
                          uint64 security_token);
  
  
  void SchedulePeriodicCheckin(const base::Time& last_checkin_time);
  
  void SetLastCheckinTimeCallback(bool success);
  
  void SetDeviceCredentialsCallback(bool success);
  
  void UpdateRegistrationCallback(bool success);
  
  void OnRegisterCompleted(const std::string& app_id,
                           const std::vector<std::string>& sender_ids,
                           RegistrationRequest::Status status,
                           const std::string& registration_id);
  
  void OnUnregisterCompleted(const std::string& app_id,
                             UnregistrationRequest::Status status);
  
  void OnGCMStoreDestroyed(bool success);
  
  void HandleIncomingMessage(const gcm::MCSMessage& message);
  
  
  void HandleIncomingDataMessage(
      const mcs_proto::DataMessageStanza& data_message_stanza,
      MessageData& message_data);
  
  
  void HandleIncomingSendError(
      const mcs_proto::DataMessageStanza& data_message_stanza,
      MessageData& message_data);
  
  scoped_ptr<GCMInternalsBuilder> internals_builder_;
  
  State state_;
  Delegate* delegate_;
  
  CheckinInfo device_checkin_info_;
  
  
  scoped_ptr<base::Clock> clock_;
  
  
  checkin_proto::ChromeBuildProto chrome_build_proto_;
  
  
  scoped_ptr<GCMStore> gcm_store_;
  scoped_refptr<net::HttpNetworkSession> network_session_;
  net::BoundNetLog net_log_;
  scoped_ptr<ConnectionFactory> connection_factory_;
  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
  
  scoped_ptr<MCSClient> mcs_client_;
  scoped_ptr<CheckinRequest> checkin_request_;
  std::vector<std::string> account_ids_;
  
  RegistrationInfoMap registrations_;
  
  
  PendingRegistrationRequests pending_registration_requests_;
  STLValueDeleter<PendingRegistrationRequests>
      pending_registration_requests_deleter_;
  
  
  PendingUnregistrationRequests pending_unregistration_requests_;
  STLValueDeleter<PendingUnregistrationRequests>
      pending_unregistration_requests_deleter_;
  
  base::WeakPtrFactory<GCMClientImpl> weak_ptr_factory_;
  DISALLOW_COPY_AND_ASSIGN(GCMClientImpl);
};
}  
#endif