// 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_GLUE_BOOKMARK_CHANGE_PROCESSOR_H_
#define CHROME_BROWSER_SYNC_GLUE_BOOKMARK_CHANGE_PROCESSOR_H_
#pragma once

#include <vector>

#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/change_processor.h"
#include "chrome/browser/sync/glue/bookmark_model_associator.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"

namespace browser_sync {

// This class is responsible for taking changes from the BookmarkModel
// and applying them to the sync_api 'syncable' model, and vice versa.
// All operations and use of this class are from the UI thread.
// This is currently bookmarks specific.
class BookmarkChangeProcessor : public BookmarkModelObserver,
                                public ChangeProcessor {
 public:
  BookmarkChangeProcessor(BookmarkModelAssociator* model_associator,
                          UnrecoverableErrorHandler* error_handler);
  virtual ~BookmarkChangeProcessor() {}

  // BookmarkModelObserver implementation.
  // BookmarkModel -> sync_api model change application.
  virtual void Loaded(BookmarkModel* model);
  virtual void BookmarkModelBeingDeleted(BookmarkModel* model);
  virtual void BookmarkNodeMoved(BookmarkModel* model,
                                 const BookmarkNode* old_parent,
                                 int old_index,
                                 const BookmarkNode* new_parent,
                                 int new_index);
  virtual void BookmarkNodeAdded(BookmarkModel* model,
                                 const BookmarkNode* parent,
                                 int index);
  virtual void BookmarkNodeRemoved(BookmarkModel* model,
                                   const BookmarkNode* parent,
                                   int index,
                                   const BookmarkNode* node);
  virtual void BookmarkNodeChanged(BookmarkModel* model,
                                   const BookmarkNode* node);
  virtual void BookmarkNodeFaviconLoaded(BookmarkModel* model,
                                         const BookmarkNode* node);
  virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
                                             const BookmarkNode* node);

  // The change processor implementation, responsible for applying changes from
  // the sync model to the bookmarks model.
  virtual void ApplyChangesFromSyncModel(
      const sync_api::BaseTransaction* trans,
      const sync_api::SyncManager::ChangeRecord* changes,
      int change_count);

  // The following methods are static and hence may be invoked at any time,
  // and do not depend on having a running ChangeProcessor.
  // Creates a bookmark node under the given parent node from the given sync
  // node. Returns the newly created node.
  static const BookmarkNode* CreateBookmarkNode(
      sync_api::BaseNode* sync_node,
      const BookmarkNode* parent,
      BookmarkModel* model,
      int index);

  // Sets the favicon of the given bookmark node from the given sync node.
  // Returns whether the favicon was set in the bookmark node.
  // |profile| is the profile that contains the HistoryService and BookmarkModel
  // for the bookmark in question.
  static bool SetBookmarkFavicon(sync_api::BaseNode* sync_node,
                                 const BookmarkNode* bookmark_node,
                                 BookmarkModel* model);

  // Applies the favicon data in |icon_bytes_vector| to |bookmark_node|.
  // |profile| is the profile that contains the HistoryService and BookmarkModel
  // for the bookmark in question.
  static void ApplyBookmarkFavicon(
      const BookmarkNode* bookmark_node,
      Profile* profile,
      const std::vector<unsigned char>& icon_bytes_vector);

  // Sets the favicon of the given sync node from the given bookmark node.
  static void SetSyncNodeFavicon(const BookmarkNode* bookmark_node,
                                 BookmarkModel* model,
                                 sync_api::WriteNode* sync_node);

  // Treat the |index|th child of |parent| as a newly added node, and create a
  // corresponding node in the sync domain using |trans|.  All properties
  // will be transferred to the new node.  A node corresponding to |parent|
  // must already exist and be associated for this call to succeed.  Returns
  // the ID of the just-created node, or if creation fails, kInvalidID.
  static int64 CreateSyncNode(const BookmarkNode* parent,
                              BookmarkModel* model,
                              int index,
                              sync_api::WriteTransaction* trans,
                              BookmarkModelAssociator* associator,
                              UnrecoverableErrorHandler* error_handler);

 protected:
  virtual void StartImpl(Profile* profile);
  virtual void StopImpl();

 private:
  enum MoveOrCreate {
    MOVE,
    CREATE,
  };

  // Create a bookmark node corresponding to |src| if one is not already
  // associated with |src|.  Returns the node that was created or updated.
  const BookmarkNode* CreateOrUpdateBookmarkNode(
      sync_api::BaseNode* src,
      BookmarkModel* model);

  // Helper function to determine the appropriate insertion index of sync node
  // |node| under the Bookmark model node |parent|, to make the positions
  // match up between the two models. This presumes that the predecessor of the
  // item (in the bookmark model) has already been moved into its appropriate
  // position.
  int CalculateBookmarkModelInsertionIndex(
      const BookmarkNode* parent,
      const sync_api::BaseNode* node) const;

  // Helper function used to fix the position of a sync node so that it matches
  // the position of a corresponding bookmark model node. |parent| and
  // |index| identify the bookmark model position.  |dst| is the node whose
  // position is to be fixed.  If |operation| is CREATE, treat |dst| as an
  // uncreated node and set its position via InitByCreation(); otherwise,
  // |dst| is treated as an existing node, and its position will be set via
  // SetPosition().  |trans| is the transaction to which |dst| belongs. Returns
  // false on failure.
  static bool PlaceSyncNode(MoveOrCreate operation,
                            const BookmarkNode* parent,
                            int index,
                            sync_api::WriteTransaction* trans,
                            sync_api::WriteNode* dst,
                            BookmarkModelAssociator* associator);

  // Copy properties (but not position) from |src| to |dst|.
  static void UpdateSyncNodeProperties(const BookmarkNode* src,
                                       BookmarkModel* model,
                                       sync_api::WriteNode* dst);

  // Helper function to encode a bookmark's favicon into a PNG byte vector.
  static void EncodeFavicon(const BookmarkNode* src,
                            BookmarkModel* model,
                            std::vector<unsigned char>* dst);

  // Remove the sync node corresponding to |node|.  It shouldn't have
  // any children.
  void RemoveOneSyncNode(sync_api::WriteTransaction* trans,
                         const BookmarkNode* node);

  // Remove all the sync nodes associated with |node| and its children.
  void RemoveSyncNodeHierarchy(const BookmarkNode* node);

  // The bookmark model we are processing changes from.  Non-NULL when
  // |running_| is true.
  BookmarkModel* bookmark_model_;

  // The two models should be associated according to this ModelAssociator.
  BookmarkModelAssociator* model_associator_;

  DISALLOW_COPY_AND_ASSIGN(BookmarkChangeProcessor);
};

}  // namespace browser_sync

#endif  // CHROME_BROWSER_SYNC_GLUE_BOOKMARK_CHANGE_PROCESSOR_H_