// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

goog.provide('goog.ui.editor.BubbleTest');
goog.setTestOnly('goog.ui.editor.BubbleTest');

goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.positioning.Corner');
goog.require('goog.positioning.OverflowStatus');
goog.require('goog.string');
goog.require('goog.style');
goog.require('goog.testing.editor.TestHelper');
goog.require('goog.testing.events');
goog.require('goog.testing.jsunit');
goog.require('goog.ui.Component');
goog.require('goog.ui.editor.Bubble');
goog.require('goog.userAgent.product');

var testHelper;
var fieldDiv;
var bubble;
var link;
var link2;
var panelId;

function setUpPage() {
  fieldDiv = goog.dom.getElement('field');
  var viewportSize = goog.dom.getViewportSize();
  // Some tests depends on enough size of viewport.
  if (viewportSize.width < 600 || viewportSize.height < 440) {
    window.moveTo(0, 0);
    window.resizeTo(640, 480);
  }
}

function setUp() {
  testHelper = new goog.testing.editor.TestHelper(fieldDiv);
  testHelper.setUpEditableElement();

  bubble = new goog.ui.editor.Bubble(document.body, 999);

  fieldDiv.innerHTML = '<a href="http://www.google.com">Google</a>' +
      '<a href="http://www.google.com">Google2</a>';
  link = fieldDiv.firstChild;
  link2 = fieldDiv.lastChild;

  window.scrollTo(0, 0);
  goog.style.setStyle(document.body, 'direction', 'ltr');
  goog.style.setStyle(document.getElementById('field'), 'position', 'static');
}

function tearDown() {
  if (panelId) {
    bubble.removePanel(panelId);
    panelId = null;
  }
  testHelper.tearDownEditableElement();
}


/**
 * This is a helper function for setting up the target element with a
 * given direction.
 *
 * @param {string} dir The direction of the target element, 'ltr' or 'rtl'.
 * @param {boolean=} opt_preferTopPosition Whether to prefer placing the bubble
 *     above the element instead of below it.  Defaults to preferring below.
 */
function prepareTargetWithGivenDirection(dir, opt_preferTopPosition) {
  goog.style.setStyle(document.body, 'direction', dir);

  fieldDiv.style.direction = dir;
  fieldDiv.innerHTML = '<a href="http://www.google.com">Google</a>';
  link = fieldDiv.firstChild;

  panelId = bubble.addPanel('A', 'Link', link, function(el) {
    el.innerHTML = '<div style="border:1px solid blue;">B</div>';
  }, opt_preferTopPosition);
}


/**
 * This is a helper function for getting the expected position of the bubble.
 * (align to the right or the left of the target element).  Align left by
 * default and align right if opt_alignRight is true. The expected Y is
 * unaffected by alignment.
 *
 * @param {boolean=} opt_alignRight Sets the expected alignment to be right.
 */
function getExpectedBubblePositionWithGivenAlignment(opt_alignRight) {
  var targetPosition = goog.style.getFramedPageOffset(link, window);
  var targetWidth = link.offsetWidth;
  var bubbleSize = goog.style.getSize(bubble.bubbleContainer_);
  var expectedBubbleX = opt_alignRight ?
      targetPosition.x + targetWidth - bubbleSize.width :
      targetPosition.x;
  var expectedBubbleY = link.offsetHeight + targetPosition.y +
      goog.ui.editor.Bubble.VERTICAL_CLEARANCE_;

  return {x: expectedBubbleX, y: expectedBubbleY};
}

function testCreateBubbleWithLinkPanel() {
  var id = goog.string.createUniqueString();
  panelId = bubble.addPanel('A', 'Link', link, function(container) {
    container.innerHTML = '<span id="' + id + '">Test</span>';
  });
  assertNotNull('Bubble should be created', bubble.bubbleContents_);
  assertNotNull('Added element should be present', goog.dom.getElement(id));
  assertTrue('Bubble should be visible', bubble.isVisible());
}

function testCloseBubble() {
  testCreateBubbleWithLinkPanel();

  var count = 0;
  goog.events.listen(
      bubble, goog.ui.Component.EventType.HIDE, function() { count++; });

  bubble.removePanel(panelId);
  panelId = null;

  assertFalse('Bubble should not be visible', bubble.isVisible());
  assertEquals('Hide event should be dispatched', 1, count);
}

function testCloseBox() {
  testCreateBubbleWithLinkPanel();

  var count = 0;
  goog.events.listen(
      bubble, goog.ui.Component.EventType.HIDE, function() { count++; });

  var closeBox = goog.dom.getElementsByTagNameAndClass(
      goog.dom.TagName.DIV, 'tr_bubble_closebox', bubble.bubbleContainer_)[0];
  goog.testing.events.fireClickSequence(closeBox);
  panelId = null;

  assertFalse('Bubble should not be visible', bubble.isVisible());
  assertEquals('Hide event should be dispatched', 1, count);
}

