// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/modules/accessibility/ax_range.h"

#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object.h"
#include "third_party/blink/renderer/platform/wtf/text/strcat.h"

namespace blink {

AXRange::AXRange(const AXPosition& start, const AXPosition& end)
    : start_(), end_() {
  if (!start.IsValid() || !end.IsValid() || start > end)
    return;

  const Document* document = start.ContainerObject()->GetDocument();
  DCHECK(document);
  DCHECK(document->IsActive());
  DCHECK(!document->NeedsLayoutTreeUpdate());
  // We don't support ranges that span across documents.
  if (end.ContainerObject()->GetDocument() != document)
    return;

  start_ = start;
  end_ = end;

#if DCHECK_IS_ON()
  dom_tree_version_ = document->DomTreeVersion();
  style_version_ = document->StyleVersion();
#endif  // DCHECK_IS_ON()
}

AXObject* AXRange::CommonAncestorContainer() const {
  if (!IsValid())
    return nullptr;
  int start_index, end_index;
  return const_cast<AXObject*>(AXObject::LowestCommonAncestor(
      *start_.ContainerObject(), *end_.ContainerObject(), &start_index,
      &end_index));
}

bool AXRange::IsCollapsed() const {
  return IsValid() && start_ == end_;
}

bool AXRange::IsValid() const {
  if (!start_.IsValid() || !end_.IsValid())
    return false;

  // We don't support ranges that span across documents.
  if (start_.ContainerObject()->GetDocument() !=
      end_.ContainerObject()->GetDocument()) {
    return false;
  }

  DCHECK(!start_.ContainerObject()->GetDocument()->NeedsLayoutTreeUpdate());
#if DCHECK_IS_ON()
  DCHECK_EQ(start_.ContainerObject()->GetDocument()->DomTreeVersion(),
            dom_tree_version_);
  DCHECK_EQ(start_.ContainerObject()->GetDocument()->StyleVersion(),
            style_version_);
#endif  // DCHECK_IS_ON()
  return true;
}

// static
AXRange AXRange::RangeOfContents(const AXObject& container) {
  return AXRange(AXPosition::CreateFirstPositionInObject(container),
                 AXPosition::CreateLastPositionInObject(container));
}

String AXRange::ToString() const {
  if (!IsValid())
    return "Invalid AXRange";
  return StrCat(
      {"AXRange from ", Start().ToString(), " to ", End().ToString()});
}

bool operator==(const AXRange& a, const AXRange& b) {
  DCHECK(a.IsValid() && b.IsValid());
  return a.Start() == b.Start() && a.End() == b.End();
}

std::ostream& operator<<(std::ostream& ostream, const AXRange& range) {
  return ostream << range.ToString().Utf8();
}

}  // namespace blink
