#from __future__ import print_function

from pycrates import *
from region.cxcregion import *
import os
import numpy as np
import sys
import unittest
import warnings 

warnings.simplefilter("ignore", Warning)

class RegionTestCases(unittest.TestCase):
    

    TESTIN = os.getenv('TESTIN')
    if TESTIN[-1] != '/':
        TESTIN += '/'

    TESTOUT = os.getenv('TESTOUT')
    if TESTOUT[-1] != '/':
        TESTOUT += '/'


    def test_01(self):
        # Create empty Region and add a circle Shape and edit the radius, check inside region
        try:
            reg = CXCRegion()
        except:
            raise Exception("Unable to create an empty Region")

        reg._add_shape( "circle", 100, 300, 5, None, 1, 0 )
        if ( (len(reg) != 1) and ("circle(100,300,5)" not in str(reg)) ) :
            raise Exception("add_shape() to FAILED")

        a = reg
        b = reg.shapes[0]

        if (str(a) != str(b)):
            raise Exception("CXCRegion/Shape __str__() FAILED")

        if (str(type(a)) == str(type(b))):
            raise Exception("CXCRegion/Shape __repr__() FAILED")

        # Try to edit shape radius attribute
        try:
            b.radii = -100
        except AttributeError:
            pass

        try:
            b.radii[0] = -100
        except ValueError:
            pass
      
        newreg = reg.edit(0, stretch=10)
        if ( (len(newreg) != 1) and ("circle(100,300,50)" not in str(newreg)) ) :
            raise Exception("edit(stretch=10) FAILED")

        area = newreg.area()
        if ("7853.98163" not in str(area)):
            raise Exception("area() FAILED")
         
        areapix = newreg.area(pixel=True)
        if ("7845" not in str(areapix)):
            raise Exception("area(pixel=True) FAILED")
        
        areapix = newreg.area(bin=0.01, pixel=True)
        if ("7853.9667" not in str(areapix)):
            raise Exception("area(pixel=0.01) FAILED")

        areapix = newreg.area(bin=2.5, pixel=True)
        if ("7856.25" not in str(areapix)):
            raise Exception("area(pixel=2.5) FAILED")

        ext = newreg.extent()
        if ( (ext['x0'] != 50.0) or (ext['x1'] != 150.0) or (ext['y0'] != 250.0) or (ext['y1'] != 350.0) ):
            raise Exception("extent() FAILED")

        if (newreg.is_inside(0,0) is True):
            raise Exception("is_inside() is True FAILED")

        if (newreg.is_inside(0,0) == True):
            raise Exception("is_inside() == True FAILED")

        if (newreg.is_inside(100,300) is False):
            raise Exception("is_inside() is False FAILED")



    def test_02(self):

        # test read/write

        # bad input - ValueError: regParse() Could not parse region string or file
        try:
            reg = CXCRegion("Bad Input")
        except ValueError:
            pass

        # bad colname input - IOError: Column FOO does not exist
        try:
            reg = CXCRegion(self.TESTIN+"test_region/test_box.reg", colname="FOO")
        except IOError:
            pass

        # bad filename and colname input - IOError: File Bad Input does not exist
        try:
            reg = CXCRegion("Bad Input", colname="FOO")
        except IOError:
            pass

        # dss test
        reg = CXCRegion(self.TESTIN+"region/image_region.fits", colname="POS")
        if ("rectangle(0.5,0.5,9,9)*circle(4,4,5)" not in str(reg)):
            raise Exception("extract dss FAILED")
       
        #  bad colname input - IOError: Column SKY does not exist
        try:
            reg= CXCRegion(self.TESTIN+"region/image_region.fits", colname="SKY")
        except IOError:
            pass

        # bad component input - RuntimeError: Subspace component 2 does not exist
        try:
            reg= CXCRegion(self.TESTIN+"region/image_region.fits", colname="POS", component=2)
        except RuntimeError:
            pass

        # test reading in with various syntax
        reg = CXCRegion(self.TESTIN+'test_region/test_box.reg')
        if ("box(272.25227,254.03978,214.47941,80.106769)" not in str(reg)):
            raise Exception("CXCRegion(<file>) FAILED")

        reg = CXCRegion('bounds(circle(10,10,5))')
        if ("rectangle(5,5,15,15)" not in str(reg)):
            raise Exception("CXCRegion(bounds(<shape>)) FAILED")

        reg = CXCRegion('region('+self.TESTIN+'test_region/test_box.reg)')
        if ("box(272.25227,254.03978,214.47941,80.106769)" not in str(reg)):
            raise Exception(" CXCRegion(region(<file>)) FAILED")

        reg = CXCRegion('bounds(region('+self.TESTIN+'test_region/test_box.reg))')
        if ("rectangle(165.013,213.986,379.492,294.093)" not in str(reg)):
            raise Exception("CXCRegion(bounds(region(<file>))) FAILED")

        # write out, then read in; should be the same
        reg.write(self.TESTOUT+"pyregion/region.fits", clobber=True, newline=True)
        reg = CXCRegion(self.TESTOUT+"pyregion/region.fits")
        
        if ("rectangle(165.013,213.986,379.492,294.093)" not in str(reg)):
            raise Exception("read/write/read FAILED")

        
        bkgreg = circle(0,0,10) - circle(0,0,1)
        inner = bkgreg.shapes[1]
        srcreg = CXCRegion( inner )
        if ("!circle(0,0,1)" not in str(srcreg)):
            raise Exception("CXCRegion(<Shape>) FAILED")


    def test_03(self):
        # Test transform with apply and invert functions

        img = read_file(self.TESTIN+'test_pycrates/image.fits')
        wcs = img.get_transform("eqpos")
        
        reg = CXCRegion("circle(4274.5,3954.5,5)")
        newreg = reg.apply_transform(wcs.apply)
        
        newreg2 = newreg.apply_transform(wcs.invert)
        
        if ("4274.5,3954.5,5" not in str(newreg2) ):
            raise Exception("apply_transform() FAILED")



    def test_04(self):
        # Test edit, delete, get_shape_region (i.e. array slicing)

        reg2 = CXCRegion("circle(100,300,5)&box(100,300,200,400)")

        if ("circle(100,300,5)*box(100,300,200,400)" not in str(reg2)):
            raise Exception("Construct Region with two Shapes FAILED")

        newreg = reg2.edit(1, rotate=45, dy=100, dx=-90)
        if ("circle(100,300,5)*rotbox(10,400,200,400,45)" not in str(newreg)):
            raise Exception("Change x-axis, y-axis length and rotate FAILED")

        reg3 = reg2.get_shape_region(0)
        
        if ("circle(100,300,5)" not in str(reg3) ):
            raise Exception("get_shape_region() FAILED")

 
        try:
            reg2._delete_shape(None)
        except IndexError as ex:
            if "Index cannot be None" not in str(ex):
                raise Exception("delete_shape(None) FAILED")
        
        try:
            reg2._delete_shape(np.double(1))
        except TypeError as ex:
            if "Index must be an integer type" not in str(ex):
                raise Exception("delete_shape() w/ non-integer index FAILED")


        reg2._delete_shape(0)  
        if ("rotbox(10,400,200,400,45)" not in str(newreg)):
            raise Exception("delete_shape() FAILED")

        try:    
            sh = reg2.shapes[1]
        except IndexError as ex:
            if ("list index out of range" not in str(ex)):
                raise Exception("Bad input index FAILED")
            pass

        try:
            reg2._delete_shape(1)
        except IndexError as ex:
            if ("index is out of range" not in str(ex)):
                raise Exception("Delete non-existant Shape index FAILED")
            pass


        a=circle(10,10,1)
        b=a.edit(dx=10)
        c=a.edit(dy=10)
        z=a+b+c
        
        if str(z) != "circle(10,10,1)+circle(20,10,1)+circle(10,20,1)":
            raise Exception("Shape concat FAILED")

        if str(z[0]) != "circle(10,10,1)":
            raise Exception("get_shape_region(0) FAILED")
        
        if str(z[np.int64(1)]) != "circle(20,10,1)":
            raise Exception("get_shape_region(numpy.integer) FAILED")
        
        if str(z[0:2]) != "circle(10,10,1)+circle(20,10,1)":
            raise Exception("get_shape_region(0:2) FAILED")

        if str(z[3::-1]) != "circle(10,20,1)+circle(20,10,1)+circle(10,10,1)":
            raise Exception("get_shape_region(3::-1) FAILED")

        if str(z[0:3:2]) != "circle(10,10,1)+circle(10,20,1)":
            raise Exception("get_shape_region(0:3:2) FAILED")

        if str(z[0:5]) != "circle(10,10,1)+circle(20,10,1)+circle(10,20,1)":
            raise Exception("get_shape_region(0:5) FAILED")

        if str(z[1:0]) != "":
            raise Exception("get_shape_region() w/ start index > stop index FAILED" )

        if str(z[4:6]) != "":
            raise Exception("get_shape_region() w/ index out of range FAILED" )

        try:
            print (z[4])
        except IndexError as ex:
            if "list index out of range" not in (str(ex)):
                raise Exception("get_shape_region() w/ bad index FAILED")

        try:
            print (z[2.0])
        except Exception as ex:
            if "Index/indices must be an integer type" not in (str(ex)):
                raise Exception("get_shape_region() w/ float index FAILED")

        try:
            print (z[1.0:2.0])
        except Exception as ex:
            if "Slice start must be an integer type." not in (str(ex)):
                raise Exception("get_shape_region() w/ non-integer start FAILED")
        try:
            print (z[0:2.0])
        except Exception as ex:
            if "Slice stop must be an integer type." not in (str(ex)):
                raise Exception("get_shape_region() w/ non-integer stop FAILED")

        try:
            print (z[0:3:1.0])
        except Exception as ex:
            if "Slice step must be an integer type." not in (str(ex)):
                raise Exception("get_shape_region() w/ non-integer step FAILED")

        try:
            print (z[None])
        except Exception as ex:
            if "Index cannot be None" not in (str(ex)):
                raise Exception("get_shape_region() w/ None index FAILED")


        srcs = circle(10,10,10)+circle(20,20,20)
        excld = field()-srcs
        if str(excld) != "field()*!circle(10,10,10)*!circle(20,20,20)":
            raise Exception("Exclude shapes FAILED")

        slce = excld[1:]
        if str(slce) != "!circle(10,10,10)*!circle(20,20,20)":
             raise Exception("Slicing excluded shapes FAILED")


    def test_05(self):
        # Test shapeReg inits

        if ("annulus(1,2,3,4)" not in str(annulus(1,2,3,4)) ):
            raise Exception("annulus() FAILED")

        try:
            reg = annulus(1,2,-3,4)
        except ValueError as ex:
            if ("radius of annulus must be positive" not in str(ex)):
                raise Exception("annulus w/ bad input values FAILED")


        if ("box(1,2,3,4)" not in str(box(1,2,3,4)) ) :
            raise Exception("box() FAILED")

        try:
            reg = box(1,2,3,-4)
        except ValueError as ex:
            if ("length of box must be positive" not in str(ex)):
                raise Exception("box w/ bad input values FAILED")


        if ("rotbox(1,2,3,4,5)" not in str(box(1,2,3,4,5)) ) :
            raise Exception("box() ==> rotbox() FAILED")

        try:
            reg = rotbox(1,2,-3,4,5)
        except ValueError as ex:
            if ("length of rotbox must be positive" not in str(ex)):
                raise Exception("rotbox w/ bad input values FAILED")


        if ("circle(1,2,3)" not in str(circle(1,2,3)) ) :
            raise Exception("circle() FAILED")

        try:
            reg = circle(1,2,-3)
        except ValueError as ex:
            if ("Radius of circle must be positive" not in str(ex)):
                raise Exception("circle w/ bad input values FAILED")


        if ("ellipse(1,2,3,4,5)" not in str(ellipse(1,2,3,4,5)) ):
            raise Exception("ellipse() FAILED")

        try:
            reg = ellipse(1,2,-3,4,5)
        except ValueError as ex:
            if ("Semi-major axis of ellipse must be positive" not in str(ex)):
                raise Exception("ellipse w/ bad input values FAILED")

        if ("ellipse(1,2,3,4,0)" not in str(ellipse(1,2,3,4)) ):
            raise Exception("ellipse() FAILED")

        try:
            reg = ellipse(1,2,3,-4,0)
        except ValueError as ex:
            if ("Semi-minor axis of ellipse must be positive" not in str(ex)):
                raise Exception("ellipse w/ bad input values FAILED")


        if ("field()" not in str(field()) ):
            raise Exception("field() FAILED")

        if ("pie(1,2,3,4,5,6)" not in str(pie(1,2,3,4,5,6)) ):
            raise Exception("pie() FAILED")

        try:
            reg = pie(1,2,3,-4,5,6)
        except ValueError as ex:
            if ("radius of pie must be positive" not in str(ex)):
                raise Exception("pie w/ bad input values FAILED")

        if ("point(1,2)" not in str(point(1,2)) ):
            raise Exception("point() FAILED")

        if ("polygon(1,2,3,4,5,6,1,2)" not in str(polygon(1,2,3,4,5,6)) ):
            raise Exception("polygon() FAILED")

        if ("polygon(1,4,2,5,3,6,1,4)" not in str(polygon( [1,2,3],[4,5,6])) ):
            raise Exception("polygon() FAILED")

        try: 
            reg= polygon(1,4,2,5,3,6,1)
        except RuntimeError as ex:
            if ("Polygon must have equal number" not in str(ex)):
                raise Exception("polygon with uneven pairs FAILED")

        try: 
            reg= polygon(1,4,2,5)
        except RuntimeError as ex:
            if ("At least 3 points are needed to make a Polygon" not in str(ex)):
                raise Exception("polygon with < 3 points FAILED")

        if ("rectangle(1,2,3,4)" not in str(rectangle(1,2,3,4)) ):
            raise Exception("rectangle() FAILED")

        #if ("rotrectangle(1,2,3,4,6)" not in str(rectangle(1,2,3,4,6)) ):
        #    raise Exception("rectangle() ==> rotrectangle()  FAILED")

        #if ("rotrectangle(1,2,3,4,5)" not in str(rotrectangle(1,2,3,4,5)) ):
        #    raise Exception("rotrectangle() FAILED")

        if ("sector(1,2,3,4)" not in str(sector(1,2,3,4)) ):
            raise Exception("sector() FAILED")


    def test_06(self):
        # Test union and intersect combo operators

        if ("circle(10,10,123)+box(10,10,1,1)" not in str(circle(10,10,123)+box(10,10,1,1)) ):
            raise Exception("circle+box FAILED")

        if ("circle(10,10,100)*box(10,10,1,1)" not in str(circle(10,10,100)*box(10,10,1,1)) ):
            raise Exception("circle*box FAILED")

        if ("circle(10,10,100)*!box(10,10,1,1)" not in str(circle(10,10,100)-box(10,10,1,1)) ):
            raise Exception("circle-box FAILED")

        if ("!circle(50,50,100)" not in str(-circle(50,50,100))):
            raise Exception("-circle FAILED")
  
        if ("circle(10,10,123)+box(10,10,1,1)" not in str( circle(10,10,123)|box(10,10,1,1))):
            raise Exception("circle|box FAILED")

        if ("circle(10,10,123)*!box(10,10,1,1)+box(10,10,1,1)*!circle(10,10,123)" not in str(circle(10,10,123)^box(10,10,1,1))):
            raise Exception("circle^box FAILED")



    def test_07(self):
        # Test copy, is, and equality operators
        a = pie(0,0, 1, 2, -45, 56)
        d = pie(0,0, 1, 2, -45, 56)
        b = a.__copy__()

        if ( (a is b) is True ):
            raise Exception("'is' operator FAILED")

        if ( (a == b) is False ):
            raise Exception("'==' operator FAILED")

        if ( (a == d) is False ):
            raise Exception("'==' operator FAILED")



    def test_08(self):
        # Test add, mult, sub operators

        a = circle(1000,1000,50) 
        b = box(1010,1010,50,100)
        c = a+b
        
        if ("circle(1000,1000,50)+box(1010,1010,50,100)" not in str(c)):
            raise Exception("a+b operation FAILED")

        if ("8574" not in str(c.area()) ):
            raise Exception("area() of two Shapes FAILED")
            
        if ( "0" not in str(c.index(a)) ):
            raise Exception("index() FAILED")

        if ( "1" not in str(c.index(b)) ):
            raise Exception("index() FAILED")
        
        if ( (a in c) is not True ):
            raise Exception("a in c FAILED")

        if ( (b in c) is not True ):
            raise Exception("b in c FAILED")

        if ( (c in b) is not False ):
            raise Exception("c in b FAILED")


        if ( (c in a) is not True ):
            raise Exception("c in a FAILED")
        
        d = a*b
        if ("circle(1000,1000,50)*box(1010,1010,50,100)" not in str(d)):
            raise Exception("a*b operation FAILED")
        
        e = a-b
        if ("circle(1000,1000,50)*!box(1010,1010,50,100)" not in str(e)):
            raise Exception("a-b operation FAILED")

        p = point(0,0)*point(1,1)
        if (p is not None) and (len(p) != 0):
            raise Exception("zero-length region FAILED")

        
    def test_09(self):
        #Test edit() method on compound region

        reg = CXCRegion("circle(1,1,10)*circle(2,2,10)*box(3,3,10,10)+circle(20,20,10)*ellipse(21,21,20,10,30)")
        if ("circle(1,1,10)*circle(2,2,10)*box(3,3,10,10)+circle(20,20,10)*ellipse(21,21,20,10,30)" not in str(reg)  ):
           raise Exception("compound region FAILED")

        try:
            reg.edit()
        except:
            raise Exception("edit without input FAILED")


        c = CXCRegion(reg.shapes[0])

        newcreg = c.edit(stretch=5)
        if ("circle(1,1,50)" not in str(newcreg)):
            raise Exception("stretch=5 FAILED")

        newcreg = c.edit(stretch=0.5)
        if ("circle(1,1,5)" not in str(newcreg)):
            raise Exception("stretch=0.5 FAILED")

        newcreg = c.edit(stretch=-10)
        if ("circle(1,1,10)" not in str(newcreg)):
            raise Exception("stretch=-10 FAILED")

        newcreg = c.edit(pad=-20)
        if ("circle(1,1,10)" not in str(newcreg)):
            raise Exception("pad=-20 FAILED")

        newreg = reg.edit(index=1, dx=1, dy=-0.3)
        if ("circle(1,1,10)*circle(3,1.7,10)*box(3,3,10,10)+circle(20,20,10)*ellipse(21,21,20,10,30)" not in str(newreg)):
            raise Exception("edit single shape FAILED")

        newreg = newreg.edit(dx=1, dy=-0.7)
        
        if ("circle(2,0.3,10)*circle(4,1,10)*box(4,2.3,10,10)+circle(21,19.3,10)*ellipse(22,20.3,20,10,30)" not in str(newreg)):
            raise Exception("edit all shapes FAILED")

        newreg = newreg.edit(3,stretch=10)
        if ("circle(2,0.3,10)*circle(4,1,10)*box(4,2.3,10,10)+circle(21,19.3,100)*ellipse(22,20.3,20,10,30)" not in str(newreg)):
            raise Exception("stretch 1 shape FAILED")

        newreg = newreg.edit(2, rotate=45)
        if ("circle(2,0.3,10)*circle(4,1,10)*rotbox(4,2.3,10,10,45)+circle(21,19.3,100)*ellipse(22,20.3,20,10,30)" not in str(newreg)):
            raise Exception("rotate box shape FAILED")

        newreg = newreg.edit(0, rotate=45)
        if ("circle(2,0.3,10)*circle(4,1,10)*rotbox(4,2.3,10,10,45)+circle(21,19.3,100)*ellipse(22,20.3,20,10,30)" not in str(newreg)):
            raise Exception("rotate circle shape FAILED")

        newreg = newreg.edit(3, pad=-85)
        if ("circle(2,0.3,10)*circle(4,1,10)*rotbox(4,2.3,10,10,45)+circle(21,19.3,15)*ellipse(22,20.3,20,10,30)" not in str(newreg)):
            raise Exception("pad shape FAILED")

        newreg = newreg.edit(rotate=-30)
        if ("circle(2,0.3,10)*circle(4,1,10)*rotbox(4,2.3,10,10,15)+circle(21,19.3,15)*ellipse(22,20.3,20,10,0)" not in str(newreg)):
            raise Exception("rotate all shapes FAILED")


        b = CXCRegion("rectangle(0,0,25,25)")
        c = b*newreg
        if ("rectangle(0,0,25,25)*circle(2,0.3,10)*circle(4,1,10)*rotbox(4,2.3,10,10,15)+rectangle(0,0,25,25)*circle(21,19.3,15)*ellipse(22,20.3,20,10,0)" not in str(c)):
            raise Exception("intersect with new region FAILED")

        


if __name__ == '__main__':
    unittest.main(verbosity=1)

