// Copyright (c) 2012 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/extensions/api/storage/settings_sync_processor.h"
#include "chrome/browser/extensions/api/storage/settings_sync_util.h"
#include "components/sync/model/sync_change_processor.h"
#include "components/sync/model/sync_data.h"
#include "components/sync/protocol/extension_setting_specifics.pb.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/api/storage/settings_namespace.h"

using content::BrowserThread;

namespace extensions {

SettingsSyncProcessor::SettingsSyncProcessor(
    const std::string& extension_id,
    syncer::ModelType type,
    syncer::SyncChangeProcessor* sync_processor)
    : extension_id_(extension_id),
      type_(type),
      sync_processor_(sync_processor),
      initialized_(false) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  CHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS);
  CHECK(sync_processor);
}

SettingsSyncProcessor::~SettingsSyncProcessor() {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
}

void SettingsSyncProcessor::Init(const base::DictionaryValue& initial_state) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  CHECK(!initialized_) << "Init called multiple times";

  for (base::DictionaryValue::Iterator i(initial_state); !i.IsAtEnd();
       i.Advance())
    synced_keys_.insert(i.key());

  initialized_ = true;
}

syncer::SyncError SettingsSyncProcessor::SendChanges(
    const ValueStoreChangeList& changes) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  CHECK(initialized_) << "Init not called";

  syncer::SyncChangeList sync_changes;
  std::set<std::string> added_keys;
  std::set<std::string> deleted_keys;

  for (ValueStoreChangeList::const_iterator i = changes.begin();
      i != changes.end(); ++i) {
    const std::string& key = i->key();
    const base::Value* value = i->new_value();
    if (value) {
      if (synced_keys_.count(key)) {
        // New value, key is synced; send ACTION_UPDATE.
        sync_changes.push_back(settings_sync_util::CreateUpdate(
            extension_id_, key, *value, type_));
      } else {
        // New value, key is not synced; send ACTION_ADD.
        sync_changes.push_back(settings_sync_util::CreateAdd(
            extension_id_, key, *value, type_));
        added_keys.insert(key);
      }
    } else {
      if (synced_keys_.count(key)) {
        // Clearing value, key is synced; send ACTION_DELETE.
        sync_changes.push_back(settings_sync_util::CreateDelete(
            extension_id_, key, type_));
        deleted_keys.insert(key);
      } else {
        LOG(WARNING) << "Deleted " << key << " but not in synced_keys_";
      }
    }
  }

  if (sync_changes.empty())
    return syncer::SyncError();

  syncer::SyncError error =
      sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes);
  if (error.IsSet())
    return error;

  synced_keys_.insert(added_keys.begin(), added_keys.end());
  for (std::set<std::string>::iterator i = deleted_keys.begin();
      i != deleted_keys.end(); ++i) {
    synced_keys_.erase(*i);
  }

  return syncer::SyncError();
}

void SettingsSyncProcessor::NotifyChanges(const ValueStoreChangeList& changes) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  CHECK(initialized_) << "Init not called";

  for (ValueStoreChangeList::const_iterator i = changes.begin();
      i != changes.end(); ++i) {
    if (i->new_value())
      synced_keys_.insert(i->key());
    else
      synced_keys_.erase(i->key());
  }
}

}  // namespace extensions
