// Copyright (c) 2010 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/download/download_history.h"

#include "base/logging.h"
#include "chrome/browser/history/download_create_info.h"
#include "chrome/browser/history/history_marshaling.h"
#include "chrome/browser/download/download_item.h"
#include "chrome/browser/profiles/profile.h"

// Our download table ID starts at 1, so we use 0 to represent a download that
// has started, but has not yet had its data persisted in the table. We use fake
// database handles in incognito mode starting at -1 and progressively getting
// more negative.
// static
const int DownloadHistory::kUninitializedHandle = 0;

DownloadHistory::DownloadHistory(Profile* profile)
    : profile_(profile),
      next_fake_db_handle_(kUninitializedHandle - 1) {
  DCHECK(profile);
}

DownloadHistory::~DownloadHistory() {
}

void DownloadHistory::Load(HistoryService::DownloadQueryCallback* callback) {
  DCHECK(callback);
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (!hs) {
    delete callback;
    return;
  }
  hs->QueryDownloads(&history_consumer_, callback);

  // This is the initial load, so do a cleanup of corrupt in-progress entries.
  hs->CleanUpInProgressEntries();
}

void DownloadHistory::AddEntry(
    const DownloadCreateInfo& info,
    DownloadItem* download_item,
    HistoryService::DownloadCreateCallback* callback) {
  // Do not store the download in the history database for a few special cases:
  // - incognito mode (that is the point of this mode)
  // - extensions (users don't think of extension installation as 'downloading')
  // - temporary download, like in drag-and-drop
  // - history service is not available (e.g. in tests)
  // We have to make sure that these handles don't collide with normal db
  // handles, so we use a negative value. Eventually, they could overlap, but
  // you'd have to do enough downloading that your ISP would likely stab you in
  // the neck first. YMMV.
  // TODO(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (download_item->is_otr() || download_item->is_extension_install() ||
      download_item->is_temporary() || !hs) {
    callback->RunWithParams(
        history::DownloadCreateRequest::TupleType(info, GetNextFakeDbHandle()));
    delete callback;
    return;
  }

  hs->CreateDownload(info, &history_consumer_, callback);
}

void DownloadHistory::UpdateEntry(DownloadItem* download_item) {
  // Don't store info in the database if the download was initiated while in
  // incognito mode or if it hasn't been initialized in our database table.
  if (download_item->db_handle() <= kUninitializedHandle)
    return;

  // TODO(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (!hs)
    return;

  hs->UpdateDownload(download_item->received_bytes(),
                     download_item->state(),
                     download_item->db_handle());
}

void DownloadHistory::UpdateDownloadPath(DownloadItem* download_item,
                                         const FilePath& new_path) {
  // No update necessary if the download was initiated while in incognito mode.
  if (download_item->db_handle() <= kUninitializedHandle)
    return;

  // TODO(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (hs)
    hs->UpdateDownloadPath(new_path, download_item->db_handle());
}

void DownloadHistory::RemoveEntry(DownloadItem* download_item) {
  // No update necessary if the download was initiated while in incognito mode.
  if (download_item->db_handle() <= kUninitializedHandle)
    return;

  // TODO(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (hs)
    hs->RemoveDownload(download_item->db_handle());
}

void DownloadHistory::RemoveEntriesBetween(const base::Time remove_begin,
                                           const base::Time remove_end) {
  // TODO(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (hs)
    hs->RemoveDownloadsBetween(remove_begin, remove_end);
}

int64 DownloadHistory::GetNextFakeDbHandle() {
  return next_fake_db_handle_--;
}