/* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
group "doc.mouseactions";
require init;

require undefined MOUSELESS;

language C++;

include "modules/dochand/docman.h";
include "modules/dochand/fdelm.h";
include "modules/dochand/win.h";
include "modules/doc/frm_doc.h";
include "modules/doc/html_doc.h";
include "modules/display/vis_dev.h";
include "modules/doc/externalinlinelistener.h";
include "modules/url/url_api.h";
include "modules/forms/formvalue.h";

global
{
}

html
{
	//! <html><body>
	//! <style>div:hover { color: orange; } </style>
	//! <style>div:active { color: blue; } </style>
	//! <div style="width:100px; height:100px"></div>
	//! <div style="width:100px; height:100px"></div>
}
test("MouseMove-Hover")
{
	verify(state.doc);
	HTML_Document* html_doc = state.doc->GetHtmlDocument();
	verify(html_doc);
	HTML_Element* first_div = find_element("div", 1);
	HTML_Element* second_div = find_element("div", 2);
	verify(first_div);
	verify(second_div);
	verify(html_doc->GetHoverHTMLElement() == NULL);

	state.doc->MouseAction(ONMOUSEMOVE, 30, 30, 0, 0, 0, FALSE, FALSE, FALSE);

	verify(html_doc->GetHoverHTMLElement() != NULL);
	verify(html_doc->GetHoverHTMLElement() == first_div);
	verify(html_doc->GetHoverPseudoHTMLElement() != NULL);
	verify(html_doc->GetHoverPseudoHTMLElement() == first_div);

	// move the mouse to the second div
	state.doc->MouseAction(ONMOUSEMOVE, 30, 130, 0, 0, 0, FALSE, FALSE, FALSE);

	verify(html_doc->GetHoverHTMLElement() != NULL);
	verify(html_doc->GetHoverHTMLElement() == second_div);
	verify(html_doc->GetHoverPseudoHTMLElement() != NULL);
	verify(html_doc->GetHoverPseudoHTMLElement() == second_div);
}

test("MouseDown-Active")
{
	verify(state.doc);
	HTML_Document* html_doc = state.doc->GetHtmlDocument();
	verify(html_doc);
	HTML_Element* first_div = find_element("div", 1);
	HTML_Element* second_div = find_element("div", 2);
	verify(first_div);
	verify(second_div);

	state.doc->MouseAction(ONMOUSEMOVE, 30, 30, 0, 0, 0, FALSE, FALSE, FALSE);

	verify(html_doc->GetHoverHTMLElement() == first_div);
	verify(html_doc->GetActiveHTMLElement() == NULL);

	state.doc->MouseAction(ONMOUSEDOWN, 30, 30, 0, 0, 0, FALSE, FALSE, FALSE);

	verify(html_doc->GetActiveHTMLElement() != NULL);
	verify(html_doc->GetActiveHTMLElement() == first_div);
	verify(html_doc->GetActivePseudoHTMLElement() == first_div);

	state.doc->MouseAction(ONMOUSEUP, 30, 30, 0, 0, 0, FALSE, FALSE, FALSE);

	verify(html_doc->GetActiveHTMLElement() == NULL);
	verify(html_doc->GetActivePseudoHTMLElement() == NULL);
}

html
{
	//! <html><head>
	//!   <script>
	//!     window.firstover = 0;
	//!     window.firstout = 0;
	//!     window.secondover = 0;
	//!     window.secondout = 0;
	//!     window.thirdover = 0;
	//!     window.thirdout = 0;
	//!   </script>
	//! </head>
	//! <body>
	//!   <div style="width:200px;height:200px;" onmouseover="window.firstover++;" onmouseout="window.firstout++">
	//!     <div style="width:100px;height:100px;" onmouseover="window.secondover++;" onmouseout="window.secondout++">
	//!       Text
	//!     </div>
	//!   </div>
	//!   <div style="width:200px;height:200px;" onmouseover="window.thirdover++;" onmouseout="window.thirdout++">
	//!   </div>
}

test("Updating hover element in a tree that changes#1")
{
	verify(state.doc);
	HTML_Document* html_doc = state.doc->GetHtmlDocument();
	verify(html_doc);
	HTML_Element* first_div = find_element("div", 1);
	HTML_Element* second_div = find_element("div", 2);
	HTML_Element* third_div = find_element("div", 3);
	verify(first_div);
	verify(second_div);
	verify(third_div);

	// This should send onmouseover to first_div and second_div
	state.doc->MouseAction(ONMOUSEMOVE, 30, 30, 0, 0, 0, FALSE, FALSE, FALSE);

	verify(html_doc->GetHoverHTMLElement() == second_div);
}

// Have to take a break so that we can process the first mousemoves.
test("Updating hover element in a tree that changes#2")
require success "Updating hover element in a tree that changes#1";
{
	verify(state.doc);
	HTML_Document* html_doc = state.doc->GetHtmlDocument();
	verify(html_doc);
	HTML_Element* first_div = find_element("div", 1);
	HTML_Element* second_div = find_element("div", 2);
	HTML_Element* third_div = find_element("div", 3);
	verify(first_div);
	verify(second_div);
	verify(third_div);

	second_div->Remove(state.doc);
	if (second_div->OutSafe(state.doc))
		second_div->Free(state.doc);

	// Now the hover should have moved to first_div
	verify(html_doc->GetHoverHTMLElement() == first_div);
}

// Have to take a break so that we can process the first mousemoves.
test("Updating hover element in a tree that changes#3")
require success "Updating hover element in a tree that changes#2";
{
	verify(state.doc);
	HTML_Document* html_doc = state.doc->GetHtmlDocument();
	verify(html_doc);
	HTML_Element* first_div = find_element("div", 1);
	HTML_Element* third_div = find_element("div", 2);
	verify(first_div);
	verify(third_div);

	// moves the mouse to the third div
	state.doc->MouseAction(ONMOUSEMOVE, 30, 250, 0, 0, 0, FALSE, FALSE, FALSE);
	verify(html_doc->GetHoverHTMLElement() == third_div);
}

test("Updating hover element in a tree that changes#4")
language ecmascript;
require success "Updating hover element in a tree that changes#3";
{
	// Do fuzzy testing first and be more and more specific further down
	// to make error reporting as good as possible
	verify(window.firstover == window.firstout);
	verify(window.secondover >= window.secondout);
	verify(window.thirdover > window.thirdout);

	verify(window.secondover == 1);
	verify(window.thirdover == 1);
	verify(window.firstover == 1);

	verify(window.firstout == 1);
	verify(window.thirdout == 0);
	verify(window.secondout == 0);
}

html
{
	//! <html><title>High document</title><body>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	//! <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
}


test("Mouse Wheel")
{
	verify(state.doc);
	verify(state.doc->GetVisualViewY() == 0);

	// FIXME: These tests rely on MouseAction to be synchronous. They are
	// if there's no mouse event listener which there isn't, but
	// there might be some from user.js or browser.js.
	// Furthermore, the implementation of
	// OpViewportListener::OnVisualViewportEdgeChangeRequest() is not required
	// to change visual viewport synchronously, or even change it at all.

	// Scroll up some (not possible)
	state.doc->MouseWheel(10, 10, FALSE, FALSE, FALSE, -10, TRUE);
	verify(state.doc->GetVisualViewY() == 0);

	// Scroll down some
	state.doc->MouseWheel(10, 10, FALSE, FALSE, FALSE, 10, TRUE);
	long y1 = state.doc->GetVisualViewY();
	verify(y1 > 0);

	// Scroll down some more
	state.doc->MouseWheel(10, 10, FALSE, FALSE, FALSE, 10, TRUE);
	long y2 = state.doc->GetVisualViewY();
	verify(y2 > 0);
	verify(y2 > y1);

	// Scroll up some
	state.doc->MouseWheel(10, 10, FALSE, FALSE, FALSE, -10, TRUE);
	long y3 = state.doc->GetVisualViewY();
	verify(y3 > 0);
	verify(y3 < y2);
	verify(y3 == y1);

	// Scroll up some more (half of the previous scrolls
	state.doc->MouseWheel(10, 10, FALSE, FALSE, FALSE, -5, TRUE);
	long y4 = state.doc->GetVisualViewY();
	verify(y4 > 0);
	verify(y4 < y3); // y3 == y1
	// verify that y4 ~= y3/2
}
