// 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.

#include "chrome/browser/custom_home_pages_table_model.h"

#include "base/i18n/rtl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "googleurl/src/gurl.h"
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "net/base/net_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/table_model_observer.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/codec/png_codec.h"

struct CustomHomePagesTableModel::Entry {
  Entry() : title_handle(0), favicon_handle(0) {}

  // URL of the page.
  GURL url;

  // Page title.  If this is empty, we'll display the URL as the entry.
  string16 title;

  // Icon for the page.
  SkBitmap icon;

  // If non-zero, indicates we're loading the title for the page.
  HistoryService::Handle title_handle;

  // If non-zero, indicates we're loading the favicon for the page.
  FaviconService::Handle favicon_handle;
};

CustomHomePagesTableModel::CustomHomePagesTableModel(Profile* profile)
    : default_favicon_(NULL),
      profile_(profile),
      observer_(NULL) {
  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  default_favicon_ = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
}

CustomHomePagesTableModel::~CustomHomePagesTableModel() {
}

void CustomHomePagesTableModel::SetURLs(const std::vector<GURL>& urls) {
  entries_.resize(urls.size());
  for (size_t i = 0; i < urls.size(); ++i) {
    entries_[i].url = urls[i];
    entries_[i].title.erase();
    entries_[i].icon.reset();
    LoadTitleAndFavicon(&(entries_[i]));
  }
  // Complete change, so tell the view to just rebuild itself.
  if (observer_)
    observer_->OnModelChanged();
}

void CustomHomePagesTableModel::Add(int index, const GURL& url) {
  DCHECK(index >= 0 && index <= RowCount());
  entries_.insert(entries_.begin() + static_cast<size_t>(index), Entry());
  entries_[index].url = url;
  LoadTitleAndFavicon(&(entries_[index]));
  if (observer_)
    observer_->OnItemsAdded(index, 1);
}

void CustomHomePagesTableModel::Remove(int index) {
  DCHECK(index >= 0 && index < RowCount());
  Entry* entry = &(entries_[index]);
  // Cancel any pending load requests now so we don't deref a bogus pointer when
  // we get the loaded notification.
  if (entry->title_handle) {
    HistoryService* history_service =
        profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
    if (history_service)
      history_service->CancelRequest(entry->title_handle);
  }
  if (entry->favicon_handle) {
    FaviconService* favicon_service =
        profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
    if (favicon_service)
      favicon_service->CancelRequest(entry->favicon_handle);
  }
  entries_.erase(entries_.begin() + static_cast<size_t>(index));
  if (observer_)
    observer_->OnItemsRemoved(index, 1);
}

void CustomHomePagesTableModel::SetToCurrentlyOpenPages() {
  // Remove the current entries.
  while (RowCount())
    Remove(0);

  // And add all tabs for all open browsers with our profile.
  int add_index = 0;
  for (BrowserList::const_iterator browser_i = BrowserList::begin();
       browser_i != BrowserList::end(); ++browser_i) {
    Browser* browser = *browser_i;
    if (browser->profile() != profile_)
      continue;  // Skip incognito browsers.

    for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
      const GURL url = browser->GetTabContentsAt(tab_index)->GetURL();
      if (!url.is_empty() &&
          !(url.SchemeIs(chrome::kChromeUIScheme) &&
            url.host() == chrome::kChromeUISettingsHost))
        Add(add_index++, url);
    }
  }
}

std::vector<GURL> CustomHomePagesTableModel::GetURLs() {
  std::vector<GURL> urls(entries_.size());
  for (size_t i = 0; i < entries_.size(); ++i)
    urls[i] = entries_[i].url;
  return urls;
}

int CustomHomePagesTableModel::RowCount() {
  return static_cast<int>(entries_.size());
}

string16 CustomHomePagesTableModel::GetText(int row, int column_id) {
  DCHECK(column_id == 0);
  DCHECK(row >= 0 && row < RowCount());
  return entries_[row].title.empty() ? FormattedURL(row) : entries_[row].title;
}

SkBitmap CustomHomePagesTableModel::GetIcon(int row) {
  DCHECK(row >= 0 && row < RowCount());
  return entries_[row].icon.isNull() ? *default_favicon_ : entries_[row].icon;
}

string16 CustomHomePagesTableModel::GetTooltip(int row) {
  return entries_[row].title.empty() ? string16() :
      l10n_util::GetStringFUTF16(IDS_OPTIONS_STARTUP_PAGE_TOOLTIP,
                                 entries_[row].title, FormattedURL(row));
}

void CustomHomePagesTableModel::SetObserver(ui::TableModelObserver* observer) {
  observer_ = observer;
}

void CustomHomePagesTableModel::LoadTitleAndFavicon(Entry* entry) {
  HistoryService* history_service =
      profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (history_service) {
    entry->title_handle = history_service->QueryURL(entry->url, false,
        &query_consumer_,
        NewCallback(this, &CustomHomePagesTableModel::OnGotTitle));
  }
  FaviconService* favicon_service =
      profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
  if (favicon_service) {
    entry->favicon_handle = favicon_service->GetFaviconForURL(entry->url,
        history::FAVICON, &query_consumer_,
        NewCallback(this, &CustomHomePagesTableModel::OnGotFavicon));
  }
}

void CustomHomePagesTableModel::OnGotTitle(HistoryService::Handle handle,
                                           bool found_url,
                                           const history::URLRow* row,
                                           history::VisitVector* visits) {
  int entry_index;
  Entry* entry =
      GetEntryByLoadHandle(&Entry::title_handle, handle, &entry_index);
  if (!entry) {
    // The URLs changed before we were called back.
    return;
  }
  entry->title_handle = 0;
  if (found_url && !row->title().empty()) {
    entry->title = row->title();
    if (observer_)
      observer_->OnItemsChanged(static_cast<int>(entry_index), 1);
  }
}

void CustomHomePagesTableModel::OnGotFavicon(
    FaviconService::Handle handle,
    history::FaviconData favicon) {
  int entry_index;
  Entry* entry =
      GetEntryByLoadHandle(&Entry::favicon_handle, handle, &entry_index);
  if (!entry) {
    // The URLs changed before we were called back.
    return;
  }
  entry->favicon_handle = 0;
  if (favicon.is_valid()) {
    int width, height;
    std::vector<unsigned char> decoded_data;
    if (gfx::PNGCodec::Decode(favicon.image_data->front(),
                              favicon.image_data->size(),
                              gfx::PNGCodec::FORMAT_BGRA, &decoded_data,
                              &width, &height)) {
      entry->icon.setConfig(SkBitmap::kARGB_8888_Config, width, height);
      entry->icon.allocPixels();
      memcpy(entry->icon.getPixels(), &decoded_data.front(),
             width * height * 4);
      if (observer_)
        observer_->OnItemsChanged(static_cast<int>(entry_index), 1);
    }
  }
}

CustomHomePagesTableModel::Entry*
    CustomHomePagesTableModel::GetEntryByLoadHandle(
    CancelableRequestProvider::Handle Entry::* member,
    CancelableRequestProvider::Handle handle,
    int* index) {
  for (size_t i = 0; i < entries_.size(); ++i) {
    if (entries_[i].*member == handle) {
      *index = static_cast<int>(i);
      return &entries_[i];
    }
  }
  return NULL;
}

string16 CustomHomePagesTableModel::FormattedURL(int row) const {
  std::string languages =
      profile_->GetPrefs()->GetString(prefs::kAcceptLanguages);
  string16 url = net::FormatUrl(entries_[row].url, languages);
  url = base::i18n::GetDisplayStringInLTRDirectionality(url);
  return url;
}