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

language C++;

include "modules/doc/frm_doc.h";
include "modules/doc/html_doc.h";
include "modules/forms/formvalue.h";
include "modules/windowcommander/src/WindowCommander.h";

html
{
	//! <html><body><h1 style="font-size: 50px">Hoi</h1>
}

test("Selections#1")
{
	verify(state.doc);

	state.doc->SelectAll(TRUE);

	verify(state.doc->HasSelectedText());

	OpString text;
	OP_STATUS status = state.doc->GetSelectedText(text);
	verify(OpStatus::IsSuccess(status));
	verify(text.Compare("Hoi") == 0);

	state.doc->SelectAll(TRUE);

	verify(state.doc->HasSelectedText());

	text.Empty();
	status = state.doc->GetSelectedText(text);
	verify(OpStatus::IsSuccess(status));
	verify(text.Compare("Hoi") == 0);

	state.doc->SelectAll(FALSE);

	verify(!state.doc->HasSelectedText());

	text.Empty();
	status = state.doc->GetSelectedText(text);
	verify(OpStatus::IsSuccess(status));
	verify(text.IsEmpty());

	state.doc->SelectAll(FALSE);

	verify(!state.doc->HasSelectedText());

	text.Empty();
	status = state.doc->GetSelectedText(text);
	verify(OpStatus::IsSuccess(status));
	verify(text.IsEmpty());
}

html
{
	//! <!DOCTYPE html>
	//! <title>text selection API bug (addRange)</title>
	"<p>This text should be selected.</p>"
		//!	<script>
		//!   var range = document.createRange();
		//!   range.selectNode(document.getElementsByTagName('p')[0]);
		//!   var sel = window.getSelection();
		//!   sel.removeAllRanges();
		//!   sel.addRange(range);
		//! </script>
		}

test("Selections#2 - bug 264685")
{
	verify(state.doc);

	verify(state.doc->HasSelectedText());

	OpString text;
	OP_STATUS status = state.doc->GetSelectedText(text);
	verify(OpStatus::IsSuccess(status));
	verify(text.Compare("This text should be selected.") == 0);
}

html
{
	//! <html><body>
	//! <input value="abcde">
}

test("Selections#3 - input field")
{
	HTML_Element* input = find_element("input");
	verify(input);
	verify(state.doc);
	verify(!state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 0);

	HTML_Document* html_doc = state.doc->GetHtmlDocument();
	verify(html_doc);
	html_doc->FocusElement(input, HTML_Document::FOCUS_ORIGIN_UNKNOWN);
	FormValue* val = input->GetFormValue();
	val->SetSelection(input, 2, 4); // selecting "cd"
	INT32 form_sel_start, form_sel_end;
	val->GetSelection(input, form_sel_start, form_sel_end);
	verify(form_sel_start == 2);
	verify(form_sel_end == 4);

	verify(state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 2);
	uni_char buf[3]; // ARRAY OK 2008-06-19 bratell
	verify(state.doc->GetSelectedText(buf, 3, TRUE));
	verify(buf[0] == 'c');
	verify(buf[1] == 'd');
	verify(buf[2] == '\0');

	val->SetSelection(input, 0, 1); // selecting "a"
	val->GetSelection(input, form_sel_start, form_sel_end);
	verify(form_sel_start == 0);
	verify(form_sel_end == 1);

	verify(state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 1);
	verify(state.doc->GetSelectedText(buf, 3, TRUE));
	verify(buf[0] == 'a');
	verify(buf[1] == '\0');

	val->SetSelection(input, 4, 5); // selecting "e"
	val->GetSelection(input, form_sel_start, form_sel_end);
	verify(form_sel_start == 4);
	verify(form_sel_end == 5);

	verify(state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 1);
	verify(state.doc->GetSelectedText(buf, 3, TRUE));
	verify(buf[0] == 'e');
	verify(buf[1] == '\0');
}

html
{
	//! <html><body>
	//! <textarea>abcde</textarea>
}

