import os
import sys
import unittest
from datetime import datetime
from pathlib import Path

import xlwings as xw
from xlwings.constants import RgbColor

from .common import TestBase, this_dir


class TestRangeInstantiation(TestBase):
    def test_range1(self):
        r = self.wb1.sheets[0].range("A1")
        self.assertEqual(r.address, "$A$1")

    def test_range2(self):
        r = self.wb1.sheets[0].range("A1:A1")
        self.assertEqual(r.address, "$A$1")

    def test_range3(self):
        r = self.wb1.sheets[0].range("B2:D5")
        self.assertEqual(r.address, "$B$2:$D$5")

    def test_range4(self):
        r = self.wb1.sheets[0].range((1, 1))
        self.assertEqual(r.address, "$A$1")

    def test_range5(self):
        r = self.wb1.sheets[0].range((1, 1), (1, 1))
        self.assertEqual(r.address, "$A$1")

    def test_range6(self):
        r = self.wb1.sheets[0].range((2, 2), (5, 4))
        self.assertEqual(r.address, "$B$2:$D$5")

    def test_range7(self):
        r = self.wb1.sheets[0].range("A1", (2, 2))
        self.assertEqual(r.address, "$A$1:$B$2")

    def test_range8(self):
        r = self.wb1.sheets[0].range((1, 1), "B2")
        self.assertEqual(r.address, "$A$1:$B$2")

    def test_range9(self):
        r = self.wb1.sheets[0].range(
            self.wb1.sheets[0].range("A1"), self.wb1.sheets[0].range("B2")
        )
        self.assertEqual(r.address, "$A$1:$B$2")

    def test_range10(self):
        with self.assertRaises(ValueError):
            self.wb1.sheets[0].range(
                self.wb2.sheets[0].range("A1"), self.wb1.sheets[0].range("B2")
            )

    def test_range11(self):
        with self.assertRaises(ValueError):
            self.wb1.sheets[1].range(
                self.wb1.sheets[0].range("A1"), self.wb1.sheets[0].range("B2")
            )

    def test_range12(self):
        with self.assertRaises(ValueError):
            self.wb1.sheets[0].range(
                self.wb1.sheets[1].range("A1"), self.wb1.sheets[0].range("B2")
            )

    def test_range13(self):
        with self.assertRaises(ValueError):
            self.wb1.sheets[0].range(
                self.wb1.sheets[0].range("A1"), self.wb1.sheets[1].range("B2")
            )

    def test_zero_based_index1(self):
        with self.assertRaises(IndexError):
            self.wb1.sheets[0].range((0, 1)).value = 123

    def test_zero_based_index2(self):
        with self.assertRaises(IndexError):
            self.wb1.sheets[0].range((1, 1), (1, 0)).value

    def test_zero_based_index3(self):
        with self.assertRaises(IndexError):
            xw.Range((1, 0)).value = 123

    def test_zero_based_index4(self):
        with self.assertRaises(IndexError):
            xw.Range((1, 0), (1, 0)).value

    def test_jagged_array(self):
        with self.assertRaises(Exception):
            self.wb1.sheets[0].range("A1").value = [[1], [1, 2]]

        with self.assertRaises(Exception):
            self.wb1.sheets[0].range("A1").value = [[1, 2, 3], [4, 5], [6, 7, 8]]

        with self.assertRaises(Exception):
            self.wb1.sheets[0].range("A1").value = ((1,), (1, 2))

        # the following should not raise an error
        self.wb1.sheets[0].range("A1").value = 1
        self.wb1.sheets[0].range("A1").value = "s"
        self.wb1.sheets[0].range("A1").value = [[1, 2], [1, 2]]
        self.wb1.sheets[0].range("A1").value = [1, 2, 3]
        self.wb1.sheets[0].range("A1").value = [[1, 2, 3]]
        self.wb1.sheets[0].range("A1").value = []


