// Copyright (c) 2011 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/tabs/tab_strip_selection_model.h"

#include <algorithm>
#include <valarray>

#include "base/logging.h"

// static
const int TabStripSelectionModel::kUnselectedIndex = -1;

static void IncrementFromImpl(int index, int* value) {
  if (*value >= index)
    (*value)++;
}

static bool DecrementFromImpl(int index, int* value) {
  if (*value == index) {
    *value = TabStripSelectionModel::kUnselectedIndex;
    return true;
  }
  if (*value > index)
    (*value)--;
  return false;
}

TabStripSelectionModel::TabStripSelectionModel()
    : active_(kUnselectedIndex),
      anchor_(kUnselectedIndex) {
}

TabStripSelectionModel::~TabStripSelectionModel() {
}

void TabStripSelectionModel::IncrementFrom(int index) {
  // Shift the selection to account for the newly inserted tab.
  for (SelectedIndices::iterator i = selected_indices_.begin();
       i != selected_indices_.end(); ++i) {
    IncrementFromImpl(index, &(*i));
  }
  IncrementFromImpl(index, &anchor_);
  IncrementFromImpl(index, &active_);
}

void TabStripSelectionModel::DecrementFrom(int index) {
  for (SelectedIndices::iterator i = selected_indices_.begin();
       i != selected_indices_.end(); ) {
    if (DecrementFromImpl(index, &(*i)))
      i = selected_indices_.erase(i);
    else
      ++i;
  }
  DecrementFromImpl(index, &anchor_);
  DecrementFromImpl(index, &active_);
}

void TabStripSelectionModel::SetSelectedIndex(int index) {
  anchor_ = active_ = index;
  SetSelectionFromAnchorTo(index);
}

bool TabStripSelectionModel::IsSelected(int index) const {
  return std::find(selected_indices_.begin(), selected_indices_.end(), index) !=
      selected_indices_.end();
}

void TabStripSelectionModel::AddIndexToSelection(int index) {
  if (!IsSelected(index)) {
    selected_indices_.push_back(index);
    std::sort(selected_indices_.begin(), selected_indices_.end());
  }
}

void TabStripSelectionModel::RemoveIndexFromSelection(int index) {
  SelectedIndices::iterator i = std::find(selected_indices_.begin(),
                                          selected_indices_.end(), index);
  if (i != selected_indices_.end())
    selected_indices_.erase(i);
}

void TabStripSelectionModel::SetSelectionFromAnchorTo(int index) {
  if (anchor_ == kUnselectedIndex) {
    SetSelectedIndex(index);
  } else {
    int delta = std::abs(index - anchor_);
    SelectedIndices new_selection(delta + 1, 0);
    for (int i = 0, min = std::min(index, anchor_); i <= delta; ++i)
      new_selection[i] = i + min;
    selected_indices_.swap(new_selection);
    active_ = index;
  }
}

void TabStripSelectionModel::AddSelectionFromAnchorTo(int index) {
  if (anchor_ == kUnselectedIndex) {
    SetSelectedIndex(index);
  } else {
    for (int i = std::min(index, anchor_), end = std::max(index, anchor_);
         i <= end; ++i) {
      if (!IsSelected(i))
        selected_indices_.push_back(i);
    }
    std::sort(selected_indices_.begin(), selected_indices_.end());
    active_ = index;
  }
}

void TabStripSelectionModel::Move(int from, int to) {
  DCHECK_NE(to, from);
  bool was_anchor = from == anchor_;
  bool was_active = from == active_;
  bool was_selected = IsSelected(from);
  if (to < from) {
    IncrementFrom(to);
    DecrementFrom(from + 1);
  } else {
    DecrementFrom(from);
    IncrementFrom(to);
  }
  if (was_active)
    active_ = to;
  if (was_anchor)
    anchor_ = to;
  if (was_selected)
    AddIndexToSelection(to);
}

void TabStripSelectionModel::Clear() {
  anchor_ = active_ = kUnselectedIndex;
  SelectedIndices empty_selection;
  selected_indices_.swap(empty_selection);
}

void TabStripSelectionModel::Copy(const TabStripSelectionModel& source) {
  selected_indices_ = source.selected_indices_;
  active_ = source.active_;
  anchor_ = source.anchor_;
}