import numpy as np
import astropy.units as u

from .. import SRegion, patch_from_polygon


def test_sregion():
    """
    Test SRegion object
    """
    # From arrays
    x = np.array([0, 0, 1, 1])
    y = np.array([0, 1, 1, 0])
    sr = SRegion(np.array([x, y]).T)

    assert sr.N == 1

    assert np.allclose(sr.centroid[0], 0.5, rtol=1.0e-3)

    assert sr.area[0] == 1.0

    # Converters
    assert hasattr(sr.path[0], "contains_point")
    assert hasattr(sr.shapely[0], "boundary")
    assert hasattr(sr.patch()[0], "get_fc")

    regstr = (
        "polygon(0.000000,0.000000,0.000000,1.000000,"
        + "1.000000,1.000000,1.000000,0.000000)"
    )
    assert sr.region[0] == regstr

    sr.label = "test"
    assert sr.region[0] == regstr + " #  text={test}"

    # SQL string
    pstr = "((0.000,0.000),(0.000,1.000),(1.000,1.000),(1.000,0.000))"
    assert sr.polystr(precision=3)[0] == pstr

    # From s_region string
    for prefix in ["POLYGON", "POLYGON ICRS"]:
        sr.SREGION_PREFIX = prefix
        pstr = (
            f"{prefix} 0.000000 0.000000 0.000000 1.000000 "
            + "1.000000 1.000000 1.000000 0.000000"
        )

        assert pstr == sr.s_region

    snew = SRegion(pstr)
    assert snew.area[0] == 1.0

    # From polygon
    snew = SRegion(sr.shapely[0])
    assert snew.area[0] == 1.0

    # Compound regions
    x2 = np.array([0, 0, 1, 1]) + 2
    y2 = np.array([0, 1, 1, 0]) + 2
    s2 = SRegion(np.array([x2, y2]).T)

    un = sr.union(s2.shapely[0], as_polygon=True)

    assert un.area == 2.0

    # Multiple string
    comp = SRegion(" ".join([sr.s_region, s2.s_region]))
    assert np.allclose(comp.area, 1.0, rtol=1.0e-3)

    un = comp.union(as_polygon=True)
    assert un.area == 2.0

    # Intersects
    assert sr.intersects(s2.shapely[0]) is False

    x3 = np.array([0, 0, 1, 1]) + 0.5
    y3 = np.array([0, 1, 1, 0]) + 0.5
    s3 = SRegion(np.array([x3, y3]).T)

    assert sr.intersects(s3.shapely[0]) is True


def test_wrap():
    """ """
    x = np.array([0, 0, 1, 1]) - 177
    y = np.array([0, 1, 1, 0])
    sr = SRegion(np.array([x, y]).T, wrap=True)

    assert sr.N == 1
    assert np.allclose(sr.area, 1.0)
    assert np.allclose(sr.centroid, [360 - 177 + 0.5, 0.5])


