/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/layers/APZUtils.h"

#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_layers.h"

namespace mozilla {
namespace layers {

namespace apz {

bool IsCloseToHorizontal(float aAngle, float aThreshold) {
  return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
}

bool IsCloseToVertical(float aAngle, float aThreshold) {
  return (fabs(aAngle - (M_PI / 2)) < aThreshold);
}

bool IsStuckAtBottom(gfxFloat aTranslation,
                     const LayerRectAbsolute& aInnerRange,
                     const LayerRectAbsolute& aOuterRange) {
  // The item will be stuck at the bottom if the async scroll delta is in
  // the range [aOuterRange.Y(), aInnerRange.Y()]. Since the translation
  // is negated with repect to the async scroll delta (i.e. scrolling down
  // produces a positive scroll delta and negative translation), we invert it
  // and check to see if it falls in the specified range.
  return aOuterRange.Y() <= -aTranslation && -aTranslation <= aInnerRange.Y();
}

bool IsStuckAtTop(gfxFloat aTranslation, const LayerRectAbsolute& aInnerRange,
                  const LayerRectAbsolute& aOuterRange) {
  // Same as IsStuckAtBottom, except we want to check for the range
  // [aInnerRange.YMost(), aOuterRange.YMost()].
  return aInnerRange.YMost() <= -aTranslation &&
         -aTranslation <= aOuterRange.YMost();
}

bool AboutToCheckerboard(const FrameMetrics& aPaintedMetrics,
                         const FrameMetrics& aCompositorMetrics) {
  // The main-thread code to compute the painted area can introduce some
  // rounding error due to multiple unit conversions, so we inflate the rect by
  // one app unit to account for that.
  CSSRect painted = aPaintedMetrics.GetDisplayPort() +
                    aPaintedMetrics.GetLayoutScrollOffset();
  painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));

  // Inflate the rect by the danger zone. See the description of the danger zone
  // prefs in AsyncPanZoomController.cpp for an explanation of this.
  CSSRect visible =
      CSSRect(aCompositorMetrics.GetVisualScrollOffset(),
              aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
  visible.Inflate(ScreenSize(StaticPrefs::apz_danger_zone_x(),
                             StaticPrefs::apz_danger_zone_y()) /
                  aCompositorMetrics.DisplayportPixelsPerCSSPixel());

  // Clamp both rects to the scrollable rect, because having either of those
  // exceed the scrollable rect doesn't make sense, and could lead to false
  // positives.
  painted = painted.Intersect(aPaintedMetrics.GetScrollableRect());
  visible = visible.Intersect(aPaintedMetrics.GetScrollableRect());

  return !painted.Contains(visible);
}

SideBits GetOverscrollSideBits(const ParentLayerPoint& aOverscrollAmount) {
  SideBits sides = SideBits::eNone;

  if (aOverscrollAmount.x < 0) {
    sides |= SideBits::eLeft;
  } else if (aOverscrollAmount.x > 0) {
    sides |= SideBits::eRight;
  }

  if (aOverscrollAmount.y < 0) {
    sides |= SideBits::eTop;
  } else if (aOverscrollAmount.y > 0) {
    sides |= SideBits::eBottom;
  }

  return sides;
}

}  // namespace apz
}  // namespace layers
}  // namespace mozilla
