from __future__ import annotations

import unittest

import urwid
from urwid.util import set_temporary_encoding


class TextTest(unittest.TestCase):
    def setUp(self):
        self.t = urwid.Text("I walk the\ncity in the night")

    def test1_wrap(self):
        expected = [t.encode("iso8859-1") for t in ("I walk the", "city in   ", "the night ")]
        got = self.t.render((10,))._text
        assert got == expected, f"got: {got!r} expected: {expected!r}"

    def test2_left(self):
        self.t.set_align_mode("left")
        expected = [t.encode("iso8859-1") for t in ("I walk the        ", "city in the night ")]
        got = self.t.render((18,))._text
        assert got == expected, f"got: {got!r} expected: {expected!r}"

    def test3_right(self):
        self.t.set_align_mode("right")
        expected = [t.encode("iso8859-1") for t in ("        I walk the", " city in the night")]
        got = self.t.render((18,))._text
        assert got == expected, f"got: {got!r} expected: {expected!r}"

    def test4_center(self):
        self.t.set_align_mode("center")
        expected = [t.encode("iso8859-1") for t in ("    I walk the    ", " city in the night")]
        got = self.t.render((18,))._text
        assert got == expected, f"got: {got!r} expected: {expected!r}"

    def test5_encode_error(self):
        with set_temporary_encoding("ascii"):
            expected = [b"?  "]
            got = urwid.Text("û").render((3,))._text
            assert got == expected, f"got: {got!r} expected: {expected!r}"

    def test_empty_markup_segment_middle(self) -> None:
        """An empty markup segment in the middle must not truncate the rendered row."""
        widget = urwid.Text([("a", "X" * 10), ("b", ""), ("c", " END")])

        self.assertEqual(("XXXXXXXXXX END", [("a", 10), ("b", 0), ("c", 4)]), widget.get_text())

        canvas = widget.render(())
        self.assertEqual([b"XXXXXXXXXX END"], canvas.text)
        self.assertEqual(("XXXXXXXXXX END",), canvas.decoded_text)

    def test_empty_markup_segment_start(self) -> None:
        """An empty markup segment at the start must keep the whole rendered row."""
        widget = urwid.Text([("b", ""), ("a", "X" * 10), ("c", " END")])

        self.assertEqual(("XXXXXXXXXX END", [("b", 0), ("a", 10), ("c", 4)]), widget.get_text())

        canvas = widget.render(())
        self.assertEqual([b"XXXXXXXXXX END"], canvas.text)
        self.assertEqual(("XXXXXXXXXX END",), canvas.decoded_text)

    def test_empty_markup_segment_end(self) -> None:
        """An empty markup segment at the end must keep the whole rendered row."""
        widget = urwid.Text([("a", "X" * 10), ("c", " END"), ("b", "")])

        self.assertEqual(("XXXXXXXXXX END", [("a", 10), ("c", 4), ("b", 0)]), widget.get_text())

        canvas = widget.render(())
        self.assertEqual([b"XXXXXXXXXX END"], canvas.text)
        self.assertEqual(("XXXXXXXXXX END",), canvas.decoded_text)

    def test_multiple_empty_markup_segments(self) -> None:
        """Several consecutive empty markup segments must not drop following content."""
        widget = urwid.Text([("a", "AB"), ("b", ""), ("c", ""), ("d", "CD")])

        self.assertEqual(("ABCD", [("a", 2), ("b", 0), ("c", 0), ("d", 2)]), widget.get_text())

        canvas = widget.render(())
        self.assertEqual([b"ABCD"], canvas.text)
        self.assertEqual(("ABCD",), canvas.decoded_text)

    def test_empty_markup_segment_render_content(self) -> None:
        """The canvas content rows must expose the display attribute of each segment."""
        widget = urwid.Text([("a", "X" * 10), ("b", ""), ("c", " END")])

        content = list(widget.render(()).content())
        attrs = [attr for row in content for attr, _cs, _text in row]
        rendered = b"".join(text for row in content for _attr, _cs, text in row)

        self.assertEqual(b"XXXXXXXXXX END", rendered)
        self.assertIn("a", attrs)
        self.assertIn("c", attrs)