test("Selections#4 - textarea")
{
	HTML_Element* textarea = find_element("textarea");
	verify(textarea);
	verify(state.doc);
	verify(!state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 0);

	HTML_Document* html_doc = state.doc->GetHtmlDocument();
	verify(html_doc);
	html_doc->FocusElement(textarea, HTML_Document::FOCUS_ORIGIN_UNKNOWN);
	FormValue* val = textarea->GetFormValue();
	val->SetSelection(textarea, 2, 4); // selecting "cd"
	INT32 form_sel_start, form_sel_end;
	val->GetSelection(textarea, form_sel_start, form_sel_end);
	verify(form_sel_start == 2);
	verify(form_sel_end == 4);

	verify(state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 2);
	uni_char buf[3]; // ARRAY OK 2008-06-19 bratell
	verify(state.doc->GetSelectedText(buf, 3, TRUE));
	verify(buf[0] == 'c');
	verify(buf[1] == 'd');
	verify(buf[2] == '\0');

	val->SetSelection(textarea, 0, 1); // selecting "a"
	val->GetSelection(textarea, form_sel_start, form_sel_end);
	verify(form_sel_start == 0);
	verify(form_sel_end == 1);

	verify(state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 1);
	verify(state.doc->GetSelectedText(buf, 3, TRUE));
	verify(buf[0] == 'a');
	verify(buf[1] == '\0');

	val->SetSelection(textarea, 4, 5); // selecting "e"
	val->GetSelection(textarea, form_sel_start, form_sel_end);
	verify(form_sel_start == 4);
	verify(form_sel_end == 5);

	verify(state.doc->HasSelectedText(TRUE));
	verify(state.doc->GetSelectedTextLen(TRUE) == 1);
	verify(state.doc->GetSelectedText(buf, 3, TRUE));
	verify(buf[0] == 'e');
	verify(buf[1] == '\0');
}

html
{
	//! <html><body><h1 style="font-size: 50px">Hoi</h1>
}

test("Selections#5 - CaretRect") 
	require undefined HAS_NOTEXTSELECTION;
	require DOCUMENT_EDIT_SUPPORT;
	require KEYBOARD_SELECTION_SUPPORT;
{
	verify(state.doc);
	
	state.doc->SetEditable(FramesDocument::DESIGNMODE);
	
	OpRect rect1;
	verify(OpStatus::IsSuccess(state.doc->GetCaretRect(rect1)));
	verify(!rect1.IsEmpty());

	state.doc->SetEditable(FramesDocument::DESIGNMODE_OFF);
	
	OpRect rect2;
	verify(!OpStatus::IsSuccess(state.doc->GetCaretRect(rect2)));
	verify(rect2.IsEmpty());
}

// Lacking a better way to check if a font exists, we test for it and
// let other tests depend on the success of this one. That way we make
// it easier for platforms to see the cause of failures relating to a
// missing font.
//
// If this isn't enough, selftest must export a way of requiring a
// certain font (check during run-time). The alternative is to remove
// the follow tests depending on a certain font.

test("AHEM")
{
	short font_number = styleManager->GetFontNumber(UNI_L("AHEM"));
	verify(font_number != -1);
}

html
{
	//! <html><body style="margin:0px"><div style="width:50px;font-size:16px;line-height:16px;font-family:'AHEM'">XXXXXXX XXXXXXX XXXXXX XXXXXXXX XXXXXXXX XXXXXXX XXXXXXXXX</div>
	//! </body></html>
}