class TestRangeAttributes(TestBase):
    def test_iterator(self):
        self.wb1.sheets[0].range("A20").value = [[1.0, 2.0], [3.0, 4.0]]
        r = self.wb1.sheets[0].range("A20:B21")

        self.assertEqual([c.value for c in r], [1.0, 2.0, 3.0, 4.0])

        # check that reiterating on same range works properly
        self.assertEqual([c.value for c in r], [1.0, 2.0, 3.0, 4.0])

    def test_sheet(self):
        self.assertEqual(
            self.wb1.sheets[1].range("A1").sheet.name, self.wb1.sheets[1].name
        )

    def test_len(self):
        self.assertEqual(len(self.wb1.sheets[0].range("A1:C4")), 12)

    def test_count(self):
        self.assertEqual(
            len(self.wb1.sheets[0].range("A1:C4")),
            self.wb1.sheets[0].range("A1:C4").count,
        )

    def test_row(self):
        self.assertEqual(self.wb1.sheets[0].range("B3:F5").row, 3)

    def test_column(self):
        self.assertEqual(self.wb1.sheets[0].range("B3:F5").column, 2)

    def test_row_count(self):
        self.assertEqual(self.wb1.sheets[0].range("B3:F5").rows.count, 3)

    def test_column_count(self):
        self.assertEqual(self.wb1.sheets[0].range("B3:F5").columns.count, 5)

    def raw_value(self):
        pass  # TODO

    def test_clear_content(self):
        self.wb1.sheets[0].range("G1").value = 22
        self.wb1.sheets[0].range("G1").clear_contents()
        self.assertEqual(self.wb1.sheets[0].range("G1").value, None)

    def test_clear_formats(self):
        self.wb1.sheets[0].range("G1").value = 22
        self.wb1.sheets[0].range("G1").color = (255, 0, 0)
        self.wb1.sheets[0].range("G1").clear_formats()
        self.assertEqual(self.wb1.sheets[0].range("G1").value, 22)
        self.assertEqual(self.wb1.sheets[0].range("G1").color, None)

    def test_clear(self):
        self.wb1.sheets[0].range("G1").value = 22
        self.wb1.sheets[0].range("G1").clear()
        self.assertEqual(self.wb1.sheets[0].range("G1").value, None)

    def test_end(self):
        self.wb1.sheets[0].range("A1:C5").value = 1.0
        self.assertEqual(
            self.wb1.sheets[0].range("A1").end("d"), self.wb1.sheets[0].range("A5")
        )
        self.assertEqual(
            self.wb1.sheets[0].range("A1").end("down"), self.wb1.sheets[0].range("A5")
        )
        self.assertEqual(
            self.wb1.sheets[0].range("C5").end("u"), self.wb1.sheets[0].range("C1")
        )
        self.assertEqual(
            self.wb1.sheets[0].range("C5").end("up"), self.wb1.sheets[0].range("C1")
        )
        self.assertEqual(
            self.wb1.sheets[0].range("A1").end("right"), self.wb1.sheets[0].range("C1")
        )
        self.assertEqual(
            self.wb1.sheets[0].range("A1").end("r"), self.wb1.sheets[0].range("C1")
        )
        self.assertEqual(
            self.wb1.sheets[0].range("C5").end("left"), self.wb1.sheets[0].range("A5")
        )
        self.assertEqual(
            self.wb1.sheets[0].range("C5").end("l"), self.wb1.sheets[0].range("A5")
        )

    def test_formula(self):
        self.wb1.sheets[0].range("A1").formula = "=SUM(A2:A10)"
        self.assertEqual(self.wb1.sheets[0].range("A1").formula, "=SUM(A2:A10)")

    def test_formula2(self):
        self.wb1.sheets[0].range("A1").formula2 = "=UNIQUE(A2:A10)"
        self.assertEqual(self.wb1.sheets[0].range("A1").formula2, "=UNIQUE(A2:A10)")

    def test_formula_array(self):
        self.wb1.sheets[0].range("A1").value = [[1, 4], [2, 5], [3, 6]]
        self.wb1.sheets[0].range("D1").formula_array = "=SUM(A1:A3*B1:B3)"
        self.assertEqual(self.wb1.sheets[0].range("D1").value, 32.0)

    def test_column_width(self):
        self.wb1.sheets[0].range("A1:B2").column_width = 10.0
        result = self.wb1.sheets[0].range("A1").column_width
        self.assertEqual(10.0, result)

        self.wb1.sheets[0].range("A1:B2").value = "ensure cells are used"
        self.wb1.sheets[0].range("B2").column_width = 20.0
        result = self.wb1.sheets[0].range("A1:B2").column_width
        self.assertEqual(None, result)

    def test_row_height(self):
        self.wb1.sheets[0].range("A1:B2").row_height = 15.0
        result = self.wb1.sheets[0].range("A1").row_height
        self.assertEqual(15.0, result)

        self.wb1.sheets[0].range("A1:B2").value = "ensure cells are used"
        self.wb1.sheets[0].range("B2").row_height = 20.0
        result = self.wb1.sheets[0].range("A1:B2").row_height
        self.assertEqual(None, result)

    def test_width(self):
        """test_width: Width depends on default style text size,
        so do not test absolute widths"""
        self.wb1.sheets[0].range("A1:D4").column_width = 10.0
        result_before = self.wb1.sheets[0].range("A1").width
        self.wb1.sheets[0].range("A1:D4").column_width = 12.0
        result_after = self.wb1.sheets[0].range("A1").width
        self.assertTrue(result_after > result_before)

    def test_height(self):
        self.wb1.sheets[0].range("A1:D4").row_height = 60.0
        result = self.wb1.sheets[0].range("A1:D4").height
        self.assertEqual(240.0, result)

    def test_left(self):
        self.assertEqual(self.wb1.sheets[0].range("A1").left, 0.0)
        self.wb1.sheets[0].range("A1").column_width = 20.0
        self.assertEqual(
            self.wb1.sheets[0].range("B1").left, self.wb1.sheets[0].range("A1").width
        )

    def test_top(self):
        self.assertEqual(self.wb1.sheets[0].range("A1").top, 0.0)
        self.wb1.sheets[0].range("A1").row_height = 20.0
        self.assertEqual(
            self.wb1.sheets[0].range("A2").top, self.wb1.sheets[0].range("A1").height
        )

    def test_number_format_cell(self):
        format_string = "mm/dd/yy;@"
        self.wb1.sheets[0].range("A1").number_format = format_string
        result = self.wb1.sheets[0].range("A1").number_format
        self.assertEqual(format_string, result)

    def test_number_format_range(self):
        format_string = "mm/dd/yy;@"
        self.wb1.sheets[0].range("A1:D4").number_format = format_string
        result = self.wb1.sheets[0].range("A1:D4").number_format
        self.assertEqual(format_string, result)

    def test_get_address(self):
        wb1 = self.app1.books.open(os.path.join(this_dir, "test book.xlsx"))

        res = wb1.sheets[0].range((1, 1), (3, 3)).get_address()
        self.assertEqual(res, "$A$1:$C$3")

        res = wb1.sheets[0].range((1, 1), (3, 3)).get_address(False)
        self.assertEqual(res, "$A1:$C3")

        res = wb1.sheets[0].range((1, 1), (3, 3)).get_address(True, False)
        self.assertEqual(res, "A$1:C$3")

        res = wb1.sheets[0].range((1, 1), (3, 3)).get_address(False, False)
        self.assertEqual(res, "A1:C3")

        res = wb1.sheets[0].range((1, 1), (3, 3)).get_address(include_sheetname=True)
        self.assertEqual(res, "'Sheet1'!$A$1:$C$3")

        res = wb1.sheets[1].range((1, 1), (3, 3)).get_address(include_sheetname=True)
        self.assertEqual(res, "'Sheet2'!$A$1:$C$3")

        res = wb1.sheets[0].range((1, 1), (3, 3)).get_address(external=True)
        self.assertEqual(res, "'[test book.xlsx]Sheet1'!$A$1:$C$3")

    def test_address(self):
        self.assertEqual(self.wb1.sheets[0].range("A1:B2").address, "$A$1:$B$2")

    def test_current_region(self):
        values = [[1.0, 2.0], [3.0, 4.0]]
        self.wb1.sheets[0].range("A20").value = values
        self.assertEqual(self.wb1.sheets[0].range("B21").current_region.value, values)

    def test_autofit_range(self):
        self.wb1.sheets[0].range("A1:D4").value = "test_string"

        self.wb1.sheets[0].range("A1:D4").row_height = 40
        self.wb1.sheets[0].range("A1:D4").column_width = 40
        self.assertEqual(40, self.wb1.sheets[0].range("A1:D4").row_height)
        self.assertEqual(40, self.wb1.sheets[0].range("A1:D4").column_width)
        self.wb1.sheets[0].range("A1:D4").autofit()
        self.assertTrue(40 != self.wb1.sheets[0].range("A1:D4").column_width)
        self.assertTrue(40 != self.wb1.sheets[0].range("A1:D4").row_height)

        self.wb1.sheets[0].range("A1:D4").row_height = 40
        self.assertEqual(40, self.wb1.sheets[0].range("A1:D4").row_height)
        self.wb1.sheets[0].range("A1:D4").rows.autofit()
        self.assertTrue(40 != self.wb1.sheets[0].range("A1:D4").row_height)

        self.wb1.sheets[0].range("A1:D4").column_width = 40
        self.assertEqual(40, self.wb1.sheets[0].range("A1:D4").column_width)
        self.wb1.sheets[0].range("A1:D4").columns.autofit()
        self.assertTrue(40 != self.wb1.sheets[0].range("A1:D4").column_width)

        self.wb1.sheets[0].range("A1:D4").rows.autofit()
        self.wb1.sheets[0].range("A1:D4").columns.autofit()

    def test_autofit_col(self):
        self.wb1.sheets[0].range("A1:D4").value = "test_string"
        self.wb1.sheets[0].range("A:D").column_width = 40
        self.assertEqual(40, self.wb1.sheets[0].range("A:D").column_width)
        self.wb1.sheets[0].range("A:D").autofit()
        self.assertTrue(40 != self.wb1.sheets[0].range("A:D").column_width)

        # Just checking if they don't throw an error
        self.wb1.sheets[0].range("A:D").rows.autofit()
        self.wb1.sheets[0].range("A:D").columns.autofit()

    def test_autofit_row(self):
        self.wb1.sheets[0].range("A1:D4").value = "test_string"
        self.wb1.sheets[0].range("1:10").row_height = 40
        self.assertEqual(40, self.wb1.sheets[0].range("1:10").row_height)
        self.wb1.sheets[0].range("1:10").autofit()
        self.assertTrue(40 != self.wb1.sheets[0].range("1:10").row_height)

        # Just checking if they don't throw an error
        self.wb1.sheets[0].range("1:1000000").rows.autofit()
        self.wb1.sheets[0].range("1:1000000").columns.autofit()

    def test_color(self):
        rgb = (30, 100, 200)

        self.wb1.sheets[0].range("A1").color = rgb
        self.assertEqual(rgb, self.wb1.sheets[0].range("A1").color)

        self.wb1.sheets[0].range("A2").color = RgbColor.rgbAqua
        self.assertEqual((0, 255, 255), self.wb1.sheets[0].range("A2").color)

        self.wb1.sheets[0].range("A2").color = None
        self.assertEqual(self.wb1.sheets[0].range("A2").color, None)

        self.wb1.sheets[0].range("A1:D4").color = rgb
        self.assertEqual(rgb, self.wb1.sheets[0].range("A1:D4").color)

    def test_len_rows(self):
        self.assertEqual(len(self.wb1.sheets[0].range("A1:C4").rows), 4)

    def test_count_rows(self):
        self.assertEqual(
            len(self.wb1.sheets[0].range("A1:C4").rows),
            self.wb1.sheets[0].range("A1:C4").rows.count,
        )

    def test_len_cols(self):
        self.assertEqual(len(self.wb1.sheets[0].range("A1:C4").columns), 3)

    def test_count_cols(self):
        self.assertEqual(
            len(self.wb1.sheets[0].range("A1:C4").columns),
            self.wb1.sheets[0].range("A1:C4").columns.count,
        )

    def test_shape(self):
        self.assertEqual(self.wb1.sheets[0].range("A1:C4").shape, (4, 3))

    def test_size(self):
        self.assertEqual(self.wb1.sheets[0].range("A1:C4").size, 12)

    def test_table(self):
        data = [
            [1, 2.222, 3.333],
            ["Test1", None, "éöà"],
            [datetime(1962, 11, 3), datetime(2020, 12, 31, 12, 12, 20), 9.999],
        ]
        self.wb1.sheets[0].range("A1").value = data
        if sys.platform.startswith("win") and self.wb1.app.version == "14.0":
            self.wb1.sheets[0].range(
                "A3:B3"
            ).number_format = "dd/mm/yyyy"  # Hack for Excel 2010 bug, see GH #43
        cells = self.wb1.sheets[0].range("A1").expand("table").value
        self.assertEqual(cells, data)

    def test_vertical(self):
        data = [
            [1, 2.222, 3.333],
            ["Test1", None, "éöà"],
            [datetime(1962, 11, 3), datetime(2020, 12, 31, 12, 12, 20), 9.999],
        ]
        self.wb1.sheets[0].range("A10").value = data
        if sys.platform.startswith("win") and self.wb1.app.version == "14.0":
            self.wb1.sheets[0].range(
                "A12:B12"
            ).number_format = "dd/mm/yyyy"  # Hack for Excel 2010 bug, see GH #43
        cells = self.wb1.sheets[0].range("A10").expand("vertical").value
        self.assertEqual(cells, [row[0] for row in data])

        cells = self.wb1.sheets[0].range("A10").expand("d").value
        self.assertEqual(cells, [row[0] for row in data])

        cells = self.wb1.sheets[0].range("A10").expand("down").value
        self.assertEqual(cells, [row[0] for row in data])

    def test_horizontal(self):
        data = [
            [1, 2.222, 3.333],
            ["Test1", None, "éöà"],
            [datetime(1962, 11, 3), datetime(2020, 12, 31, 12, 12, 20), 9.999],
        ]
        self.wb1.sheets[0].range("A20").value = data
        cells = self.wb1.sheets[0].range("A20").expand("horizontal").value
        self.assertEqual(cells, data[0])

        cells = self.wb1.sheets[0].range("A20").expand("r").value
        self.assertEqual(cells, data[0])

        cells = self.wb1.sheets[0].range("A20").expand("right").value
        self.assertEqual(cells, data[0])

    def test_hyperlink(self):
        address = "www.xlwings.org"
        # Naked address
        self.wb1.sheets[0].range("A1").add_hyperlink(address)
        self.assertEqual(self.wb1.sheets[0].range("A1").value, address)
        hyperlink = self.wb1.sheets[0].range("A1").hyperlink
        if not hyperlink.endswith("/"):
            hyperlink += "/"
        self.assertEqual(hyperlink, "http://" + address + "/")

        # Address + FriendlyName
        self.wb1.sheets[0].range("A2").add_hyperlink(address, "test_link")
        self.assertEqual(self.wb1.sheets[0].range("A2").value, "test_link")
        hyperlink = self.wb1.sheets[0].range("A2").hyperlink
        if not hyperlink.endswith("/"):
            hyperlink += "/"
        self.assertEqual(hyperlink, "http://" + address + "/")

    def test_hyperlink_formula(self):
        self.wb1.sheets[0].range(
            "B10"
        ).formula = '=HYPERLINK("http://xlwings.org", "xlwings")'
        self.assertEqual(
            self.wb1.sheets[0].range("B10").hyperlink, "http://xlwings.org"
        )

    def test_insert_cell(self):
        self.wb1.sheets[0].range("A1:C1").value = "test"
        self.wb1.sheets[0].range("A1").insert()
        self.assertIsNone(self.wb1.sheets[0].range("A1").value)
        self.assertEqual(self.wb1.sheets[0].range("A2").value, "test")

    def test_insert_row(self):
        self.wb1.sheets[0].range("A1:C1").value = "test"
        self.wb1.sheets[0].range("1:1").insert()
        self.assertEqual(self.wb1.sheets[0].range("A1:C1").value, [None, None, None])
        self.assertEqual(
            self.wb1.sheets[0].range("A2:C2").value, ["test", "test", "test"]
        )

    def test_insert_column(self):
        self.wb1.sheets[0].range("A1:A3").value = "test"
        self.wb1.sheets[0].range("A:A").insert()
        self.assertEqual(self.wb1.sheets[0].range("A1:A3").value, [None, None, None])
        self.assertEqual(
            self.wb1.sheets[0].range("B1:B3").value, ["test", "test", "test"]
        )

    def test_insert_cell_shift_down(self):
        self.wb1.sheets[0].range("A1:C1").value = "test"
        self.wb1.sheets[0].range("A1").insert(shift="down")
        self.assertIsNone(self.wb1.sheets[0].range("A1").value)
        self.assertEqual(self.wb1.sheets[0].range("A2").value, "test")

    def test_insert_cell_shift_right(self):
        self.wb1.sheets[0].range("A1:C1").value = "test"
        self.wb1.sheets[0].range("A1").insert(shift="right")
        self.assertIsNone(self.wb1.sheets[0].range("A1").value)
        self.assertEqual(
            self.wb1.sheets[0].range("B1:D1").value, ["test", "test", "test"]
        )

    def test_delete_cell(self):
        self.wb1.sheets[0].range("A1").value = ["one", "two", "three"]
        self.wb1.sheets[0].range("A1").delete()
        self.assertIsNone(self.wb1.sheets[0].range("C1").value)
        self.assertEqual(self.wb1.sheets[0].range("A1").value, "two")

    def test_delete_row(self):
        self.wb1.sheets[0].range("A1:C1").value = "one"
        self.wb1.sheets[0].range("A2:C2").value = "two"
        self.wb1.sheets[0].range("1:1").delete()
        self.assertEqual(self.wb1.sheets[0].range("A1:C1").value, ["two", "two", "two"])
        self.assertEqual(self.wb1.sheets[0].range("A2:C2").value, [None, None, None])

    def test_delete_column(self):
        self.wb1.sheets[0].range("A1:A1").value = "one"
        self.wb1.sheets[0].range("B1:B2").value = "two"
        self.wb1.sheets[0].range("C1:C2").value = "two"
        self.wb1.sheets[0].range("A:A").delete()
        self.assertEqual(self.wb1.sheets[0].range("C1:C2").value, [None, None])
        self.assertEqual(self.wb1.sheets[0].range("A1:A2").value, ["two", "two"])

    def test_delete_cell_shift_up(self):
        self.wb1.sheets[0].range("A1").value = ["one", "two", "three"]
        self.wb1.sheets[0].range("A1").delete("up")
        self.assertIsNone(self.wb1.sheets[0].range("A1").value)
        self.assertEqual(self.wb1.sheets[0].range("B1:C1").value, ["two", "three"])

    def test_delete_cell_shift_left(self):
        self.wb1.sheets[0].range("A1").value = ["one", "two", "three"]
        self.wb1.sheets[0].range("A1").delete("left")
        self.assertIsNone(self.wb1.sheets[0].range("C1").value)
        self.assertEqual(self.wb1.sheets[0].range("A1").value, "two")

    def test_copy_destination(self):
        sheet = self.wb1.sheets[0]
        sheet.range("A1:B1").value = "test"
        sheet.range("A1:B1").copy(destination=sheet.range("A2"))
        self.assertEqual(sheet.range("A1:B1").value, sheet.range("A2:B2").value)

    def test_copy_clipboard(self):
        sheet = self.wb1.sheets[0]
        sheet.range("A1:B1").value = "test"
        sheet.range("A1:B1").copy()

    def test_paste(self):
        sheet = self.wb1.sheets[0]
        sheet.range("A1:B1").value = "test"
        sheet.range("A1:B1").color = (34, 34, 34)
        sheet.range("A1:B1").copy()
        sheet.range("A2").paste()
        self.assertEqual(sheet["A1:B1"].value, sheet["A2:B2"].value)
        self.assertEqual(sheet["A1:B1"].color, sheet["A2:B2"].color)

    def test_paste_values(self):
        sheet = self.wb1.sheets[0]
        sheet.range("A1:B1").value = "test"
        sheet.range("A1:B1").color = (34, 34, 34)
        sheet.range("A1:B1").copy()
        sheet.range("A2").paste(paste="values")
        self.assertEqual(sheet["A1:B1"].value, sheet["A2:B2"].value)
        self.assertNotEqual(sheet["A1:B1"].color, sheet["A2:B2"].color)

    def test_resize(self):
        r = self.wb1.sheets[0].range("A1").resize(4, 5)
        self.assertEqual(r.address, "$A$1:$E$4")

        r = self.wb1.sheets[0].range("A1").resize(row_size=4)
        self.assertEqual(r.address, "$A$1:$A$4")

        r = self.wb1.sheets[0].range("A1:B4").resize(column_size=5)
        self.assertEqual(r.address, "$A$1:$E$4")

        r = self.wb1.sheets[0].range("A1:B4").resize(row_size=5)
        self.assertEqual(r.address, "$A$1:$B$5")

        r = self.wb1.sheets[0].range("A1:B4").resize()
        self.assertEqual(r.address, "$A$1:$B$4")

        r = self.wb1.sheets[0].range("A1:C5").resize(row_size=1)
        self.assertEqual(r.address, "$A$1:$C$1")

        with self.assertRaises(AssertionError):
            self.wb1.sheets[0].range("A1:B4").resize(row_size=0)

        with self.assertRaises(AssertionError):
            self.wb1.sheets[0].range("A1:B4").resize(column_size=0)

    def test_offset(self):
        o = self.wb1.sheets[0].range("A1:B3").offset(3, 4)
        self.assertEqual(o.address, "$E$4:$F$6")

        o = self.wb1.sheets[0].range("A1:B3").offset(row_offset=3)
        self.assertEqual(o.address, "$A$4:$B$6")

        o = self.wb1.sheets[0].range("A1:B3").offset(column_offset=4)
        self.assertEqual(o.address, "$E$1:$F$3")

    def test_last_cell(self):
        self.assertEqual(self.wb1.sheets[0].range("B3:F5").last_cell.row, 5)
        self.assertEqual(self.wb1.sheets[0].range("B3:F5").last_cell.column, 6)

    def test_select(self):
        self.wb2.sheets[0].range("C10").select()
        self.assertEqual(
            self.app2.selection.address, self.wb2.sheets[0].range("C10").address
        )

    def test_wrap_text(self):
        self.assertFalse(self.wb1.sheets[0]["A1"].wrap_text)
        self.wb1.sheets[0]["A1"].wrap_text = True
        self.assertTrue(self.wb1.sheets[0]["A1"].wrap_text)


