// Copyright 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 "cc/resources/layer_tiling_data.h"

#include <vector>

#include "base/logging.h"

namespace cc {

scoped_ptr<LayerTilingData> LayerTilingData::Create(gfx::Size tile_size,
                                                    BorderTexelOption border) {
  return make_scoped_ptr(new LayerTilingData(tile_size, border));
}

LayerTilingData::LayerTilingData(gfx::Size tile_size, BorderTexelOption border)
    : tiling_data_(tile_size, gfx::Size(), border == HAS_BORDER_TEXELS) {
  SetTileSize(tile_size);
}

LayerTilingData::~LayerTilingData() {}

void LayerTilingData::SetTileSize(gfx::Size size) {
  if (tile_size() == size)
    return;

  reset();

  tiling_data_.SetMaxTextureSize(size);
}

gfx::Size LayerTilingData::tile_size() const {
  return tiling_data_.max_texture_size();
}

void LayerTilingData::SetBorderTexelOption(
    BorderTexelOption border_texel_option) {
  bool border_texels = border_texel_option == HAS_BORDER_TEXELS;
  if (has_border_texels() == border_texels)
    return;

  reset();
  tiling_data_.SetHasBorderTexels(border_texels);
}

const LayerTilingData& LayerTilingData::operator=
    (const LayerTilingData & tiler) {
  tiling_data_ = tiler.tiling_data_;

  return *this;
}

void LayerTilingData::AddTile(scoped_ptr<Tile> tile, int i, int j) {
  DCHECK(!TileAt(i, j));
  tile->move_to(i, j);
  tiles_.add(std::make_pair(i, j), tile.Pass());
}

scoped_ptr<LayerTilingData::Tile> LayerTilingData::TakeTile(int i, int j) {
  return tiles_.take_and_erase(std::make_pair(i, j));
}

LayerTilingData::Tile* LayerTilingData::TileAt(int i, int j) const {
  return tiles_.get(std::make_pair(i, j));
}

void LayerTilingData::ContentRectToTileIndices(gfx::Rect content_rect,
                                               int* left,
                                               int* top,
                                               int* right,
                                               int* bottom) const {
  // An empty rect doesn't result in an empty set of tiles, so don't pass an
  // empty rect.
  // TODO(enne): Possibly we should fill a vector of tiles instead, since the
  // normal use of this function is to enumerate some tiles.
  DCHECK(!content_rect.IsEmpty());

  *left = tiling_data_.TileXIndexFromSrcCoord(content_rect.x());
  *top = tiling_data_.TileYIndexFromSrcCoord(content_rect.y());
  *right = tiling_data_.TileXIndexFromSrcCoord(content_rect.right() - 1);
  *bottom = tiling_data_.TileYIndexFromSrcCoord(content_rect.bottom() - 1);
}

gfx::Rect LayerTilingData::TileRect(const Tile* tile) const {
  gfx::Rect tile_rect = tiling_data_.TileBoundsWithBorder(tile->i(), tile->j());
  tile_rect.set_size(tile_size());
  return tile_rect;
}

Region LayerTilingData::OpaqueRegionInContentRect(
    gfx::Rect content_rect) const {
  if (content_rect.IsEmpty())
    return Region();

  Region opaque_region;
  int left, top, right, bottom;
  ContentRectToTileIndices(content_rect, &left, &top, &right, &bottom);
  for (int j = top; j <= bottom; ++j) {
    for (int i = left; i <= right; ++i) {
      Tile* tile = TileAt(i, j);
      if (!tile)
        continue;

      gfx::Rect tile_opaque_rect =
          gfx::IntersectRects(content_rect, tile->opaque_rect());
      opaque_region.Union(tile_opaque_rect);
    }
  }
  return opaque_region;
}

void LayerTilingData::SetBounds(gfx::Size size) {
  tiling_data_.SetTotalSize(size);
  if (size.IsEmpty()) {
    tiles_.clear();
    return;
  }

  // Any tiles completely outside our new bounds are invalid and should be
  // dropped.
  int left, top, right, bottom;
  ContentRectToTileIndices(
      gfx::Rect(size), &left, &top, &right, &bottom);
  std::vector<TileMapKey> invalid_tile_keys;
  for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
    if (it->first.first > right || it->first.second > bottom)
      invalid_tile_keys.push_back(it->first);
  }
  for (size_t i = 0; i < invalid_tile_keys.size(); ++i)
    tiles_.erase(invalid_tile_keys[i]);
}

}  // namespace cc