def test_circles():
    """
    Initialize from ``CIRCLE X Y R``
    """
    # CIRCLE string
    circ = SRegion("CIRCLE 5. 5. 1", ncircle=256)
    assert np.allclose(circ.area, np.pi, rtol=1.0e-3)
    assert np.allclose(circ.centroid[0], 5.0, rtol=1.0e-3)

    # R = 2, no units
    circ2 = SRegion("CIRCLE 5. 5. 2", ncircle=256)
    assert np.allclose(circ2.area, 2**2 * np.pi, rtol=1.0e-3)
    assert np.allclose(circ2.centroid[0], 5.0, rtol=1.0e-3)

    # R = 1 arcsec
    cosd = np.cos(45.0 / 180 * np.pi)
    csky = SRegion('CIRCLE 45. 45. 1"', ncircle=256)
    assert np.allclose(csky.area, np.pi / 3600.0**2 / cosd, rtol=1.0e-3)
    assert np.allclose(
        csky.sky_area(unit=u.arcsec**2), np.pi * u.arcsec**2, rtol=1.0e-3
    )

    # R = 1 arcmin
    cosd = np.cos(45.0 / 180 * np.pi)
    csky = SRegion("CIRCLE 45. 45. 1'", ncircle=256)
    assert np.allclose(csky.area, np.pi / 3600.0 / cosd, rtol=1.0e-3)
    assert np.allclose(
        csky.sky_area(unit=u.arcsec**2), 3600 * np.pi * u.arcsec**2, rtol=1.0e-3
    )
    assert np.allclose(csky.centroid[0], 45.0, rtol=1.0e-3)

    # Sky buffer
    cosd = np.cos(45.0 / 180 * np.pi)
    csky = SRegion('CIRCLE 45. 45. 0.001"', ncircle=256)
    csky.sky_buffer(1.0)

    assert np.allclose(csky.area, np.pi / cosd, rtol=1.0e-3)
    assert np.allclose(csky.sky_area(unit=u.deg**2), np.pi * u.deg**2, rtol=1.0e-3)


def test_boxes():
    """
    Initialize from ``BOX X Y W H``
    """
    # BOX string
    box = SRegion("BOX 90 10 5 5")
    assert box.N == 1

    assert np.allclose(box.centroid[0][0], 90, rtol=1.0e-3)
    assert np.allclose(box.centroid[0][1], 10, rtol=1.0e-3)

    assert box.area[0] == 25.0

    # BOX ICRS string
    box = SRegion("BOX ICRS 90 10 5 5")
    assert box.N == 1

    assert np.allclose(box.centroid[0][0], 90, rtol=1.0e-3)
    assert np.allclose(box.centroid[0][1], 10, rtol=1.0e-3)

    assert box.area[0] == 25.0

    # size with units
    box = SRegion("BOX ICRS 90 10 5' 5'")
    assert box.N == 1

    assert np.allclose(box.centroid[0][0], 90, rtol=1.0e-3)
    assert np.allclose(box.centroid[0][1], 10, rtol=1.0e-3)
    assert np.allclose(box.sky_area()[0].value, 25.0)
    assert np.allclose(box.area[0], 25.0 / 3600. / np.cos(10. /180 * np.pi))

    box = SRegion("BOX ICRS 90 10 5\" 5\"")
    assert box.N == 1

    assert np.allclose(box.centroid[0][0], 90, rtol=1.0e-3)
    assert np.allclose(box.centroid[0][1], 10, rtol=1.0e-3)
    assert np.allclose(box.sky_area()[0].value, 25.0 / 3600.)
    assert np.allclose(box.area[0], 25.0 / (3600.**2) / np.cos(10. /180 * np.pi))

    # BOX ICRS GEOCENTER string
    box = SRegion("BOX ICRS GEOCENTER 11.9 40.4 7.5 7.5")
    assert box.N == 1

    assert np.allclose(box.centroid[0][0], 11.9, rtol=1.0e-3)
    assert np.allclose(box.centroid[0][1], 40.4, rtol=1.0e-3)

    assert box.area[0] == 56.25


def test_stcs():
    """
    Test stripping values from STC-S specified strings
    """
    stcs = """Union ICRS ( Polygon 239.807341 -18.296691 239.803564 -18.300277 239.799786 -18.296691 239.803563
    -18.293105 Polygon 239.797826 -18.295944 239.794049 -18.299530 239.790272 -18.295944 239.794049
    -18.292358)"""

    sr = SRegion(stcs, verbose=False)

    assert len(sr.xy) == 2
    assert np.allclose(sr.area, 2.709e-5, rtol=0.01)