class TestRangeIndexing(TestBase):
    # 2d Range
    def test_index1(self):
        r = self.wb1.sheets[0].range("A1:B2")

        self.assertEqual(r[0].address, "$A$1")
        self.assertEqual(r(1).address, "$A$1")

        self.assertEqual(r[0, 0].address, "$A$1")
        self.assertEqual(r(1, 1).address, "$A$1")

    def test_index2(self):
        r = self.wb1.sheets[0].range("A1:B2")

        self.assertEqual(r[1].address, "$B$1")
        self.assertEqual(r(2).address, "$B$1")

        self.assertEqual(r[0, 1].address, "$B$1")
        self.assertEqual(r(1, 2).address, "$B$1")

    def test_index3(self):
        with self.assertRaises(IndexError):
            r = self.wb1.sheets[0].range("A1:B2")
            r[4].address

    def test_index4(self):
        r = self.wb1.sheets[0].range("A1:B2")
        self.assertEqual(r(5).address, "$A$3")

    def test_index5(self):
        with self.assertRaises(IndexError):
            r = self.wb1.sheets[0].range("A1:B2")
            r[0, 4].address

    def test_index6(self):
        r = self.wb1.sheets[0].range("A1:B2")
        self.assertEqual(r(1, 5).address, "$E$1")

    # Row
    def test_index1row(self):
        r = self.wb1.sheets[0].range("A1:D1")

        self.assertEqual(r[0].address, "$A$1")
        self.assertEqual(r(1).address, "$A$1")

        self.assertEqual(r[0, 0].address, "$A$1")
        self.assertEqual(r(1, 1).address, "$A$1")

    def test_index2row(self):
        r = self.wb1.sheets[0].range("A1:D1")

        self.assertEqual(r[1].address, "$B$1")
        self.assertEqual(r(2).address, "$B$1")

        self.assertEqual(r[0, 1].address, "$B$1")
        self.assertEqual(r(1, 2).address, "$B$1")

    def test_index3row(self):
        with self.assertRaises(IndexError):
            r = self.wb1.sheets[0].range("A1:D1")
            r[4].address

    def test_index4row(self):
        r = self.wb1.sheets[0].range("A1:D1")
        self.assertEqual(r(5).address, "$A$2")

    def test_index5row(self):
        with self.assertRaises(IndexError):
            r = self.wb1.sheets[0].range("A1:D1")
            r[0, 4].address

    def test_index6row(self):
        r = self.wb1.sheets[0].range("A1:D1")
        self.assertEqual(r(1, 5).address, "$E$1")

    # Column
    def test_index1col(self):
        r = self.wb1.sheets[0].range("A1:A4")

        self.assertEqual(r[0].address, "$A$1")
        self.assertEqual(r(1).address, "$A$1")

        self.assertEqual(r[0, 0].address, "$A$1")
        self.assertEqual(r(1, 1).address, "$A$1")

    def test_index2col(self):
        r = self.wb1.sheets[0].range("A1:A4")

        self.assertEqual(r[1].address, "$A$2")
        self.assertEqual(r(2).address, "$A$2")

        self.assertEqual(r[1, 0].address, "$A$2")
        self.assertEqual(r(2, 1).address, "$A$2")

    def test_index3col(self):
        with self.assertRaises(IndexError):
            r = self.wb1.sheets[0].range("A1:A4")
            r[4].address

    def test_index4col(self):
        r = self.wb1.sheets[0].range("A1:A4")
        self.assertEqual(r(5).address, "$A$5")

    def test_index5col(self):
        with self.assertRaises(IndexError):
            r = self.wb1.sheets[0].range("A1:A4")
            r[4, 0].address

    def test_index6col(self):
        r = self.wb1.sheets[0].range("A1:A4")
        self.assertEqual(r(5, 1).address, "$A$5")


