############################################################################
# Original work Copyright 2018 Palantir Technologies, Inc.                 #
# Original work licensed under the MIT License.                            #
# See ThirdPartyNotices.txt in the project root for license information.   #
# All modifications Copyright (c) Open Law Library. 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.                                           #
############################################################################
import re

from lsprotocol import types
from pygls.workspace import TextDocument, PositionCodec
from .conftest import DOC, DOC_URI


def test_document_empty_edit():
    doc = TextDocument("file:///uri", "")
    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=0, character=0),
            end=types.Position(line=0, character=0),
        ),
        range_length=0,
        text="f",
    )
    doc.apply_change(change)
    assert doc.source == "f"


def test_document_end_of_file_edit():
    old = ["print 'a'\n", "print 'b'\n"]
    doc = TextDocument("file:///uri", "".join(old))

    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=2, character=0),
            end=types.Position(line=2, character=0),
        ),
        range_length=0,
        text="o",
    )
    doc.apply_change(change)

    assert doc.lines == [
        "print 'a'\n",
        "print 'b'\n",
        "o",
    ]


def test_document_full_edit():
    old = ["def hello(a, b):\n", "    print a\n", "    print b\n"]
    doc = TextDocument(
        "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Full
    )
    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=1, character=4),
            end=types.Position(line=2, character=11),
        ),
        range_length=0,
        text="print a, b",
    )
    doc.apply_change(change)

    assert doc.lines == ["print a, b"]

    doc = TextDocument(
        "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Full
    )
    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=0, character=0),
            end=types.Position(line=0, character=0),
        ),
        text="print a, b",
    )
    doc.apply_change(change)

    assert doc.lines == ["print a, b"]


def test_document_line_edit():
    doc = TextDocument("file:///uri", "itshelloworld")
    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=0, character=3),
            end=types.Position(line=0, character=8),
        ),
        range_length=0,
        text="goodbye",
    )
    doc.apply_change(change)
    assert doc.source == "itsgoodbyeworld"


def test_document_lines():
    doc = TextDocument(DOC_URI, DOC)
    assert len(doc.lines) == 4
    assert doc.lines[0] == "document\n"


def test_document_multiline_edit():
    old = ["def hello(a, b):\n", "    print a\n", "    print b\n"]
    doc = TextDocument(
        "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Incremental
    )
    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=1, character=4),
            end=types.Position(line=2, character=11),
        ),
        range_length=0,
        text="print a, b",
    )
    doc.apply_change(change)

    assert doc.lines == ["def hello(a, b):\n", "    print a, b\n"]

    doc = TextDocument(
        "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Incremental
    )
    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=1, character=4),
            end=types.Position(line=2, character=11),
        ),
        text="print a, b",
    )
    doc.apply_change(change)

    assert doc.lines == ["def hello(a, b):\n", "    print a, b\n"]


def test_document_no_edit():
    old = ["def hello(a, b):\n", "    print a\n", "    print b\n"]
    doc = TextDocument(
        "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.None_
    )
    change = types.TextDocumentContentChangeEvent_Type1(
        range=types.Range(
            start=types.Position(line=1, character=4),
            end=types.Position(line=2, character=11),
        ),
        range_length=0,
        text="print a, b",
    )
    doc.apply_change(change)

    assert doc.lines == old


def test_document_props():
    doc = TextDocument(DOC_URI, DOC)

    assert doc.uri == DOC_URI
    assert doc.source == DOC


def test_document_source_unicode():
    document_mem = TextDocument(DOC_URI, "my source")
    document_disk = TextDocument(DOC_URI)
    assert isinstance(document_mem.source, type(document_disk.source))


def test_position_from_utf16():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16)
    assert codec.position_from_client_units(
        ['x="😋"'], types.Position(line=0, character=3)
    ) == types.Position(line=0, character=3)
    assert codec.position_from_client_units(
        ['x="😋"'], types.Position(line=0, character=5)
    ) == types.Position(line=0, character=4)


def test_position_from_utf32():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf32)
    assert codec.position_from_client_units(
        ['x="😋"'], types.Position(line=0, character=3)
    ) == types.Position(line=0, character=3)
    assert codec.position_from_client_units(
        ['x="😋"'], types.Position(line=0, character=4)
    ) == types.Position(line=0, character=4)


def test_position_from_utf8():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf8)
    assert codec.position_from_client_units(
        ['x="😋"'], types.Position(line=0, character=3)
    ) == types.Position(line=0, character=3)
    assert codec.position_from_client_units(
        ['x="😋"'], types.Position(line=0, character=7)
    ) == types.Position(line=0, character=4)


def test_position_to_utf16():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16)
    assert codec.position_to_client_units(
        ['x="😋"'], types.Position(line=0, character=3)
    ) == types.Position(line=0, character=3)

    assert codec.position_to_client_units(
        ['x="😋"'], types.Position(line=0, character=4)
    ) == types.Position(line=0, character=5)


def test_position_to_utf32():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf32)
    assert codec.position_to_client_units(
        ['x="😋"'], types.Position(line=0, character=3)
    ) == types.Position(line=0, character=3)

    assert codec.position_to_client_units(
        ['x="😋"'], types.Position(line=0, character=4)
    ) == types.Position(line=0, character=4)


def test_position_to_utf8():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf8)
    assert codec.position_to_client_units(
        ['x="😋"'], types.Position(line=0, character=3)
    ) == types.Position(line=0, character=3)

    assert codec.position_to_client_units(
        ['x="😋"'], types.Position(line=0, character=4)
    ) == types.Position(line=0, character=6)


