// 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/base_history_model.h"

#include "base/gfx/png_decoder.h"
#include "chrome/app/theme/theme_resources.h"
#include "chrome/browser/profile.h"
#include "chrome/common/jpeg_codec.h"
#include "chrome/common/resource_bundle.h"
#include "SkBitmap.h"

// Size of the favicon and thumbnail caches.
static const int kThumbnailCacheSize = 100;

// Icon to display when one isn't found for the page.
static SkBitmap* kDefaultFavicon = NULL;

// static
const int BaseHistoryModel::kHistoryScopeMonths = 18;

BaseHistoryModel::BaseHistoryModel(Profile* profile)
    : profile_(profile),
      observer_(NULL),
      thumbnails_(kThumbnailCacheSize),
      favicons_(kThumbnailCacheSize),
      is_search_results_(false) {
  if (!kDefaultFavicon) {
    kDefaultFavicon = ResourceBundle::GetSharedInstance().
        GetBitmapNamed(IDR_DEFAULT_FAVICON);
  }
}

BaseHistoryModel::~BaseHistoryModel() {
}

void BaseHistoryModel::SetObserver(BaseHistoryModelObserver* observer) {
  observer_ = observer;
}

BaseHistoryModelObserver* BaseHistoryModel::GetObserver() const {
  return observer_;
}

SkBitmap* BaseHistoryModel::GetThumbnail(int index) {
  return GetImage(THUMBNAIL, index);
}

SkBitmap* BaseHistoryModel::GetFavicon(int index) {
  SkBitmap* favicon = GetImage(FAVICON, index);
  return favicon ? favicon : kDefaultFavicon;
}

void BaseHistoryModel::AboutToScheduleRequest() {
  if (observer_ && cancelable_consumer_.PendingRequestCount() == 0)
    observer_->ModelBeginWork();
}

void BaseHistoryModel::RequestCompleted() {
  if (observer_ && cancelable_consumer_.PendingRequestCount() == 1)
    observer_->ModelEndWork();
}

SkBitmap* BaseHistoryModel::GetImage(ImageType image_type, int index) {
#ifndef NDEBUG
  DCHECK(IsValidIndex(index));
#endif
  history::URLID id = GetURLID(index);
  DCHECK(id);

  CacheType* cache = (image_type == THUMBNAIL) ? &thumbnails_ : &favicons_;
  CacheType::iterator iter = cache->Get(id);
  if (iter != cache->end()) {
    // Empty bitmaps indicate one that is pending a load. We still return NULL
    // in this case.
    return iter->second.getSize() ? &iter->second : NULL;
  }

  HistoryService* history_service =
      profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (!history_service)
    return NULL;

  // The entry isn't cached, ask the history service for it.

  // Add an entry to the cache. This ensures we don't request the same page
  // more than once.
  cache->Put(id, SkBitmap());

  // If this is the first request, we need to notify our delegate we're
  // beginning work.
  AboutToScheduleRequest();

  // Start the request & associate the page ID with this thumbnail request.
  HistoryService::Handle request;
  if (image_type == THUMBNAIL) {
    request = history_service->GetPageThumbnail(GetURL(index),
        &cancelable_consumer_,
        NewCallback(this, &BaseHistoryModel::OnThumbnailDataAvailable));
  } else {
    request = history_service->GetFavIconForURL(GetURL(index),
        &cancelable_consumer_,
        NewCallback(this, &BaseHistoryModel::OnFaviconDataAvailable));
  }
  cancelable_consumer_.SetClientData(history_service, request, id);
  return NULL;
}

void BaseHistoryModel::OnThumbnailDataAvailable(
    HistoryService::Handle request_handle,
    scoped_refptr<RefCountedBytes> data) {
  RequestCompleted();

  if (!data || data->data.empty()) {  // This gets called on failure too.
    return;
  }

  // The client data for this request is the page ID for the thumbnail.
  HistoryService* history_service =
      profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  history::URLID page = cancelable_consumer_.GetClientData(history_service,
                                                            request_handle);
  DCHECK(page > 0) << "Page ID is not found, maybe we forgot to set it";

  scoped_ptr<SkBitmap> bitmap(JPEGCodec::Decode(
      &data->data.front(), data->data.size()));
  if (!bitmap.get())
    return;

  thumbnails_.Put(page, *bitmap);

  if (observer_)
    observer_->ModelChanged(false);
}

void BaseHistoryModel::OnFaviconDataAvailable(
    HistoryService::Handle handle,
    bool know_favicon,
    scoped_refptr<RefCountedBytes> data,
    bool expired,
    GURL icon_url) {
  RequestCompleted();

  SkBitmap fav_icon;
  if (!know_favicon || !data.get() ||
      !PNGDecoder::Decode(&data->data, &fav_icon)) {
    return;
  }
  history::URLID page =
      cancelable_consumer_.GetClientData(
          profile_->GetHistoryService(Profile::EXPLICIT_ACCESS), handle);
  favicons_.Put(page, fav_icon);

  if (observer_)
    observer_->ModelChanged(false);
}