// Copyright 2014 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 "mojo/services/view_manager/server_view.h"

#include "mojo/services/view_manager/server_view_delegate.h"

namespace mojo {
namespace service {

ServerView::ServerView(ServerViewDelegate* delegate, const ViewId& id)
    : delegate_(delegate), id_(id), parent_(NULL), visible_(true) {
  DCHECK(delegate);  // Must provide a delegate.
}

ServerView::~ServerView() {
  while (!children_.empty())
    children_.front()->parent()->Remove(children_.front());

  if (parent_)
    parent_->Remove(this);

  delegate_->OnViewDestroyed(this);
}

void ServerView::Add(ServerView* child) {
  // We assume validation checks happened already.
  DCHECK(child);
  DCHECK(child != this);
  DCHECK(!child->Contains(this));
  if (child->parent() == this) {
    if (children_.size() == 1)
      return;  // Already in the right position.
    Reorder(child, children_.back(), ORDER_DIRECTION_ABOVE);
    return;
  }

  const ServerView* old_parent = child->parent();
  child->delegate_->OnWillChangeViewHierarchy(child, this, old_parent);
  if (child->parent())
    child->parent()->RemoveImpl(child);

  child->parent_ = this;
  children_.push_back(child);
  child->delegate_->OnViewHierarchyChanged(child, this, old_parent);
}

void ServerView::Remove(ServerView* child) {
  // We assume validation checks happened else where.
  DCHECK(child);
  DCHECK(child != this);
  DCHECK(child->parent() == this);

  child->delegate_->OnWillChangeViewHierarchy(child, NULL, this);
  RemoveImpl(child);
  child->delegate_->OnViewHierarchyChanged(child, NULL, this);
}

void ServerView::Reorder(ServerView* child,
                         ServerView* relative,
                         OrderDirection direction) {
  // We assume validation checks happened else where.
  DCHECK(child);
  DCHECK(child->parent() == this);
  DCHECK_GT(children_.size(), 1u);
  children_.erase(std::find(children_.begin(), children_.end(), child));
  Views::iterator i = std::find(children_.begin(), children_.end(), relative);
  if (direction == ORDER_DIRECTION_ABOVE) {
    DCHECK(i != children_.end());
    children_.insert(++i, child);
  } else if (direction == ORDER_DIRECTION_BELOW) {
    DCHECK(i != children_.end());
    children_.insert(i, child);
  }
}

void ServerView::SetBounds(const gfx::Rect& bounds) {
  if (bounds_ == bounds)
    return;

  const gfx::Rect old_bounds = bounds_;
  bounds_ = bounds;
  delegate_->OnViewBoundsChanged(this, old_bounds, bounds);
}

const ServerView* ServerView::GetRoot() const {
  const ServerView* view = this;
  while (view && view->parent())
    view = view->parent();
  return view;
}

std::vector<const ServerView*> ServerView::GetChildren() const {
  std::vector<const ServerView*> children;
  children.reserve(children_.size());
  for (size_t i = 0; i < children_.size(); ++i)
    children.push_back(children_[i]);
  return children;
}

std::vector<ServerView*> ServerView::GetChildren() {
  // TODO(sky): rename to children() and fix return type.
  return children_;
}

bool ServerView::Contains(const ServerView* view) const {
  for (const ServerView* parent = view; parent; parent = parent->parent_) {
    if (parent == this)
      return true;
  }
  return false;
}

void ServerView::SetVisible(bool value) {
  if (visible_ == value)
    return;

  delegate_->OnWillChangeViewVisibility(this);
  visible_ = value;
}

bool ServerView::IsDrawn(const ServerView* root) const {
  if (!root->visible_)
    return false;
  const ServerView* view = this;
  while (view && view != root && view->visible_)
    view = view->parent_;
  return view == root;
}

void ServerView::SetSurfaceId(cc::SurfaceId surface_id) {
  surface_id_ = surface_id;
  delegate_->OnViewSurfaceIdChanged(this);
}

void ServerView::RemoveImpl(ServerView* view) {
  view->parent_ = NULL;
  children_.erase(std::find(children_.begin(), children_.end(), view));
}

}  // namespace service
}  // namespace mojo