def test_whitespace():
    """ """
    pstr = "CIRCLE 5. 5. 1"
    for str_i in [pstr, pstr.replace(" ", "   "), "  " + pstr, pstr + "\n"]:
        sr = SRegion(str_i, ncircle=256)
        assert sr.N == 1
        assert np.allclose(sr.area, np.pi, rtol=1.0e-3)
        assert np.allclose(sr.centroid[0], 5.0, rtol=1.0e-3)

    pstr = (
        "POLYGON 0.000000 0.000000 0.000000 1.000000 "
        + "1.000000 1.000000 1.000000 0.000000"
    )

    for str_i in [pstr, pstr.replace(" ", "  "), "  " + pstr, pstr + "\n"]:
        sr = SRegion(str_i)
        assert sr.N == 1
        assert np.allclose(sr.area, 1.0)
        assert np.allclose(sr.centroid, 0.5)


def test_fromwcs():
    """
    Initialize from `astropy.wcs.WCS`
    """
    from astropy.io.fits import Header
    import astropy.wcs as pywcs

    # From WCS
    header_string = """
CRPIX1  =                  1.0                                                  
CRPIX2  =                  1.0                                                  
CRVAL1  =                 90.0                                                  
CRVAL2  =                  0.0                                                  
CD1_1   =             -0.00001                                                  
CD1_2   =                  0.0                                                  
CD2_1   =                  0.0                                                  
CD2_2   =              0.00001                                                  
NAXIS1  =                 1001                                                  
NAXIS2  =                 1001                                                  
CTYPE1  = 'RA---TAN'                                                            
CTYPE2  = 'DEC--TAN'                                                            
RADESYS = 'ICRS    '                                                            
EQUINOX =               2000.0                                                  
LATPOLE =                    0                                                  
LONPOLE =                180.0
    """

    head = Header.fromstring(header_string.replace("\n", ""))
    wcs = pywcs.WCS(head)
    sw = SRegion(wcs)
    pixel_area = np.abs(
        (head["NAXIS1"] - 1) * head["CD1_1"] * (head["NAXIS2"] - 1) * head["CD2_2"]
    )

    assert np.allclose(sw.area, pixel_area, atol=1.0e-5)

    assert np.allclose(sw.sky_area(unit=u.deg**2), pixel_area * u.deg**2)
    assert np.allclose(
        sw.sky_area(unit=u.arcmin**2), (pixel_area * u.deg**2).to(u.arcmin**2)
    )


def test_patch():
    """
    Test patch function
    """
    from matplotlib.patches import PathPatch
    circ = SRegion("CIRCLE 5. 5. 1", ncircle=256)
    _ = patch_from_polygon(circ.shapely[0], fc="k")


def test_draw_patch():
    """
    Draw in matplotlib axis
    """
    import matplotlib.pyplot as plt

    fig, ax = plt.subplots(1, 1)
    ax.plot([-10, 10], [-10, 10])

    circ = SRegion("CIRCLE 5. 5. 1", ncircle=256)

    circ.add_patch_to_axis(ax, fc="r", alpha=0.5)

    plt.close("all")


def test_convex_hull():

    x = np.array([0, 0, 1, 1, 0.5])
    y = np.array([0, 1, 1, 0, 0.5])

    srh = SRegion(np.array([x, y]).T, get_convex_hull=True)
    assert srh.xy[0].ndim == 2
    assert srh.area[0] == 1.0


def test_padding():

    x = np.array([0, 0, 1, 1])
    y = np.array([0, 1, 1, 0])

    sr = SRegion(np.array([x, y]).T, pad=2)
    assert sr.area[0] == 4.0

    sr = SRegion(np.array([x, y]).T)
    sr.pad(scale=2, in_place=True)
    assert sr.area[0] == 4.0

    sr = SRegion(np.array([x, y]).T, wrap=False)
    sr2 = sr.pad(scale=2., in_place=False)
    assert sr2.area[0] == 4.0
    assert np.allclose(sr2.centroid, 0.5)

    sr = SRegion(np.array([x, y]).T, wrap=False)
    sr2 = sr.pad(scale=2., center=[0,0], in_place=False)
    assert sr2.area[0] == 4.0
    assert np.allclose(sr2.centroid, 1.0)