class TestRangeSlicing(TestBase):
    # 2d Range
    def test_slice1(self):
        r = self.wb1.sheets[0].range("B2:D4")
        self.assertEqual(r[0:, 1:].address, "$C$2:$D$4")

    def test_slice2(self):
        r = self.wb1.sheets[0].range("B2:D4")
        self.assertEqual(r[1:2, 1:2].address, "$C$3")

    def test_slice3(self):
        r = self.wb1.sheets[0].range("B2:D4")
        self.assertEqual(r[:1, :2].address, "$B$2:$C$2")

    def test_slice4(self):
        r = self.wb1.sheets[0].range("B2:D4")
        self.assertEqual(r[:, :].address, "$B$2:$D$4")

    # Row
    def test_slice1row(self):
        r = self.wb1.sheets[0].range("B2:D2")
        self.assertEqual(r[1:].address, "$C$2:$D$2")

    def test_slice2row(self):
        r = self.wb1.sheets[0].range("B2:D2")
        self.assertEqual(r[1:2].address, "$C$2")

    def test_slice3row(self):
        r = self.wb1.sheets[0].range("B2:D2")
        self.assertEqual(r[:2].address, "$B$2:$C$2")

    def test_slice4row(self):
        r = self.wb1.sheets[0].range("B2:D2")
        self.assertEqual(r[:].address, "$B$2:$D$2")

    # Column
    def test_slice1col(self):
        r = self.wb1.sheets[0].range("B2:B4")
        self.assertEqual(r[1:].address, "$B$3:$B$4")

    def test_slice2col(self):
        r = self.wb1.sheets[0].range("B2:B4")
        self.assertEqual(r[1:2].address, "$B$3")

    def test_slice3col(self):
        r = self.wb1.sheets[0].range("B2:B4")
        self.assertEqual(r[:2].address, "$B$2:$B$3")

    def test_slice4col(self):
        r = self.wb1.sheets[0].range("B2:B4")
        self.assertEqual(r[:].address, "$B$2:$B$4")


