// Copyright (c) 2012 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 "components/history/core/browser/android/visit_sql_handler.h"

#include <stdint.h>

#include "base/logging.h"
#include "base/stl_util.h"
#include "components/history/core/browser/url_database.h"
#include "components/history/core/browser/visit_database.h"

using base::Time;

namespace history {

namespace {

// The interesting columns of this handler.
const HistoryAndBookmarkRow::ColumnID kInterestingColumns[] = {
    HistoryAndBookmarkRow::CREATED, HistoryAndBookmarkRow::VISIT_COUNT,
    HistoryAndBookmarkRow::LAST_VISIT_TIME };

} // namespace

VisitSQLHandler::VisitSQLHandler(URLDatabase* url_db, VisitDatabase* visit_db)
    : SQLHandler(kInterestingColumns, base::size(kInterestingColumns)),
      url_db_(url_db),
      visit_db_(visit_db) {}

VisitSQLHandler::~VisitSQLHandler() {
}

// The created time is updated according the given |row|.
// We simulate updating created time by
// a. Remove all visits.
// b. Insert a new visit which has visit time same as created time.
// c. Insert the number of visits according the visit count in urls table.
//
// Visit row is insertted/removed to keep consistent with urls table.
// a. If the visit count in urls table is less than the visit rows in visit
//    table, all existent visits will be removed. The new visits will be
//    insertted according the value in urls table.
// b. Otherwise, only add the increased number of visit count.
bool VisitSQLHandler::Update(const HistoryAndBookmarkRow& row,
                             const TableIDRows& ids_set) {
  for (TableIDRows::const_iterator id = ids_set.begin();
       id != ids_set.end(); ++id) {
    VisitVector visits;
    if (!visit_db_->GetVisitsForURL(id->url_id, &visits))
      return false;
    int visit_count_in_table = visits.size();
    URLRow url_row;
    if (!url_db_->GetURLRow(id->url_id, &url_row))
      return false;
    int visit_count_needed = url_row.visit_count();

    if (visit_count_needed == 0)
      return Delete(ids_set);

    // If created time is updated or new visit count is less than the current
    // one, delete all visit rows.
    if (row.is_value_set_explicitly(HistoryAndBookmarkRow::CREATED) ||
        visit_count_in_table > visit_count_needed) {
      if (!DeleteVisitsForURL(id->url_id))
        return false;
      visit_count_in_table = 0;
    }

    if (row.is_value_set_explicitly(HistoryAndBookmarkRow::CREATED) &&
        visit_count_needed > 0) {
      if (!AddVisit(id->url_id, row.created()))
        return false;
      visit_count_in_table++;
    }

    if (!AddVisitRows(id->url_id, visit_count_needed - visit_count_in_table,
                       url_row.last_visit()))
      return false;
  }
  return true;
}

bool VisitSQLHandler::Insert(HistoryAndBookmarkRow* row) {
  DCHECK(row->is_value_set_explicitly(HistoryAndBookmarkRow::URL_ID));

  URLRow url_row;
  if (!url_db_->GetURLRow(row->url_id(), &url_row))
    return false;

  int visit_count = url_row.visit_count();

  if (visit_count == 0)
    return true;

  // Add a row if the last visit time is different from created time.
  if (row->is_value_set_explicitly(HistoryAndBookmarkRow::CREATED) &&
      row->created() != url_row.last_visit() && visit_count > 0) {
    if (!AddVisit(row->url_id(), row->created()))
      return false;
    visit_count--;
  }

  if (!AddVisitRows(row->url_id(), visit_count, url_row.last_visit()))
    return false;

  return true;
}

bool VisitSQLHandler::Delete(const TableIDRows& ids_set) {
  for (TableIDRows::const_iterator ids = ids_set.begin();
       ids != ids_set.end(); ++ids) {
    DeleteVisitsForURL(ids->url_id);
  }
  return true;
}

bool VisitSQLHandler::AddVisit(URLID url_id, const Time& visit_time) {
  // TODO : Is 'ui::PAGE_TRANSITION_AUTO_BOOKMARK' proper?
  // if not, a new ui::PageTransition type will need.
  VisitRow visit_row(url_id, visit_time, 0,
                     ui::PAGE_TRANSITION_AUTO_BOOKMARK, 0, false);
  return visit_db_->AddVisit(&visit_row, SOURCE_BROWSED);
}

bool VisitSQLHandler::AddVisitRows(URLID url_id,
                                   int visit_count,
                                   const Time& last_visit_time) {
  int64_t last_update_value = last_visit_time.ToInternalValue();
  for (int i = 0; i < visit_count; i++) {
    if (!AddVisit(url_id, Time::FromInternalValue(last_update_value - i)))
      return false;
  }
  return true;
}

bool VisitSQLHandler::DeleteVisitsForURL(URLID url_id) {
  VisitVector visits;
  if (!visit_db_->GetVisitsForURL(url_id, &visits))
    return false;

  for (VisitVector::const_iterator v = visits.begin(); v != visits.end(); ++v) {
    visit_db_->DeleteVisit(*v);
  }
  return true;
}

}  // namespace history.
