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

// This view displays a list of historical page visits.  It requires a
// BaseHistoryModel to provide the information that will be shown.

#ifndef CHROME_BROWSER_HISTORY_VIEW_H_
#define CHROME_BROWSER_HISTORY_VIEW_H_

#include <vector>

#include "chrome/browser/history_model.h"
#include "chrome/views/link.h"
#include "chrome/views/scroll_view.h"

class PageNavigator;
class HistoryItemRenderer;
class BaseHistoryModel;
class SearchableUIContainer;

class HistoryView : public views::View,
                    public BaseHistoryModelObserver,
                    public views::LinkController,
    views::VariableRowHeightScrollHelper::Controller {
 public:
  HistoryView(SearchableUIContainer* container,
              BaseHistoryModel* model,
              PageNavigator* navigator);
  virtual ~HistoryView();

  // Returns true if this view is currently visible to the user.
  bool IsVisible();

  // Overridden for layout purposes.
  virtual void DidChangeBounds(const gfx::Rect& previous,
                               const gfx::Rect& current);
  virtual void Layout();

  virtual bool GetFloatingViewIDForPoint(int x, int y, int* id);

  // Overriden for focus traversal.
  virtual bool EnumerateFloatingViews(
      views::View::FloatingViewPosition position,
      int starting_id, int* id);
  virtual views::View* ValidateFloatingViewForID(int id);

  // Render the visible area.
  virtual void Paint(ChromeCanvas* canvas);

  // BaseHistoryModelObserver implementation.
  void ModelChanged(bool result_set_changed);
  void ModelBeginWork();
  void ModelEndWork();

  // Returns the entry height, varies with font-height to prevent clipping.
  int GetEntryHeight();

  // Sets whether the delete controls are visible.
  void SetShowDeleteControls(bool show_delete_controls);

  // We expose the PageNavigator so history entries can cause navigations
  // directly.
  PageNavigator* navigator() const { return navigator_; }

  // Scrolling.
  virtual int GetPageScrollIncrement(views::ScrollView* scroll_view,
                                     bool is_horizontal, bool is_positive);
  virtual int GetLineScrollIncrement(views::ScrollView* scroll_view,
                                     bool is_horizontal, bool is_positive);
  virtual views::VariableRowHeightScrollHelper::RowInfo GetRowInfo(int y);

 private:
  // For any given break (see comments for BreakOffsets, below), we store the
  // index of the item following the break, and whether or not the break
  // corresponds to a day break or session break.
  struct BreakValue {
    int index;
    bool day;
  };

  // The map of our breaks (see comments for BreakOffsets, below).
  typedef std::map<int, BreakValue> BreakOffsets;

  // Ensures the renderers are valid.
  void EnsureRenderer();

  // Returns the bottom of the last entry.
  int GetLastEntryMaxY();

  // Returns the max view id.
  int GetMaxViewID();

  // Returns the y coordinate for the view with the specified floating view id,
  // the index into the model of the specified view and whether the view is
  // a delete control.
  int GetYCoordinateForViewID(int view_id,
                              int* model_index,
                              bool* is_delete_control);

  // Invoked when the user clicks the delete previous visits link.
  virtual void LinkActivated(views::Link* source, int event_flags);

  // Prompts the user to make sure they really want to delete, and if so
  // deletes the day at the specified model index.
  void DeleteDayAtModelIndex(int index);

  // Returns the number of delete controls shown before the specified iterator.
  int CalculateDeleteOffset(const BreakOffsets::const_iterator& it);

  // Returns the width of the delete control, calculating if necessary.
  int GetDeleteControlWidth();

  // Calculates the bounds for the delete control given the specified y
  // location.
  gfx::Rect CalculateDeleteControlBounds(int base_y);

  // The "searchable view" container for this view.
  SearchableUIContainer* container_;

  // The font used for the "n days" ago heading.
  ChromeFont day_break_font_;

  // A "stamper"-style renderer for only painting the things that are
  // in the current view.
  HistoryItemRenderer* renderer_;

  // Used to render 'delete' controls.
  scoped_ptr<views::Link> delete_renderer_;

  // Class that performs the navigation when the user clicks on a page.
  PageNavigator* navigator_;

  // Pointer to the model that provides the contents of this view.
  scoped_ptr<BaseHistoryModel> model_;

  // For laying out the potentially huge list of history entries, we
  // cache the offsets of session and day breaks.
  //
  // Each entry in BreakOffsets is a pair, where the key is the y
  // coordinate of a day heading and the value is a struct containing
  // both the index of the first history entry after that day
  // heading and a boolean value indicating whether the offset
  // represents a day or session break (these display differently).
  //
  // This lets us quickly compute, for a given y value, how to lay out
  // entries in the vicinity of that y value.
  //
  // Here's an example:
  //   4 days ago             <- key: the y coordinate of the top of this block
  //   +----------------
  //   | history item #7      <- index: 7, the index of this history item
  //   +----------------
  //   +----------------
  //   | history item #8
  //   +----------------
  //                          <- key: the y coordinate of this separator
  //   +----------------
  //   | history item #9      <- index: 9, the index of this history item
  //   +----------------
  //   +----------------
  //   | history item #10
  //   +----------------
  //   5 days ago             <- key: the y coordinate of this block
  //   +----------------
  //   | history item #11     <- index: 11
  //   +----------------

  // Each history item is represented as a floating view. In addition the
  // the delete controls are represented as floating views. A ramification
  // of this is that when the delete controls are displayed the view ids do
  // not necessarily directly correspond to a model index. The following
  // example shows the view ids and corresponding model index.
  //
  //              delete      <- view_id=10
  //   +----------------
  //   | history item #7      <- view_id=11, model_index=10
  //   +----------------
  //   +----------------
  //   | history item #8      <- view_id=12, model_index=11
  //   +----------------
  //              delete      <- view_id=13
  //   +----------------
  //   | history item #9      <- view_id=14, model_index=12
  //   +----------------
  //
  BreakOffsets break_offsets_;

  // Retrieve the nearest BreakOffsets less than or equal to the given y.
  // Another way of looking at this is that it fetches the BreakOffsets
  // entry that heads the section containing y.
  BreakOffsets::iterator GetBreakOffsetIteratorForY(int y);

  int GetBreakOffsetHeight(BreakValue value);

  views::VariableRowHeightScrollHelper scroll_helper_;

  // Whether we are showing search results.
  bool show_results_;

  // The loading state of the model.
  bool loading_;

  // Whether we're showing delete controls.
  bool show_delete_controls_;

  // How tall a single line of text is.
  int line_height_;

  // Width needed for the delete control. Calculated in GetDeleteControlWidth.
  int delete_control_width_;

  DISALLOW_EVIL_CONSTRUCTORS(HistoryView);
};

#endif  // CHROME_BROWSER_HISTORY_VIEW_H_