class EditTest(unittest.TestCase):
    def setUp(self):
        self.t1 = urwid.Edit(b"", "blah blah")
        self.t2 = urwid.Edit(b"stuff:", "blah blah")
        self.t3 = urwid.Edit(b"junk:\n", "blah blah\n\nbloo", 1)
        self.t4 = urwid.Edit("better:")

    def ktest(self, e, key, expected, pos, desc):
        got = e.keypress((12,), key)
        assert got == expected, f"{desc}.  got: {got!r} expected:{expected!r}"
        assert e.edit_pos == pos, f"{desc}. pos: {e.edit_pos!r} expected pos: {pos!r}"

    def test1_left(self):
        self.t1.set_edit_pos(0)
        self.ktest(self.t1, "left", "left", 0, "left at left edge")

        self.ktest(self.t2, "left", None, 8, "left within text")

        self.t3.set_edit_pos(10)
        self.ktest(self.t3, "left", None, 9, "left after newline")

    def test2_right(self):
        self.ktest(self.t1, "right", "right", 9, "right at right edge")

        self.t2.set_edit_pos(8)
        self.ktest(self.t2, "right", None, 9, "right at right edge-1")
        self.t3.set_edit_pos(0)
        self.t3.keypress((12,), "right")
        assert self.t3.get_pref_col((12,)) == 1

    def test3_up(self):
        self.ktest(self.t1, "up", "up", 9, "up at top")
        self.t2.set_edit_pos(2)
        self.t2.keypress((12,), "left")
        assert self.t2.get_pref_col((12,)) == 7
        self.ktest(self.t2, "up", "up", 1, "up at top again")
        assert self.t2.get_pref_col((12,)) == 7
        self.t3.set_edit_pos(10)
        self.ktest(self.t3, "up", None, 0, "up at top+1")

    def test4_down(self):
        self.ktest(self.t1, "down", "down", 9, "down single line")
        self.t3.set_edit_pos(5)
        self.ktest(self.t3, "down", None, 10, "down line 1 to 2")
        self.ktest(self.t3, "down", None, 15, "down line 2 to 3")
        self.ktest(self.t3, "down", "down", 15, "down at bottom")

    def test_utf8_input(self):
        with set_temporary_encoding("utf-8"):
            self.t1.set_edit_text("")
            self.t1.keypress((12,), "û")
            self.assertEqual(self.t1.edit_text, "û".encode())
            self.t4.keypress((12,), "û")
            self.assertEqual(self.t4.edit_text, "û")


class EditRenderTest(unittest.TestCase):
    def rtest(self, w, expected_text, expected_cursor):
        expected_text = [t.encode("iso8859-1") for t in expected_text]
        get_cursor = w.get_cursor_coords((4,))
        assert get_cursor == expected_cursor, f"got: {get_cursor!r} expected: {expected_cursor!r}"
        r = w.render((4,), focus=1)
        text = [t for a, cs, t in [ln[0] for ln in r.content()]]
        assert text == expected_text, f"got: {text!r} expected: {expected_text!r}"
        assert r.cursor == expected_cursor, f"got: {r.cursor!r} expected: {expected_cursor!r}"

    def test1_SpaceWrap(self):
        w = urwid.Edit("", "blah blah")
        w.set_edit_pos(0)
        self.rtest(w, ["blah", "blah"], (0, 0))

        w.set_edit_pos(4)
        self.rtest(w, ["lah ", "blah"], (3, 0))

        w.set_edit_pos(5)
        self.rtest(w, ["blah", "blah"], (0, 1))

        w.set_edit_pos(9)
        self.rtest(w, ["blah", "lah "], (3, 1))

    def test2_ClipWrap(self):
        w = urwid.Edit("", "blah\nblargh", 1)
        w.set_wrap_mode("clip")
        w.set_edit_pos(0)
        self.rtest(w, ["blah", "blar"], (0, 0))

        w.set_edit_pos(10)
        self.rtest(w, ["blah", "argh"], (3, 1))

        w.set_align_mode("right")
        w.set_edit_pos(6)
        self.rtest(w, ["blah", "larg"], (0, 1))

    def test3_AnyWrap(self):
        w = urwid.Edit("", "blah blah")
        w.set_wrap_mode("any")

        self.rtest(w, ["blah", " bla", "h   "], (1, 2))

    def test4_CursorNudge(self):
        w = urwid.Edit("", "hi", align="right")
        w.keypress((4,), "end")

        self.rtest(w, [" hi "], (3, 0))

        w.keypress((4,), "left")
        self.rtest(w, ["  hi"], (3, 0))