test("Selections#6 - retrieving text coordinates.")
	require undefined HAS_NOTEXTSELECTION;
{
	FramesDocument* doc = state.doc;
	verify(doc != NULL);

	int x = -1, y = -1, line_height = -1;

	// Select first three letters.
	verify(OpStatus::IsSuccess(doc->StartSelection(0, 0)));
	doc->MoveSelectionFocusPoint(48, 0, FALSE);
	verify(doc->HasSelectedText());

	verify(OpStatus::IsSuccess(doc->GetSelectionAnchorPosition(x, y, line_height)));
	verify(x == 0);
	verify(y == 0);
	verify(line_height == 16);

	verify(OpStatus::IsSuccess(doc->GetSelectionFocusPosition(x, y, line_height)));
	verify(x == 48);	// 3 * 16 = 48
	verify(y == 0);
	verify(line_height == 16);

	// Select first line.
	doc->MoveSelectionFocusPoint(143, 0, FALSE);
	verify(OpStatus::IsSuccess(doc->GetSelectionFocusPosition(x, y, line_height)));
	verify(x == 112);	// 7 * 16 = 112
	verify(y == 0);
	verify(line_height == 16);

	// Select first two lines.
	doc->MoveSelectionFocusPoint(143, 31, FALSE);
	verify(OpStatus::IsSuccess(doc->GetSelectionFocusPosition(x, y, line_height)));
	verify(x == 112);	// 7 * 16 = 112
	verify(y == 16);
	verify(line_height == 16);

	// Select first three lines.
	doc->MoveSelectionFocusPoint(143, 47, FALSE);
	verify(OpStatus::IsSuccess(doc->GetSelectionFocusPosition(x, y, line_height)));
	verify(x == 96);	// 6 * 16 = 96
	verify(y == 32);
	verify(line_height == 16);
}

html
{
	"<html><body style='margin:0px'><div style=\"transform: rotate(-45deg);font-size:16px;line-height:16px;font-family:'AHEM'\">XXXXXXX</div></body></html>"
}

test("Selections#6 - retrieving coordinates from rotated text.")
	require undefined HAS_NOTEXTSELECTION;
	require CSS_TRANSFORMS;
{
	FramesDocument* doc = state.doc;
	verify(doc != NULL);

	// Select first three letters.
	verify(doc->SelectAll());
	verify(doc->HasSelectedText());

	// Only compare relative coordinates.
	int start_x = -1, start_y = -1, start_line_height = -1;
	verify(OpStatus::IsSuccess(doc->GetSelectionAnchorPosition(start_x, start_y, start_line_height)));
	int end_x = -1, end_y = -1, end_line_height = -1;
	verify(OpStatus::IsSuccess(doc->GetSelectionFocusPosition(end_x, end_y, end_line_height)));

	verify(start_x < end_x);
	verify(start_y > end_y);
	verify(start_line_height > 0);
	verify(end_line_height > 0);
}

html
{
	"<body>ABC</body>"
}

test("KeyboardSelection - simple")
	require KEYBOARD_SELECTION_SUPPORT;
{
	verify_success(state.doc->GetWindow()->GetWindowCommander()->SetKeyboardSelectable(TRUE));
	verify(state.doc->GetKeyboardSelectionMode());

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_DOCUMENT_HOME, FALSE);
	TextSelection* sel = state.doc->GetTextSelection();
	verify(sel);
	SelectionBoundaryPoint point = *sel->GetAnchorPoint();
	SelectionBoundaryPoint point2 = *sel->GetFocusPoint();
	verify(point == point2);
	HTML_Element* body = find_element("body");
	verify(body);
	HTML_Element* text = body->FirstChildActual();
	verify(text && text->Type() == HE_TEXT);
	verify(point.GetElement() == body || point.GetElement() == text);
	verify(point.GetOffset() == 0);

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_RIGHT, FALSE);
	sel = state.doc->GetTextSelection();
	verify(sel);
	point = *sel->GetAnchorPoint();
	point2 = *sel->GetFocusPoint();
	verify(point == point2);
	verify(point.GetElement() == text);
	verify(point.GetOffset() == 1);

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_LINE_END, FALSE);
	sel = state.doc->GetTextSelection();
	verify(sel);
	point = *sel->GetAnchorPoint();
	point2 = *sel->GetFocusPoint();
	verify(point == point2);
	verify(point.GetElement() == text);
	verify(point.GetOffset() == 3);

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_WORD_LEFT, FALSE);
	sel = state.doc->GetTextSelection();
	verify(sel);
	point = *sel->GetAnchorPoint();
	point2 = *sel->GetFocusPoint();
	verify(point == point2);
	verify(point.GetElement() == text);
	verify(point.GetOffset() == 0);
}

