// 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 "ui/views/controls/table/table_header.h"

#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/canvas.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/background.h"
#include "ui/views/controls/table/table_utils.h"
#include "ui/views/controls/table/table_view.h"

#if defined(USE_AURA)
#include "ui/base/cursor/cursor.h"
#endif

namespace views {

namespace {

const int kVerticalPadding = 4;

// The minimum width we allow a column to go down to.
const int kMinColumnWidth = 10;

// Distace from edge columns can be resized by.
const int kResizePadding = 5;

// Amount of space above/below the separator.
const int kSeparatorPadding = 4;

const SkColor kTextColor = SK_ColorBLACK;
const SkColor kBackgroundColor1 = SkColorSetRGB(0xF9, 0xF9, 0xF9);
const SkColor kBackgroundColor2 = SkColorSetRGB(0xE8, 0xE8, 0xE8);
const SkColor kSeparatorColor = SkColorSetRGB(0xAA, 0xAA, 0xAA);

// Size of the sort indicator (doesn't include padding).
const int kSortIndicatorSize = 8;

gfx::NativeCursor GetResizeCursor() {
#if defined(USE_AURA)
  return ui::kCursorColumnResize;
#elif defined(OS_WIN)
  static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_SIZEWE);
  return g_hand_cursor;
#endif
}

}  // namespace

// static
const int TableHeader::kHorizontalPadding = 7;
// static
const int TableHeader::kSortIndicatorWidth = kSortIndicatorSize +
    TableHeader::kHorizontalPadding * 2;

typedef std::vector<TableView::VisibleColumn> Columns;

TableHeader::TableHeader(TableView* table) : table_(table) {
  set_background(Background::CreateVerticalGradientBackground(
                     kBackgroundColor1, kBackgroundColor2));
}

TableHeader::~TableHeader() {
}

void TableHeader::Layout() {
  SetBounds(x(), y(), table_->width(), GetPreferredSize().height());
}

void TableHeader::OnPaint(gfx::Canvas* canvas) {
  // Paint the background and a separator at the bottom. The separator color
  // matches that of the border around the scrollview.
  OnPaintBackground(canvas);
  SkColor border_color = GetNativeTheme()->GetSystemColor(
      ui::NativeTheme::kColorId_UnfocusedBorderColor);
  canvas->DrawLine(gfx::Point(0, height() - 1),
                   gfx::Point(width(), height() - 1), border_color);

  const Columns& columns = table_->visible_columns();
  const int sorted_column_id = table_->sort_descriptors().empty() ? -1 :
      table_->sort_descriptors()[0].column_id;
  for (size_t i = 0; i < columns.size(); ++i) {
    if (columns[i].width >= 2) {
      const int separator_x = GetMirroredXInView(
          columns[i].x + columns[i].width - 1);
      canvas->DrawLine(gfx::Point(separator_x, kSeparatorPadding),
                       gfx::Point(separator_x, height() - kSeparatorPadding),
                       kSeparatorColor);
    }

    const int x = columns[i].x + kHorizontalPadding;
    int width = columns[i].width - kHorizontalPadding - kHorizontalPadding;
    if (width <= 0)
      continue;

    const int title_width = font_.GetStringWidth(columns[i].column.title);
    const bool paint_sort_indicator =
        (columns[i].column.id == sorted_column_id &&
         title_width + kSortIndicatorWidth <= width);

    if (paint_sort_indicator &&
        columns[i].column.alignment == ui::TableColumn::RIGHT) {
      width -= kSortIndicatorWidth;
    }

    canvas->DrawStringInt(
        columns[i].column.title, font_, kTextColor,
        GetMirroredXWithWidthInView(x, width), kVerticalPadding, width,
        height() - kVerticalPadding * 2,
        TableColumnAlignmentToCanvasAlignment(columns[i].column.alignment));

    if (paint_sort_indicator) {
      SkPaint paint;
      paint.setColor(kTextColor);
      paint.setStyle(SkPaint::kFill_Style);
      paint.setAntiAlias(true);

      int indicator_x = 0;
      ui::TableColumn::Alignment alignment = columns[i].column.alignment;
      if (base::i18n::IsRTL()) {
        if (alignment == ui::TableColumn::LEFT)
          alignment = ui::TableColumn::RIGHT;
        else if (alignment == ui::TableColumn::RIGHT)
          alignment = ui::TableColumn::LEFT;
      }
      switch (alignment) {
        case ui::TableColumn::LEFT:
          indicator_x = x + title_width;
          break;
        case ui::TableColumn::CENTER:
          indicator_x = x + width / 2;
          break;
        case ui::TableColumn::RIGHT:
          indicator_x = x + width;
          break;
      }

      const int scale = base::i18n::IsRTL() ? -1 : 1;
      indicator_x += (kSortIndicatorWidth - kSortIndicatorSize) / 2;
      indicator_x = GetMirroredXInView(indicator_x);
      int indicator_y = height() / 2 - kSortIndicatorSize / 2;
      SkPath indicator_path;
      if (table_->sort_descriptors()[0].ascending) {
        indicator_path.moveTo(
            SkIntToScalar(indicator_x),
            SkIntToScalar(indicator_y + kSortIndicatorSize));
        indicator_path.lineTo(
            SkIntToScalar(indicator_x + kSortIndicatorSize * scale),
            SkIntToScalar(indicator_y + kSortIndicatorSize));
        indicator_path.lineTo(
            SkIntToScalar(indicator_x + kSortIndicatorSize / 2 * scale),
            SkIntToScalar(indicator_y));
      } else {
        indicator_path.moveTo(SkIntToScalar(indicator_x),
                              SkIntToScalar(indicator_y));
        indicator_path.lineTo(
            SkIntToScalar(indicator_x + kSortIndicatorSize * scale),
            SkIntToScalar(indicator_y));
        indicator_path.lineTo(
            SkIntToScalar(indicator_x + kSortIndicatorSize / 2 * scale),
            SkIntToScalar(indicator_y + kSortIndicatorSize));
      }
      indicator_path.close();
      canvas->DrawPath(indicator_path, paint);
    }
  }
}

