// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef CHROME_BROWSER_HISTORY_STARRED_URL_DATABASE_H__
#define CHROME_BROWSER_HISTORY_STARRED_URL_DATABASE_H__

#include <map>
#include <set>

#include "base/basictypes.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/history/url_database.h"
#include "chrome/views/tree_node_model.h"
#include "testing/gtest/include/gtest/gtest_prod.h"

struct sqlite3;
class SqliteStatementCache;

namespace history {

// Encapsulates a URL database plus starred information.
//
// WARNING: many of the following methods allow you to update, delete or
// insert starred entries specifying a visual order. These methods do NOT
// adjust the visual order of surrounding entries. You must explicitly do
// it yourself using AdjustStarredVisualOrder as appropriate.
class StarredURLDatabase : public URLDatabase {
 public:
  // Must call InitStarTable() AND any additional init functions provided by
  // URLDatabase before using this class' functions.
  StarredURLDatabase();
  virtual ~StarredURLDatabase();

  // Returns the id of the starred entry. This does NOT return entry.id,
  // rather the database is queried for the id based on entry.url or
  // entry.group_id.
  StarID GetStarIDForEntry(const StarredEntry& entry);

  // Returns the internal star ID for the externally-generated group ID.
  StarID GetStarIDForGroupID(UIStarID group_id);

  // Gets the details for the specified star entry in entry.
  bool GetStarredEntry(StarID star_id, StarredEntry* entry);

  // Creates a starred entry with the requested information. The structure will
  // be updated with the ID of the newly created entry. The URL table will be
  // updated to point to the entry. The URL row will be created if it doesn't
  // exist.
  //
  // We currently only support one entry per URL. This URL should not already be
  // starred when calling this function or it will fail and will return 0.
  StarID CreateStarredEntry(StarredEntry* entry);

  // Returns starred entries.
  // This returns three different result types:
  //   only_on_bookmark_bar == true: only those entries on the bookmark bar are
  //                                 returned.
  //   only_on_bookmark_bar == false and parent_id == 0: all starred
  //                                                     entries/groups.
  //   otherwise only the direct children (groups and entries) of parent_id are
  //   returned.
  bool GetStarredEntries(UIStarID parent_group_id,
                         std::vector<StarredEntry>* entries);

  // Deletes a starred entry. This adjusts the visual order of all siblings
  // after the entry. The deleted entry(s) will be *appended* to
  // |*deleted_entries| (so delete can be called more than once with the same
  // list) and deleted URLs will additionally be added to the set.
  //
  // This function invokes DeleteStarredEntryImpl to do the actual work, which
  // recurses.
  void DeleteStarredEntry(StarID star_id,
                          std::set<GURL>* unstarred_urls,
                          std::vector<StarredEntry>* deleted_entries);

  // Implementation to update the database for UpdateStarredEntry. Returns true
  // on success. If successful, the parent_id, url_id, id and date_added fields
  // of the entry are reset from that of the database.
  bool UpdateStarredEntry(StarredEntry* entry);

  // Gets up to max_count starred entries of type URL adding them to entries.
  // The results are ordered by date added in descending order (most recent
  // first).
  void GetMostRecentStarredEntries(int max_count,
                                   std::vector<StarredEntry>* entries);

  // Gets the URLIDs for all starred entries of type URL whose title matches
  // the specified query.
  void GetURLsForTitlesMatching(const std::wstring& query,
                                std::set<URLID>* ids);

  // Returns true if the starred table is in a sane state. If false, the starred
  // table is likely corrupt and shouldn't be used.
  bool is_starred_valid() const { return is_starred_valid_; }

  // Updates the URL ID for the given starred entry. This is used by the expirer
  // when URL IDs change. It can't use UpdateStarredEntry since that doesn't
  // set the URLID, and it does a bunch of other logic that we don't need.
  bool UpdateURLIDForStar(StarID star_id, URLID new_url_id);

 protected:
  // The unit tests poke our innards.
  friend class HistoryTest;
  FRIEND_TEST(HistoryTest, CreateStarGroup);

  // Returns the database and statement cache for the functions in this
  // interface. The decendent of this class implements these functions to
  // return its objects.
  virtual sqlite3* GetDB() = 0;
  virtual SqliteStatementCache& GetStatementCache() = 0;

  // Creates the starred tables if necessary.
  bool InitStarTable();

  // Makes sure the starred table is in a sane state. This does the following:
  // . Makes sure there is a bookmark bar and other nodes. If no bookmark bar
  //   node is found, the table is dropped and recreated.
  // . Removes any bookmarks with no URL. This can happen if a URL is removed
  //   from the urls table without updating the starred table correctly.
  // . Makes sure the visual order of all nodes is correct.
  // . Moves all bookmarks and folders that are not descendants of the bookmark
  //   bar or other folders to the bookmark bar.
  // . Makes sure there isn't a cycle in the folders. A cycle means some folder
  //   has as its parent one of its children.
  //
  // This returns false if the starred table is in a bad state and couldn't
  // be fixed, true otherwise.
  //
  // This should be invoked after migration.
  bool EnsureStarredIntegrity();

