// Copyright (c) 2006-2008 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/bookmarks/bookmark_folder_tree_model.h"

#include "chrome/app/theme/theme_resources.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"

#include "generated_resources.h"

BookmarkFolderTreeModel::BookmarkFolderTreeModel(BookmarkModel* model)
    : views::TreeNodeModel<FolderNode>(new FolderNode(NULL)),
      model_(model),
      recently_bookmarked_node_(new FolderNode(NULL)),
      search_node_(new FolderNode(NULL)){
  recently_bookmarked_node_->SetTitle(
      l10n_util::GetString(IDS_BOOKMARK_TREE_RECENTLY_BOOKMARKED_NODE_TITLE));
  search_node_->SetTitle(
      l10n_util::GetString(IDS_BOOKMARK_TREE_SEARCH_NODE_TITLE));
  if (model_->IsLoaded())
    AddRootChildren();
  model_->AddObserver(this);
}

BookmarkFolderTreeModel::~BookmarkFolderTreeModel() {
  if (model_)
    model_->RemoveObserver(this);
}

BookmarkFolderTreeModel::NodeType BookmarkFolderTreeModel::GetNodeType(
    views::TreeModelNode* node) {
  if (node == recently_bookmarked_node_)
    return RECENTLY_BOOKMARKED;
  if (node == search_node_)
    return SEARCH;
  if (node == GetRoot())
    return NONE;
  return BOOKMARK;
}

FolderNode* BookmarkFolderTreeModel::GetFolderNodeForBookmarkNode(
    BookmarkNode* node) {
  if (!node->is_folder())
    return NULL;

  return GetFolderNodeForBookmarkNode(AsNode(GetRoot()), node);
}

BookmarkNode* BookmarkFolderTreeModel::TreeNodeAsBookmarkNode(
    views::TreeModelNode* node) {
  if (GetNodeType(node) != BOOKMARK)
    return NULL;
  return AsNode(node)->value;
}

void BookmarkFolderTreeModel::Loaded(BookmarkModel* model) {
  AddRootChildren();
}

void BookmarkFolderTreeModel::BookmarkModelBeingDeleted(BookmarkModel* model) {
  DCHECK(model_);
  model_->RemoveObserver(this);
  model_ = NULL;
}

void BookmarkFolderTreeModel::BookmarkNodeMoved(BookmarkModel* model,
                                                BookmarkNode* old_parent,
                                                int old_index,
                                                BookmarkNode* new_parent,
                                                int new_index) {
  BookmarkNode* moved_node = new_parent->GetChild(new_index);
  if (!moved_node->is_folder())
    return;  // We're only showing folders, so we can ignore this.

  FolderNode* moved_folder_node = GetFolderNodeForBookmarkNode(moved_node);
  DCHECK(moved_folder_node);
  int old_folder_index =
      moved_folder_node->GetParent()->IndexOfChild(moved_folder_node);
  Remove(moved_folder_node->GetParent(), old_folder_index);
  int new_folder_index = CalculateIndexForChild(moved_node);
  Add(GetFolderNodeForBookmarkNode(new_parent), new_folder_index,
      moved_folder_node);
}

void BookmarkFolderTreeModel::BookmarkNodeAdded(BookmarkModel* model,
                                                BookmarkNode* parent,
                                                int index) {
  BookmarkNode* new_node = parent->GetChild(index);
  if (!new_node->is_folder())
    return;  // We're only showing folders, so we can ignore this.

  int folder_index = CalculateIndexForChild(new_node);
  Add(GetFolderNodeForBookmarkNode(parent), folder_index,
      CreateFolderNode(new_node));
}

void BookmarkFolderTreeModel::BookmarkNodeRemoved(BookmarkModel* model,
                                                  BookmarkNode* parent,
                                                  int index,
                                                  BookmarkNode* node) {
  if (!node->is_folder())
    return;  // We're only showing folders.

  FolderNode* folder_node = GetFolderNodeForBookmarkNode(parent);
  DCHECK(folder_node);
  for (int i = 0; i < folder_node->GetChildCount(); ++i) {
    if (folder_node->GetChild(i)->value == node) {
      scoped_ptr<FolderNode> removed_node(Remove(folder_node, i));
      return;
    }
  }

  // If we get here it means a folder was removed that we didn't know about,
  // which shouldn't happen.
  NOTREACHED();
}

void BookmarkFolderTreeModel::BookmarkNodeChanged(BookmarkModel* model,
                                                  BookmarkNode* node) {
  if (!node->is_folder())
    return;

  FolderNode* folder_node = GetFolderNodeForBookmarkNode(node);
  if (!folder_node)
    return;

  folder_node->SetTitle(node->GetTitle());
  if (GetObserver())
    GetObserver()->TreeNodeChanged(this, folder_node);
}

void BookmarkFolderTreeModel::GetIcons(std::vector<SkBitmap>* icons) {
  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  icons->push_back(*rb.GetBitmapNamed(IDR_BOOKMARK_MANAGER_RECENT_ICON));
  icons->push_back(*rb.GetBitmapNamed(IDR_BOOKMARK_MANAGER_SEARCH_ICON));
}

int BookmarkFolderTreeModel::GetIconIndex(views::TreeModelNode* node) {
  if (node == recently_bookmarked_node_)
    return 0;
  if (node == search_node_)
    return 1;

  // Return -1 to use the default.
  return -1;
}

void BookmarkFolderTreeModel::AddRootChildren() {
  Add(AsNode(GetRoot()), 0, CreateFolderNode(model_->GetBookmarkBarNode()));
  Add(AsNode(GetRoot()), 1, CreateFolderNode(model_->other_node()));
  Add(AsNode(GetRoot()), 2, recently_bookmarked_node_);
  Add(AsNode(GetRoot()), 3, search_node_);
}

FolderNode* BookmarkFolderTreeModel::GetFolderNodeForBookmarkNode(
    FolderNode* folder_node,
    BookmarkNode* node) {
  DCHECK(node->is_folder());
  if (folder_node->value == node)
    return folder_node;
  for (int i = 0; i < folder_node->GetChildCount(); ++i) {
    FolderNode* result =
        GetFolderNodeForBookmarkNode(folder_node->GetChild(i), node);
    if (result)
      return result;
  }
  return NULL;
}

FolderNode* BookmarkFolderTreeModel::CreateFolderNode(BookmarkNode* node) {
  DCHECK(node->is_folder());
  FolderNode* folder_node = new FolderNode(node);
  folder_node->SetTitle(node->GetTitle());

  // And clone the children folders.
  for (int i = 0; i < node->GetChildCount(); ++i) {
    BookmarkNode* child = node->GetChild(i);
    if (child->is_folder())
      folder_node->Add(folder_node->GetChildCount(), CreateFolderNode(child));
  }
  return folder_node;
}

int BookmarkFolderTreeModel::CalculateIndexForChild(BookmarkNode* node) {
  BookmarkNode* parent = node->GetParent();
  DCHECK(parent);
  for (int i = 0, folder_count = 0; i < parent->GetChildCount(); ++i) {
    BookmarkNode* child = parent->GetChild(i);
    if (child == node)
      return folder_count;
    if (child->is_folder())
      folder_count++;
  }
  NOTREACHED();
  return 0;
}