gfx::Size TableHeader::GetPreferredSize() {
  return gfx::Size(1, kVerticalPadding * 2 + font_.GetHeight());
}

gfx::NativeCursor TableHeader::GetCursor(const ui::MouseEvent& event) {
  return GetResizeColumn(GetMirroredXInView(event.x())) != -1 ?
      GetResizeCursor() : View::GetCursor(event);
}

bool TableHeader::OnMousePressed(const ui::MouseEvent& event) {
  if (event.IsOnlyLeftMouseButton()) {
    const int index = GetResizeColumn(GetMirroredXInView(event.x()));
    if (index != -1) {
      DCHECK(!is_resizing());
      resize_details_.reset(new ColumnResizeDetails);
      resize_details_->column_index = index;
      resize_details_->initial_x = event.root_location().x();
      resize_details_->initial_width =
          table_->visible_columns()[index].width;
    }
    return true;
  }
  // Return false so that context menus on ancestors work.
  return false;
}

bool TableHeader::OnMouseDragged(const ui::MouseEvent& event) {
  if (is_resizing()) {
    const int scale = base::i18n::IsRTL() ? -1 : 1;
    const int delta = scale *
        (event.root_location().x() - resize_details_->initial_x);
    table_->SetVisibleColumnWidth(
        resize_details_->column_index,
        std::max(kMinColumnWidth, resize_details_->initial_width + delta));
  }
  return true;
}

void TableHeader::OnMouseReleased(const ui::MouseEvent& event) {
  const bool was_resizing = resize_details_ != NULL;
  resize_details_.reset();
  if (!was_resizing && event.IsOnlyLeftMouseButton() &&
      !table_->visible_columns().empty()) {
    const int x = GetMirroredXInView(event.x());
    const int index = GetClosestVisibleColumnIndex(table_, x);
    const TableView::VisibleColumn& column(table_->visible_columns()[index]);
    if (x >= column.x && x < column.x + column.width && event.y() >= 0 &&
        event.y() < height())
      table_->ToggleSortOrder(index);
  }
}

void TableHeader::OnMouseCaptureLost() {
  if (is_resizing()) {
    table_->SetVisibleColumnWidth(resize_details_->column_index,
                                  resize_details_->initial_width);
  }
  resize_details_.reset();
}

int TableHeader::GetResizeColumn(int x) const {
  const Columns& columns(table_->visible_columns());
  if (columns.empty())
    return -1;

  const int index = GetClosestVisibleColumnIndex(table_, x);
  DCHECK_NE(-1, index);
  const TableView::VisibleColumn& column(table_->visible_columns()[index]);
  if (index > 0 && x >= column.x - kResizePadding &&
      x <= column.x + kResizePadding) {
    return index - 1;
  }
  const int max_x = column.x + column.width;
  return (x >= max_x - kResizePadding && x <= max_x + kResizePadding) ?
      index : -1;
}

}  // namespace views