def test_range_from_utf16():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16)
    assert codec.range_from_client_units(
        ['x="😋"'],
        types.Range(
            start=types.Position(line=0, character=3),
            end=types.Position(line=0, character=5),
        ),
    ) == types.Range(
        start=types.Position(line=0, character=3),
        end=types.Position(line=0, character=4),
    )

    range = types.Range(
        start=types.Position(line=0, character=3),
        end=types.Position(line=0, character=5),
    )
    actual = codec.range_from_client_units(['x="😋😋"'], range)
    expected = types.Range(
        start=types.Position(line=0, character=3),
        end=types.Position(line=0, character=4),
    )
    assert actual == expected


def test_range_to_utf16():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16)
    assert codec.range_to_client_units(
        ['x="😋"'],
        types.Range(
            start=types.Position(line=0, character=3),
            end=types.Position(line=0, character=4),
        ),
    ) == types.Range(
        start=types.Position(line=0, character=3),
        end=types.Position(line=0, character=5),
    )

    range = types.Range(
        start=types.Position(line=0, character=3),
        end=types.Position(line=0, character=4),
    )
    actual = codec.range_to_client_units(['x="😋😋"'], range)
    expected = types.Range(
        start=types.Position(line=0, character=3),
        end=types.Position(line=0, character=5),
    )
    assert actual == expected


def test_offset_at_position_utf16():
    doc = TextDocument(DOC_URI, DOC)
    assert doc.offset_at_position(types.Position(line=0, character=8)) == 8
    assert doc.offset_at_position(types.Position(line=1, character=5)) == 12
    assert doc.offset_at_position(types.Position(line=2, character=0)) == 13
    assert doc.offset_at_position(types.Position(line=2, character=4)) == 17
    assert doc.offset_at_position(types.Position(line=3, character=6)) == 27
    assert doc.offset_at_position(types.Position(line=3, character=7)) == 28
    assert doc.offset_at_position(types.Position(line=3, character=8)) == 28
    assert doc.offset_at_position(types.Position(line=4, character=0)) == 40
    assert doc.offset_at_position(types.Position(line=5, character=0)) == 40


def test_offset_at_position_utf32():
    doc = TextDocument(
        DOC_URI,
        DOC,
        position_codec=PositionCodec(encoding=types.PositionEncodingKind.Utf32),
    )
    assert doc.offset_at_position(types.Position(line=0, character=8)) == 8
    assert doc.offset_at_position(types.Position(line=5, character=0)) == 39


def test_offset_at_position_utf8():
    doc = TextDocument(
        DOC_URI,
        DOC,
        position_codec=PositionCodec(encoding=types.PositionEncodingKind.Utf8),
    )
    assert doc.offset_at_position(types.Position(line=0, character=8)) == 8
    assert doc.offset_at_position(types.Position(line=5, character=0)) == 41


def test_utf16_to_utf32_position_cast():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16)
    lines = ["", "😋😋", ""]
    assert codec.position_from_client_units(
        lines, types.Position(line=0, character=0)
    ) == types.Position(line=0, character=0)
    assert codec.position_from_client_units(
        lines, types.Position(line=0, character=1)
    ) == types.Position(line=0, character=0)
    assert codec.position_from_client_units(
        lines, types.Position(line=1, character=0)
    ) == types.Position(line=1, character=0)
    assert codec.position_from_client_units(
        lines, types.Position(line=1, character=2)
    ) == types.Position(line=1, character=1)
    assert codec.position_from_client_units(
        lines, types.Position(line=1, character=3)
    ) == types.Position(line=1, character=2)
    assert codec.position_from_client_units(
        lines, types.Position(line=1, character=4)
    ) == types.Position(line=1, character=2)
    assert codec.position_from_client_units(
        lines, types.Position(line=1, character=100)
    ) == types.Position(line=1, character=2)
    assert codec.position_from_client_units(
        lines, types.Position(line=3, character=0)
    ) == types.Position(line=2, character=0)
    assert codec.position_from_client_units(
        lines, types.Position(line=4, character=10)
    ) == types.Position(line=2, character=0)


def test_position_for_line_endings():
    codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16)
    lines = ["x\r\n", "y\n"]
    assert codec.position_from_client_units(
        lines, types.Position(line=0, character=10)
    ) == types.Position(line=0, character=1)
    assert codec.position_from_client_units(
        lines, types.Position(line=1, character=10)
    ) == types.Position(line=1, character=1)


def test_word_at_position():
    """
    Return word under the cursor (or last in line if past the end)
    """
    doc = TextDocument(DOC_URI, DOC)

    assert doc.word_at_position(types.Position(line=0, character=8)) == "document"
    assert doc.word_at_position(types.Position(line=0, character=1000)) == "document"
    assert doc.word_at_position(types.Position(line=1, character=5)) == "for"
    assert doc.word_at_position(types.Position(line=2, character=0)) == "testing"
    assert doc.word_at_position(types.Position(line=3, character=10)) == "unicode"
    assert doc.word_at_position(types.Position(line=4, character=0)) == ""
    assert doc.word_at_position(types.Position(line=4, character=0)) == ""
    re_start_word = re.compile(r"[A-Za-z_0-9.]*$")
    re_end_word = re.compile(r"^[A-Za-z_0-9.]*")
    assert (
        doc.word_at_position(
            types.Position(
                line=3,
                character=10,
            ),
            re_start_word=re_start_word,
            re_end_word=re_end_word,
        )
        == "unicode."
    )