class TestRangeShortcut(TestBase):
    def test_shortcut1(self):
        self.assertEqual(self.wb1.sheets[0]["A1"], self.wb1.sheets[0].range("A1"))

    def test_shortcut2(self):
        self.assertEqual(self.wb1.sheets[0]["A1:B5"], self.wb1.sheets[0].range("A1:B5"))

    def test_shortcut3(self):
        self.assertEqual(self.wb1.sheets[0][0, 1], self.wb1.sheets[0].range("B1"))

    def test_shortcut4(self):
        self.assertEqual(self.wb1.sheets[0][:5, :5], self.wb1.sheets[0].range("A1:E5"))

    def test_shortcut5(self):
        with self.assertRaises(TypeError):
            self.wb1.sheets[0]["A1", "B5"]

    def test_shortcut6(self):
        with self.assertRaises(TypeError):
            self.wb1.sheets[0][self.wb1.sheets[0]["A1"], "B5"]

    def test_shortcut7(self):
        with self.assertRaises(TypeError):
            self.wb1.sheets[0]["A1", self.wb1.sheets[0]["B5"]]


class TestRangeExpansion(TestBase):
    def test_table(self):
        sht = self.wb1.sheets[0]
        rng = sht[0, 0]

        rng.value = [["a"] * 5] * 5

        self.assertEqual(rng.options(expand="table").value, [["a"] * 5] * 5)

    def test_vertical(self):
        sht = self.wb1.sheets[0]
        rng = sht[0, 0:3]

        sht[0, 0].value = [["a"] * 3] * 5

        self.assertEqual(rng.options(expand="down").value, [["a"] * 3] * 5)

    def test_horizontal(self):
        sht = self.wb1.sheets[0]
        rng = sht[0:5, 0]

        sht[0, 0].value = [["a"] * 3] * 5

        self.assertEqual(rng.options(expand="right").value, [["a"] * 3] * 5)

    def test_copy_picture(self):
        rng = self.wb1.sheets[0]["A1:B2"]
        rng.copy_picture()
        rng.copy_picture("screen", "picture")
        rng.copy_picture("printer", "bitmap")