function testViewPortSizeMonitorEvent() {
  testCreateBubbleWithLinkPanel();

  var numCalled = 0;
  bubble.reposition = function() { numCalled++; };

  assertNotUndefined(
      'viewPortSizeMonitor_ should not be undefined',
      bubble.viewPortSizeMonitor_);
  bubble.viewPortSizeMonitor_.dispatchEvent(goog.events.EventType.RESIZE);

  assertEquals('reposition not called', 1, numCalled);
}

function testBubblePositionPreferTop() {
  called = false;
  bubble.positionAtAnchor_ = function(targetCorner, bubbleCorner, overflow) {
    called = true;

    // Assert that the bubble is positioned below the target.
    assertEquals(goog.positioning.Corner.TOP_START, targetCorner);
    assertEquals(goog.positioning.Corner.BOTTOM_START, bubbleCorner);

    return goog.positioning.OverflowStatus.NONE;
  };
  prepareTargetWithGivenDirection('ltr', true);
  assertTrue(called);
}

function testBubblePosition() {
  panelId = bubble.addPanel('A', 'Link', link, goog.nullFunction);
  var CLEARANCE = goog.ui.editor.Bubble.VERTICAL_CLEARANCE_;
  var bubbleContainer = bubble.bubbleContainer_;

  // The field is at a normal place, alomost the top of the viewport, and
  // there is enough space at the bottom of the field.
  var targetPos = goog.style.getFramedPageOffset(link, window);
  var targetSize = goog.style.getSize(link);
  var pos = goog.style.getFramedPageOffset(bubbleContainer);
  assertEquals(targetPos.y + targetSize.height + CLEARANCE, pos.y);
  assertEquals(targetPos.x, pos.x);

  // Move the target to the bottom of the viewport.
  var field = document.getElementById('field');
  var fieldPos = goog.style.getFramedPageOffset(field, window);
  fieldPos.y +=
      bubble.dom_.getViewportSize().height - (targetPos.y + targetSize.height);
  goog.style.setStyle(field, 'position', 'absolute');
  goog.style.setPosition(field, fieldPos);
  bubble.reposition();
  var bubbleSize = goog.style.getSize(bubbleContainer);
  targetPosition = goog.style.getFramedPageOffset(link, window);
  pos = goog.style.getFramedPageOffset(bubbleContainer);
  assertEquals(targetPosition.y - CLEARANCE - bubbleSize.height, pos.y);
}

function testBubblePositionRightAligned() {
  if (goog.userAgent.product.SAFARI) {
    // TODO(b/20733468): Disabled so we can get the rest of the Closure test
    // suite running in a continuous build. Will investigate later.
    return;
  }

  prepareTargetWithGivenDirection('rtl');

  var expectedPos = getExpectedBubblePositionWithGivenAlignment(true);
  var pos = goog.style.getFramedPageOffset(bubble.bubbleContainer_);
  assertRoughlyEquals(expectedPos.x, pos.x, 0.1);
  assertRoughlyEquals(expectedPos.y, pos.y, 0.1);
}


/**
 * Test for bug 1955511, the bubble should align to the right side
 * of the target element when the bubble is RTL, regardless of the
 * target element's directionality.
 */
function testBubblePositionLeftToRight() {
  if (goog.userAgent.product.SAFARI) {
    // TODO(b/20733468): Disabled so we can get the rest of the Closure test
    // suite running in a continuous build. Will investigate later.
    return;
  }

  goog.style.setStyle(bubble.bubbleContainer_, 'direction', 'ltr');
  prepareTargetWithGivenDirection('rtl');

  var expectedPos = getExpectedBubblePositionWithGivenAlignment();
  var pos = goog.style.getFramedPageOffset(bubble.bubbleContainer_);
  assertRoughlyEquals(expectedPos.x, pos.x, 0.1);
  assertRoughlyEquals(expectedPos.y, pos.y, 0.1);
}


/**
 * Test for bug 1955511, the bubble should align to the left side
 * of the target element when the bubble is LTR, regardless of the
 * target element's directionality.
 */
function testBubblePositionRightToLeft() {
  goog.style.setStyle(bubble.bubbleContainer_, 'direction', 'rtl');
  prepareTargetWithGivenDirection('ltr');

  var expectedPos = getExpectedBubblePositionWithGivenAlignment(true);
  var pos = goog.style.getFramedPageOffset(bubble.bubbleContainer_);
  assertEquals(expectedPos.x, pos.x);
  assertEquals(expectedPos.y, pos.y);
}