text: // Copyright 2013 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.
  
  #include "chrome/browser/sync_file_system/drive_backend/register_app_task.h"
  
  #include "base/bind.h"
  #include "base/location.h"
  #include "chrome/browser/drive/drive_api_util.h"
  #include "chrome/browser/drive/drive_service_interface.h"
  #include "chrome/browser/google_apis/drive_api_parser.h"
  #include "chrome/browser/google_apis/gdata_wapi_parser.h"
  #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
  #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
  #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
  #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
  #include "chrome/browser/sync_file_system/drive_backend/tracker_set.h"
  #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
  
  namespace sync_file_system {
  namespace drive_backend {
  
  namespace {
  
  bool CompareOnCTime(const FileTracker& left,
                      const FileTracker& right) {
    return left.synced_details().creation_time() <
        right.synced_details().creation_time();
  }
  
  }  // namespace
  
  RegisterAppTask::RegisterAppTask(SyncEngineContext* sync_context,
                                   const std::string& app_id)
      : sync_context_(sync_context),
        create_folder_retry_count_(0),
        app_id_(app_id),
        weak_ptr_factory_(this) {
  }
  
  RegisterAppTask::~RegisterAppTask() {
  }
  
  void RegisterAppTask::Run(const SyncStatusCallback& callback) {
    if (create_folder_retry_count_++ >= kMaxRetry) {
      callback.Run(SYNC_STATUS_FAILED);
      return;
    }
  
    if (!metadata_database() || !drive_service()) {
      callback.Run(SYNC_STATUS_FAILED);
      return;
    }
  
    int64 sync_root = metadata_database()->GetSyncRootTrackerID();
    TrackerSet trackers;
    if (!metadata_database()->FindTrackersByParentAndTitle(
            sync_root, app_id_, &trackers)) {
      CreateAppRootFolder(callback);
      return;
    }
  
    FileTracker candidate;
    if (!FilterCandidates(trackers, &candidate)) {
      CreateAppRootFolder(callback);
      return;
    }
  
    if (candidate.active()) {
      RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
      return;
    }
  
    RegisterAppIntoDatabase(candidate, callback);
  }
  
  void RegisterAppTask::CreateAppRootFolder(const SyncStatusCallback& callback) {
    int64 sync_root_tracker_id = metadata_database()->GetSyncRootTrackerID();
    FileTracker sync_root_tracker;
    bool should_success = metadata_database()->FindTrackerByTrackerID(
        sync_root_tracker_id,
        &sync_root_tracker);
    DCHECK(should_success);
  
    drive_service()->AddNewDirectory(
        sync_root_tracker.file_id(),
        app_id_,
        base::Bind(&RegisterAppTask::DidCreateAppRootFolder,
                   weak_ptr_factory_.GetWeakPtr(),
                   callback,
                   metadata_database()->GetLargestChangeID()));
  }
  
  void RegisterAppTask::DidCreateAppRootFolder(
      const SyncStatusCallback& callback,
      int64 change_id,
      google_apis::GDataErrorCode error,
      scoped_ptr<google_apis::ResourceEntry> entry) {
    if (error != google_apis::HTTP_SUCCESS &&
        error != google_apis::HTTP_CREATED) {
      callback.Run(SYNC_STATUS_FAILED);
      return;
    }
  
    DCHECK(entry);
    scoped_ptr<google_apis::FileResource> resource(
        drive::util::ConvertResourceEntryToFileResource(*entry));
    metadata_database()->UpdateByFileResource(
        change_id,
        *resource,
        base::Bind(&RegisterAppTask::DidUpdateDatabase,
                   weak_ptr_factory_.GetWeakPtr(),
                   callback,
                   resource->file_id()));
  }
  
  void RegisterAppTask::DidUpdateDatabase(const SyncStatusCallback& callback,
                                          const std::string& file_id,
                                          SyncStatusCode status) {
    if (status != SYNC_STATUS_OK) {
      callback.Run(SYNC_STATUS_FAILED);
      return;
    }
  
    FileMetadata file;
    bool should_success = metadata_database()->FindFileByFileID(
        file_id, &file);
    DCHECK(should_success);
  
    TrackerSet trackers;
    should_success = metadata_database()->FindTrackersByFileID(
        file_id, &trackers);
    DCHECK(should_success);
  
    DCHECK_EQ(1u, trackers.tracker_set().size());
    FileTracker tracker = **trackers.begin();
    DCHECK_EQ(metadata_database()->GetSyncRootTrackerID(),
              tracker.parent_tracker_id());
  
    metadata_database()->UpdateTracker(
        tracker.tracker_id(),
        file.details(),
        base::Bind(&RegisterAppTask::DidPrepareForRegister,
                   weak_ptr_factory_.GetWeakPtr(),
                   callback));
  }
  
  void RegisterAppTask::DidPrepareForRegister(const SyncStatusCallback& callback,
                                              SyncStatusCode status) {
    if (status != SYNC_STATUS_OK) {
      callback.Run(SYNC_STATUS_FAILED);
      return;
    }
  
    // TODO(tzik): Ensure the file didn't make create-create conflict by listing
    // the same title folders.
  
    Run(callback);
  }
  
  bool RegisterAppTask::FilterCandidates(const TrackerSet& trackers,
                                         FileTracker* candidate) {
    DCHECK(candidate);
    if (trackers.has_active()) {
      *candidate = *trackers.active_tracker();
      return true;
    }
  
    FileTracker* oldest_tracker = NULL;
    for (TrackerSet::const_iterator itr = trackers.begin();
         itr != trackers.end(); ++itr) {
      FileTracker* tracker = *itr;
      DCHECK(!tracker->active());
      DCHECK(tracker->has_synced_details());
      if (tracker->synced_details().file_kind() != FILE_KIND_FOLDER)
        continue;
  
      if (tracker->synced_details().deleted())
        continue;
  
      if (oldest_tracker && CompareOnCTime(*oldest_tracker, *tracker))
        continue;
  
      oldest_tracker = tracker;
    }
  
    if (!oldest_tracker)
      return false;
  
    *candidate = *oldest_tracker;
    return true;
  }
  
  void RegisterAppTask::RegisterAppIntoDatabase(
      const FileTracker& tracker,
      const SyncStatusCallback& callback) {
    metadata_database()->RegisterApp(
        app_id_, tracker.file_id(), callback);
  }
  
  MetadataDatabase* RegisterAppTask::metadata_database() {
    return sync_context_->GetMetadataDatabase();
  }
  
  drive::DriveServiceInterface* RegisterAppTask::drive_service() {
    return sync_context_->GetDriveService();
  }
  
  }  // namespace drive_backend
  }  // namespace sync_file_system
  