class TestCellErrors(TestBase):
    def test_cell_errors_default(self):
        wb = xw.Book(Path(this_dir) / "cell_errors.xlsx")
        sheet = wb.sheets[0]

        for i in range(1, 8):
            self.assertIsNone(sheet.range((i, 1)).value)
        wb.close()

    def test_cell_errors_str(self):
        wb = xw.Book(Path(this_dir) / "cell_errors.xlsx")
        sheet = wb.sheets[0]
        # Single cells, since macOS has massive issues with ranges that contain cell
        # errors, see #1028 and #1924
        expected = ["#DIV/0!", "#N/A", "#NAME?", "#NULL!", "#NUM!", "#REF!", "#VALUE!"]
        for i in range(1, 8):
            self.assertEqual(
                sheet.range((i, 1)).options(err_to_str=True).value, expected[i - 1]
            )
        wb.close()


class TestMerging(TestBase):
    def test_merge(self):
        sheet = self.wb1.sheets[0]
        self.assertEqual(sheet["A1"].merge_area, sheet["A1"])
        self.assertEqual(sheet["A1"].merge_cells, False)
        sheet["A1:A2"].merge()
        self.assertEqual(sheet["A1"].merge_area, sheet["A1:A2"])
        self.assertEqual(sheet["A1"].merge_cells, True)
        sheet["A1:B2"].merge()
        self.assertEqual(sheet["A1"].merge_area, sheet["A1:B2"])
        sheet["A1:B2"].unmerge()
        self.assertEqual(sheet["A1"].merge_area, sheet["A1"])
        sheet["A1:B2"].merge(True)
        self.assertEqual(sheet["A1"].merge_area, sheet["A1:B1"])


class TestNotes(unittest.TestCase):
    def test_note(self):
        sheet = xw.Book(Path(this_dir) / "reports" / "template_one_frame.xlsx").sheets[
            0
        ]
        self.assertEqual(sheet["A1"].note.text, "<frame>")
        self.assertIsNone(sheet["A2"].note)
        sheet["A1"].note.delete()
        self.assertIsNone(sheet["A1"].note)
        sheet.book.close()


if __name__ == "__main__":
    unittest.main()