html
{
	"<body></body>"
}

test("KeyboardSelection - containing element")
require KEYBOARD_SELECTION_SUPPORT || DOCUMENT_EDIT_SUPPORT;
{
	HTML_Element* body = find_element("body");
	verify(state.doc->GetCaret()->GetContainingElementActual(body) == body);
}

html
{
	"<pre>Line1\nLine2</pre>"
}

test("KeyboardSelection - GetIsLineEnd")
require KEYBOARD_SELECTION_SUPPORT;
{
	verify_success(state.doc->GetWindow()->GetWindowCommander()->SetKeyboardSelectable(TRUE));
	verify(state.doc->GetKeyboardSelectionMode());

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_DOCUMENT_HOME, FALSE);
	TextSelection* sel = state.doc->GetTextSelection();
	verify(sel);
	SelectionBoundaryPoint point = *sel->GetAnchorPoint();
	SelectionBoundaryPoint point2 = *sel->GetFocusPoint();
	verify(point == point2);
	HTML_Element* body = find_element("body");
	verify(body);
	HTML_Element* pre = body->FirstChildActual();
	verify(pre);
	HTML_Element* text = pre->FirstChildActual();
	verify(text && text->Type() == HE_TEXT);
	verify(point.GetElement() == body || point.GetElement() == text);
	verify(point.GetOffset() == 0);

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_DOWN, FALSE);
	sel = state.doc->GetTextSelection();
	verify(sel);
	point = *sel->GetAnchorPoint();
	point2 = *sel->GetFocusPoint();
	verify(point == point2);
	verify(point.GetElement() == text);
	verify(point.GetOffset() == 6);
	verify(!state.doc->GetCaret()->GetIsLineEnd());

	point.SetBindDirection(SelectionBoundaryPoint::BIND_BACKWARD);
	state.doc->SetSelection(&point, &point);
	verify(state.doc->GetCaret()->GetIsLineEnd());
	verify(state.doc->GetCaret()->GetOffset() == 6);

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_LINE_HOME, FALSE);
	sel = state.doc->GetTextSelection();
	verify(sel);
	point = *sel->GetAnchorPoint();
	point2 = *sel->GetFocusPoint();
	verify(point == point2);
	verify(point.GetElement() == text);
	verify(point.GetOffset() == 0);
	verify(state.doc->GetCaret()->GetOffset() == 0);
}

html
{
	"<body>foo<div><p>bar</p></div><p>baz</p></body>"
}

test("KeyboardSelection - select")
require KEYBOARD_SELECTION_SUPPORT;
{
	verify_success(state.doc->GetWindow()->GetWindowCommander()->SetKeyboardSelectable(TRUE));
	verify(state.doc->GetKeyboardSelectionMode());

	HTML_Element* body = find_element("body");
	HTML_Element* foo = body->FirstChild();
	HTML_Element* div = foo->Suc();
	HTML_Element* p = div->FirstChild();
	HTML_Element* bar = p->FirstChild();
	HTML_Element* p2 = div->Suc();
	HTML_Element* baz = p2->FirstChild();

	SelectionBoundaryPoint start(div, 0), end(p2, 1);
	state.doc->SetSelection(&start, &end, TRUE);
	verify(state.doc->GetCaret()->GetOffset() == 3);
	verify(state.doc->GetCaret()->GetElement() == p2->FirstChild());

	verify(bar->IsInSelection());
	verify(baz->IsInSelection());

	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_UP, TRUE);
	state.doc->GetWindow()->GetWindowCommander()->MoveCaret(OpWindowCommander::CARET_UP, TRUE);

	verify(!bar->IsInSelection());
	verify(!baz->IsInSelection());
}
