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

#include "config.h"
#include "core/editing/InputMethodController.h"

#include "core/dom/Element.h"
#include "core/dom/Range.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLDocument.h"
#include "core/html/HTMLInputElement.h"
#include "core/testing/DummyPageHolder.h"
#include <gtest/gtest.h>

using namespace blink;
using namespace blink;

namespace {

class InputMethodControllerTest : public ::testing::Test {
protected:
    InputMethodController& controller() { return frame().inputMethodController(); }
    HTMLDocument& document() const { return *m_document; }
    LocalFrame& frame() const { return m_dummyPageHolder->frame(); }
    Element* insertHTMLElement(const char* elementCode, const char* elementId);

private:
    virtual void SetUp() OVERRIDE;

    OwnPtr<DummyPageHolder> m_dummyPageHolder;
    HTMLDocument* m_document;
};

void InputMethodControllerTest::SetUp()
{
    m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
    m_document = toHTMLDocument(&m_dummyPageHolder->document());
    ASSERT(m_document);
}

Element* InputMethodControllerTest::insertHTMLElement(
    const char* elementCode, const char* elementId)
{
    document().write(elementCode);
    document().updateLayout();
    Element* element = document().getElementById(elementId);
    element->focus();
    return element;
}

TEST_F(InputMethodControllerTest, BackspaceFromEndOfInput)
{
    HTMLInputElement* input = toHTMLInputElement(
        insertHTMLElement("<input id='sample'>", "sample"));

    input->setValue("fooX");
    controller().setEditableSelectionOffsets(PlainTextRange(4, 4));
    EXPECT_STREQ("fooX", input->value().utf8().data());
    controller().extendSelectionAndDelete(0, 0);
    EXPECT_STREQ("fooX", input->value().utf8().data());

    input->setValue("fooX");
    controller().setEditableSelectionOffsets(PlainTextRange(4, 4));
    EXPECT_STREQ("fooX", input->value().utf8().data());
    controller().extendSelectionAndDelete(1, 0);
    EXPECT_STREQ("foo", input->value().utf8().data());

    input->setValue(String::fromUTF8("foo\xE2\x98\x85")); // U+2605 == "black star"
    controller().setEditableSelectionOffsets(PlainTextRange(4, 4));
    EXPECT_STREQ("foo\xE2\x98\x85", input->value().utf8().data());
    controller().extendSelectionAndDelete(1, 0);
    EXPECT_STREQ("foo", input->value().utf8().data());

    input->setValue(String::fromUTF8("foo\xF0\x9F\x8F\x86")); // U+1F3C6 == "trophy"
    controller().setEditableSelectionOffsets(PlainTextRange(4, 4));
    EXPECT_STREQ("foo\xF0\x9F\x8F\x86", input->value().utf8().data());
    controller().extendSelectionAndDelete(1, 0);
    EXPECT_STREQ("foo", input->value().utf8().data());

    input->setValue(String::fromUTF8("foo\xE0\xB8\x81\xE0\xB9\x89")); // composed U+0E01 "ka kai" + U+0E49 "mai tho"
    controller().setEditableSelectionOffsets(PlainTextRange(4, 4));
    EXPECT_STREQ("foo\xE0\xB8\x81\xE0\xB9\x89", input->value().utf8().data());
    controller().extendSelectionAndDelete(1, 0);
    EXPECT_STREQ("foo", input->value().utf8().data());

    input->setValue("fooX");
    controller().setEditableSelectionOffsets(PlainTextRange(4, 4));
    EXPECT_STREQ("fooX", input->value().utf8().data());
    controller().extendSelectionAndDelete(0, 1);
    EXPECT_STREQ("fooX", input->value().utf8().data());
}

TEST_F(InputMethodControllerTest, SetCompositionFromExistingText)
{
    Element* div = insertHTMLElement(
        "<div id='sample' contenteditable='true'>hello world</div>", "sample");

    Vector<CompositionUnderline> underlines;
    underlines.append(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
    controller().setCompositionFromExistingText(underlines, 0, 5);

    RefPtrWillBeRawPtr<Range> range = controller().compositionRange();
    EXPECT_EQ(0, range->startOffset());
    EXPECT_EQ(5, range->endOffset());

    PlainTextRange plainTextRange(PlainTextRange::create(*div, *range.get()));
    EXPECT_EQ(0u, plainTextRange.start());
    EXPECT_EQ(5u, plainTextRange.end());
}

TEST_F(InputMethodControllerTest, SetCompositionFromExistingTextWithCollapsedWhiteSpace)
{
    // Creates a div with one leading new line char. The new line char is hidden
    // from the user and IME, but is visible to InputMethodController.
    Element* div = insertHTMLElement(
        "<div id='sample' contenteditable='true'>\nhello world</div>", "sample");

    Vector<CompositionUnderline> underlines;
    underlines.append(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
    controller().setCompositionFromExistingText(underlines, 0, 5);

    RefPtrWillBeRawPtr<Range> range = controller().compositionRange();
    EXPECT_EQ(1, range->startOffset());
    EXPECT_EQ(6, range->endOffset());

    PlainTextRange plainTextRange(PlainTextRange::create(*div, *range.get()));
    EXPECT_EQ(0u, plainTextRange.start());
    EXPECT_EQ(5u, plainTextRange.end());
}

TEST_F(InputMethodControllerTest, SetCompositionFromExistingTextWithInvalidOffsets)
{
    insertHTMLElement("<div id='sample' contenteditable='true'>test</div>", "sample");

    Vector<CompositionUnderline> underlines;
    underlines.append(CompositionUnderline(7, 8, Color(255, 0, 0), false, 0));
    controller().setCompositionFromExistingText(underlines, 7, 8);

    EXPECT_FALSE(controller().compositionRange());
}

} // namespace