  // Creates a starred entry with the specified parameters in the database.
  // Returns the newly created id, or 0 on failure.
  //
  // WARNING: Does not update the visual order.
  StarID CreateStarredEntryRow(URLID url_id,
                               UIStarID group_id,
                               UIStarID parent_group_id,
                               const std::wstring& title,
                               const Time& date_added,
                               int visual_order,
                               StarredEntry::Type type);

  // Sets the title, parent_id, parent_group_id, visual_order and date_modifed
  // of the specified star entry.
  //
  // WARNING: Does not update the visual order.
  bool UpdateStarredEntryRow(StarID star_id,
                             const std::wstring& title,
                             UIStarID parent_group_id,
                             int visual_order,
                             Time date_modified);

  // Adjusts the visual order of all children of parent_group_id with a
  // visual_order >= start_visual_order by delta. For example,
  // AdjustStarredVisualOrder(10, 0, 1) increments the visual order all children
  // of group 10 with a visual order >= 0 by 1.
  bool AdjustStarredVisualOrder(UIStarID parent_group_id,
                                int start_visual_order,
                                int delta);

  // Deletes the entry from the starred database base on the starred id (NOT
  // the url id).
  //
  // WARNING: Does not update the visual order.
  bool DeleteStarredEntryRow(StarID star_id);

  // Should the integrity of the starred table be checked on every mutation?
  // Default is true. Set to false when running tests that need to allow the
  // table to be in an invalid state while testing.
  bool check_starred_integrity_on_mutation_;

 private:
  // Used when checking integrity of starred table.
  typedef ChromeViews::TreeNodeWithValue<history::StarredEntry> StarredNode;

  // Implementation of DeleteStarredEntryImpl.
  void DeleteStarredEntryImpl(StarID star_id,
                              std::set<GURL>* unstarred_urls,
                              std::vector<StarredEntry>* deleted_entries);

  // Returns the max group id, or 0 if there is an error.
  UIStarID GetMaxGroupID();

  // Gets all the bookmarks and folders creating a StarredNode for each
  // bookmark and folder. On success all the root nodes (bookmark bar node,
  // other folder node, folders with no parent or folders with a parent that
  // would make a cycle) are added to roots.
  //
  // If a group_id occurs more than once, all but the first ones id is added to
  // groups_with_duplicate_ids.
  //
  // All bookmarks not on the bookmark bar/other folder are added to
  // unparented_urls.
  //
  // It's up to the caller to delete the nodes returned in roots and
  // unparented_urls.
  //
  // This is used during integrity enforcing/checking of the starred table.
  bool BuildStarNodes(
      std::set<StarredNode*>* roots,
      std::set<StarID>* groups_with_duplicate_ids,
      std::set<StarredNode*>* unparented_urls,
      std::set<StarID>* empty_url_ids);

  // Sets the visual order of all of node's children match the order in |node|.
  // If the order differs, the database is updated. Returns false if the order
  // differed and the db couldn't be updated.
  bool EnsureVisualOrder(StarredNode* node);

  // Returns the first node in nodes with the specified type, or null if there
  // is not a node with the specified type.
  StarredNode* GetNodeByType(
      const std::set<StarredNode*>& nodes,
      StarredEntry::Type type);

  // Implementation for setting starred integrity. See description of
  // EnsureStarredIntegrity for the details of what this does.
  //
  // All entries in roots that are not the bookmark bar and other node are
  // moved to be children of the bookmark bar node. Similarly all nodes
  // in unparented_urls are moved to be children of the bookmark bar.
  //
  // Returns true on success, false if the starred table is in a bad state and
  // couldn't be repaired.
  bool EnsureStarredIntegrityImpl(
      std::set<StarredNode*>* roots,
      const std::set<StarID>& groups_with_duplicate_ids,
      std::set<StarredNode*>* unparented_urls,
      const std::set<StarID>& empty_url_ids);

  // Resets the visual order and parent_group_id of source's StarredEntry
  // and adds it to the end of new_parent's children.
  //
  // This is used if the starred table is an unexpected state and an entry
  // needs to be moved.
  bool Move(StarredNode* source, StarredNode* new_parent);

  // Drops and recreates the starred table as well as marking all URLs as
  // unstarred. This is used as a last resort if the bookmarks table is
  // totally corrupt.
  bool RecreateStarredEntries();

  // Iterates through the children of node make sure the visual order matches
  // the order in node's children. DCHECKs if the order differs. This recurses.
  void CheckVisualOrder(StarredNode* node);

  // Makes sure the starred table is in a sane state, if not this DCHECKs.
  // This is invoked internally after any mutation to the starred table.
  //
  // This is a no-op in release builds.
  void CheckStarredIntegrity();

  // True if the starred table is valid. This is initialized in
  // EnsureStarredIntegrityImpl.
  bool is_starred_valid_;

  DISALLOW_EVIL_CONSTRUCTORS(StarredURLDatabase);
};

}  // namespace history

#endif  // CHROME_BROWSER_HISTORY_STARRED_URL_DATABASE_H__