from history.ascfits import ASCFITS
import unittest
import warnings 

warnings.simplefilter("ignore", Warning)

class TestASCFITS(unittest.TestCase):

    cards = ['HISTORY  TOOL  :dmhedit  2016-08-11T20:43:33                            ASC12345',  # valid TOOL card
             'HISTORY  PARM  :infile=myfile.fits                                      ASC12346',  # valid PARM card
             'HISTORY  PARM  :stdlev1={d:time,s:ccd_id,s:node_id,i:expno,s:chip,s:tdetASC12347',  # valid PARM card - full content
             'HISTORY  CONT  :,f:det,f:sky,s:phas,l:pha,f:energy,l:pi,s:fltgrade,s:graASC12348',  # valid CONT card - full content
             'HISTORY  STCK  :/project/data/ap/sdp/process_run/tmp/ACIS_F_L1_086557597ASC12349',  # valid STCK card - full content
             'HISTORY  CONT  :n455/ascf004323913N032.fits                             ASC12350',  # valid CONT card
             'HISTORY  TOOL  :mytool  Version 1.0                                     ASC12346',  # 
             'HISTORY--TOOL  :mytool  Version 1.0                                     ASC12346',  # char[8:9] != '  '
             'HISTORY  JUNK  :mytool  Version 1.0                                     ASC12346',  # char[10:13] != accepted option
             'HISTORY  TOOL--:mytool  Version 1.0                                     ASC12346',  # char[14:16] != '  :'
             'HISTORY  TOOL  *mytool  Version 1.0                                     ASC12346',  # char[14:16] != '  :'
             'HISTORY  TOOL  :mytool  Version 1.0                                     ABC12346',  # char[73:75] != 'ASC'
             'HISTORY  TOOL  :mytool  Version 1.0                                     ASCDEFGH',  # char[76:80] != numeric
             'HISTORY--TOOL:we ran mytool infile=myfile.fits outfile=results.fits dat=20160811',  # wacky record with good critical elements
             "OBJECT  = 'XRT/PSPC PSF AR LAC' / name of object                                ",  # non-history card
             'HISTORY  TOOL  :mytool  Version 1.0  ASC12346',                                     # too short
             'HISTORY  TOOL  :mytool  Version 1.0                                             ASC12346',  # too long
             'HISTORY  TOOL  :mytool  Version 1.0                                     ASC1234600',# extra numeric content
             'HISTORY  TOOL  :mytool  Version 1.0                                     ASC12346XX',# extra text content
             'HISTORY  PARM  :PIXLIB Version: 4.0.0                                   ASC00015',  # valid PIXLIB head card
             'HISTORY  PARM  :ARDLIB Version: 0.7.1-4                                 ASC00028',  # valid ARDLIB head card
             'HISTORY  TOOL  :                                                        ASC99999',  # valid head record, empty content
             'HISTORY  TOOL  :mytool                                                  ASC66666',  # valid head record, missing version
             ]

    def test_01(self):
        # Test is_compliant( card, strict=True )

        ascfits = ASCFITS()

        # Non-string input
        try:
            result = ascfits.is_compliant( 7 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        ii = 0
        expected = [True,True,True,True,True,True,True,False,False,False,False,False,False,False,False,False,False,False,False,True,True,True,True]
        for item in self.cards:
            try:
                result = ascfits.is_compliant( card=item, strict=True )
            except Exception as ex:
                print( ex.__class__.__name__ + ": " + str(ex))
                raise Exception("Error: unexpected exception thrown")

            #print( item + " => " + str(result) )
            self.assertEqual( result, expected[ii] )
            ii += 1


    def test_02(self):
        # Test is_compliant( card, strict=False )

        ascfits = ASCFITS()

        ii = 0
        expected = [True,True,True,True,True,True,True,True,False,True,True,True,False,True,False,False,False,False,False,True,True,True,True]
        for item in self.cards:
            try:
                result = ascfits.is_compliant( card=item, strict=False )
            except Exception as ex:
                print( ex.__class__.__name__ + ": " + str(ex))
                raise Exception("Error: unexpected exception thrown")

            #print( item + " => " + str(result) )
            self.assertEqual( result, expected[ii] )
            ii += 1


    def test_03(self):
        # Test label()

        ascfits = ASCFITS()

        try:
            result = ascfits.label( 7 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        ii = 0
        expected = ["TOOL","PARM","PARM","CONT","STCK","CONT","TOOL","PARM","PARM","TOOL","TOOL"]
        for item in self.cards:
            if ascfits.is_compliant( card=item, strict=True ):
                try:
                    result = ascfits.label( item )
                except Exception as ex:
                    print( ex.__class__.__name__ + ": " + str(ex))
                    raise Exception("Error: unexpected exception thrown")

                #print( item + " => " + str(result) )
                self.assertEqual( result, expected[ii] )
                ii += 1


    def test_04(self):
        # Test body()

        ascfits = ASCFITS()

        try:
            result = ascfits.body( 7 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        ii = 0
        expected = ['dmhedit  2016-08-11T20:43:33                            ',
                    'infile=myfile.fits                                      ',
                    'stdlev1={d:time,s:ccd_id,s:node_id,i:expno,s:chip,s:tdet',
                    ',f:det,f:sky,s:phas,l:pha,f:energy,l:pi,s:fltgrade,s:gra',
                    '/project/data/ap/sdp/process_run/tmp/ACIS_F_L1_086557597',
                    'n455/ascf004323913N032.fits                             ',
                    'mytool  Version 1.0                                     ',
                    'PIXLIB Version: 4.0.0                                   ',
                    'ARDLIB Version: 0.7.1-4                                 ',
                    '                                                        ',
                    'mytool                                                  ',
                    ]
        for item in self.cards:
            if ascfits.is_compliant( card=item, strict=True ):
                try:
                    result = ascfits.body( item )
                except Exception as ex:
                    print( ex.__class__.__name__ + ": " + str(ex))
                    raise Exception("Error: unexpected exception thrown")

                self.assertEqual( result, expected[ii] )
                ii += 1


    def test_05(self):
        # Test seqno()

        ascfits = ASCFITS()

        try:
            result = ascfits.seqno( 7 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        ii = 0
        expected = [12345,
                    12346,
                    12347,
                    12348,
                    12349,
                    12350,
                    12346,
                    15,
                    28,
                    99999,
                    66666,
                    ]
        for item in self.cards:
            if ascfits.is_compliant( card=item, strict=True ):
                try:
                    result = ascfits.seqno( item )
                except Exception as ex:
                    print( ex.__class__.__name__ + ": " + str(ex))
                    raise Exception("Error: unexpected exception thrown")

                self.assertEqual( result, expected[ii] )
                ii += 1

    def test_06(self):
        # Test tool()

        ascfits = ASCFITS()

        try:
            result = ascfits.tool( 7 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        ii = 0
        expected = ["dmhedit",None,None,None,None,None,"mytool","PIXLIB","ARDLIB","","mytool"]
        for item in self.cards:
            if ascfits.is_compliant( card=item, strict=True ):
                try:
                    result = ascfits.tool( item )
                except Exception as ex:
                    print( ex.__class__.__name__ + ": " + str(ex))
                    raise Exception("Error: unexpected exception thrown")

                self.assertEqual( result, expected[ii] )
                ii += 1

    def test_07(self):
        # Test version()

        ascfits = ASCFITS()

        try:
            result = ascfits.version( 7 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        ii = 0
        expected = ["2016-08-11T20:43:33",
                    None,
                    None,
                    None,
                    None,
                    None,
                    "Version 1.0",
                    "Version: 4.0.0",
                    "Version: 0.7.1-4",
                    "",
                    "",
                    ]
        for item in self.cards:
            if ascfits.is_compliant( card=item, strict=True ):
                try:
                    result = ascfits.version( item )
                except Exception as ex:
                    print( ex.__class__.__name__ + ": " + str(ex))
                    raise Exception("Error: unexpected exception thrown")

                self.assertEqual( result, expected[ii] )
                ii += 1

    def test_08(self):
        # Test is_head()

        ascfits = ASCFITS()

        try:
            result = ascfits.is_head( 7 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        ii = 0
        expected = [True,
                    False,
                    False,
                    False,
                    False,
                    False,
                    True,
                    True,
                    True,
                    True,
                    True,
                    ]
        for item in self.cards:
            if ascfits.is_compliant( card=item, strict=True ):
                try:
                    result = ascfits.is_head( item )
                except Exception as ex:
                    print( ex.__class__.__name__ + ": " + str(ex))
                    raise Exception("Error: unexpected exception thrown")

                self.assertEqual( result, expected[ii] )
                ii += 1


    def test_09(self):
        # Test as_card()

        ascfits = ASCFITS()

        # Argument vetting
        # 
        # 'label' argument - type
        try:
            result = ascfits.as_card( 7 ,"random body content", 100 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        # 'label' argument - value
        try:
            result = ascfits.as_card("JUNK","random body content", 100 )
        except ValueError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        # 'body' argument - type
        try:
            result = ascfits.as_card( "PARM" ,None, 100 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        # 'seqno' argument - type
        try:
            result = ascfits.as_card( "PARM" ,"random body content", 38.448 )
        except TypeError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        # 'seqno' argument - value
        try:
            result = ascfits.as_card( "PARM" ,"random body content", -100 )
        except ValueError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        # 'seqno' argument - value
        try:
            result = ascfits.as_card( "PARM" ,"random body content", 100000 )
        except ValueError:
            pass
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: expected exception not thrown")
        else:
            raise Exception("Error: No exception thrown for bad input.")

        # 'body' argument - value >56 chars
        try:
            body = "/project/data/ap/sdp/process_run/tmp/ACIS_F_L1_086557597n455/ascf004323913N032.fits"
            result = ascfits.as_card( "STCK" , body, 123, with_tag=True )
        except Exception as ex:
            print( ex.__class__.__name__ + ": " + str(ex))
            raise Exception("Error: unexpected exception thrown")
        expected = "HISTORY  STCK  :/project/data/ap/sdp/process_run/tmp/ACIS_F_L1_086557597ASC00123"
        self.assertEqual( result, expected )

        ii = 0
        for item in self.cards:
            if ascfits.is_compliant( card=item, strict=True ):
                label = ascfits.label( item )
                body  = ascfits.body( item )
                seqno = ascfits.seqno( item )

                result = ascfits.as_card( label, body, seqno, with_tag=True  )

                # Should get same output
                self.assertEqual( result, item )
                ii += 1


    def test_10(self):
        # Test as_cards()

        ascfits = ASCFITS()

        # Argument vetting tests 
        # + 'tool' with whitespace only content == ValueError
        # + param list entry with <3 dimensions [[name,value]] == ValueError
        # + param list entry with non-string content (anywhere) == ValueError

        ii = 0
        expected = ["HISTORY  TOOL  :mytool  Version 1.0.0                                   ASC99997",
                    "HISTORY  PARM  :infile=@/data/in/infiles.lis                            ASC99998",
                    "HISTORY  STCK  :/data/in/file1.fits                                     ASC99999",
                    "HISTORY  STCK  :/data/regression/test/development/2019-01-12/mytool/fileASC00001",
                    "HISTORY  CONT  :2.fits                                                  ASC00002",
                    "HISTORY  STCK  :/data/regression/test/development/201/mytool/file13.fitsASC00003",
                    "HISTORY  PARM  :outfile=/data/out/results.fits                          ASC00004",
                    "HISTORY  PARM  :stdlev1={d:time,s:ccd_id,s:node_id,i:expno,s:chip,s:tdetASC00005",
                    "HISTORY  CONT  :,f:det,f:sky,s:phas,l:pha,f:energy,l:pi,s:fltgrade}     ASC00006",
                    "HISTORY  PARM  :cl+                                                     ASC00007",
                   ]

        tool    = "mytool"
        version = "Version 1.0.0"
        instack = ["/data/in/file1.fits",
                   "/data/regression/test/development/2019-01-12/mytool/file2.fits",
                   "/data/regression/test/development/201/mytool/file13.fits",
                  ]
        params  = [["infile","@/data/in/infiles.lis", instack ],
                   ["outfile","/data/out/results.fits", None ],
                   ["stdlev1","{d:time,s:ccd_id,s:node_id,i:expno,s:chip,s:tdet,f:det,f:sky,s:phas,l:pha,f:energy,l:pi,s:fltgrade}",None],
                   ["cl+",None, None ],
                  ]
        seqno   = 99997

        result = ascfits.as_cards( tool, version, params, seqno, with_tag=True  )
        for card in result:
            self.assertEqual( card, expected[ii] )
            ii += 1


    def test_11(self):
        # Test as_cards() with PIXLIB record content

        ascfits = ASCFITS()

        ii = 0
        expected = ["HISTORY  PARM  :PIXLIB Version: 4.0.0                                   ASC00015",
                    "HISTORY  PARM  :******     PIXLIB Parameter File      *****             ASC00016",
                    "HISTORY  PARM  :/home/ascds/DS.l3/param/geom.par:                       ASC00017",
                    "HISTORY  PARM  :instruments  = /export/CALDB/level3/data/chandra/defaultASC00018",
                    "HISTORY  CONT  :/geom/telD1999-07-23geomN0006.fits(CALDB)               ASC00019",
                    "HISTORY  PARM  :aimpoints    = /export/CALDB/level3/data/chandra/defaultASC00020",
                    "HISTORY  CONT  :/aimpts/telD1999-07-23aimptsN0002.fits(CALDB)           ASC00021",
                    "HISTORY  PARM  :tdet         = /export/CALDB/level3/data/chandra/defaultASC00022",
                    "HISTORY  CONT  :/tdet/telD1999-07-23tdetN0001.fits(CALDB)               ASC00023",
                    "HISTORY  PARM  :sky          = /export/CALDB/level3/data/chandra/defaultASC00024",
                    "HISTORY  CONT  :/sky/telD1999-07-23skyN0002.fits(CALDB)                 ASC00025",
                    "HISTORY  PARM  :shell        = /export/CALDB/level3/data/chandra/defaultASC00026",
                    "HISTORY  CONT  :/sgeom/telD1999-07-23sgeomN0001.fits(CALDB)             ASC00027",
                    "HISTORY  PARM  :obsfile      = nowT00:00:00()                           ASC00028"]

        tool    = "PIXLIB"
        version = "Version: 4.0.0"
        params  = [["******     PIXLIB Parameter File      *****             ", None, None ],
                   ["/home/ascds/DS.l3/param/geom.par:", None, None ],
                   ["instruments  ", " /export/CALDB/level3/data/chandra/default/geom/telD1999-07-23geomN0006.fits(CALDB)", None, None ],
                   ["aimpoints    ", " /export/CALDB/level3/data/chandra/default/aimpts/telD1999-07-23aimptsN0002.fits(CALDB)", None, None ],
                   ["tdet         ", " /export/CALDB/level3/data/chandra/default/tdet/telD1999-07-23tdetN0001.fits(CALDB)", None, None ],
                   ["sky          ", " /export/CALDB/level3/data/chandra/default/sky/telD1999-07-23skyN0002.fits(CALDB)", None, None ],
                   ["shell        ", " /export/CALDB/level3/data/chandra/default/sgeom/telD1999-07-23sgeomN0001.fits(CALDB)", None, None ],
                   ["obsfile      ", " nowT00:00:00()", None, None ],
                  ]
        seqno   = 15

        result = ascfits.as_cards( tool, version, params, seqno, with_tag=True  )
        for card in result:
            self.assertEqual( card, expected[ii] )
            ii += 1


    def test_12(self):
        # Test as_cards() with ARDLIB record content

        ascfits = ASCFITS()

        ii = 0
        expected = ["HISTORY  PARM  :ARDLIB Version: 0.7.1-4                                 ASC00028",
                    "HISTORY  PARM  :File used for CALDB info: /state/partition1/tmp.ascdsl3/ASC00029",
                    "HISTORY  CONT  :L3ASRC____338268443n247/output/acisf01878_000N001_r0100_ASC00030",
                    "HISTORY  CONT  :wmap3.fits                                              ASC00031",
                    "HISTORY  PARM  :Note: CTI_APP = PPPPPBPBPP                              ASC00032",
                    "HISTORY  PARM  :Parameter file: /state/partition1/tmp.ascdsl3/L3ASRC____ASC00033",
                    "HISTORY  CONT  :338268443n247/output/acisf01878_000N001_r0100_ardlib3.paASC00034",
                    "HISTORY  CONT  :r                                                       ASC00035",
                    "HISTORY  PARM  :Vignetting File: /state/partition1/local/install.CAT3.0/ASC00036",
                    "HISTORY  CONT  :CALDB/data/chandra/tel/hrma/bcf/vignet/hrmaD1996-12-20viASC00037",
                    "HISTORY  CONT  :gnetN0003.fits[AXAF_VIGNET1]                            ASC00038",
                    "HISTORY  PARM  :Onaxis Area File: /state/partition1/local/install.CAT3.0ASC00039",
                    "HISTORY  CONT  :/CALDB/data/chandra/tel/hrma/bcf/effarea/hrmaD1996-12-20ASC00040",
                    "HISTORY  CONT  :axeffaN0007.fits[AXAF_AXEFFA1]                          ASC00041",
                    "HISTORY  PARM  :ACIS QE File: /state/partition1/local/install.CAT3.0/CALASC00042",
                    "HISTORY  CONT  :DB/data/chandra/acis/bcf/qe/acisD1997-04-17qeN0006.fits[ASC00043",
                    "HISTORY  CONT  :AXAF_QE3]                                               ASC00044",
                    "HISTORY  PARM  :ACIS Uniformity File: /state/partition1/local/install.CAASC00045",
                    "HISTORY  CONT  :T3.0/CALDB/data/chandra/acis/bcf/qe/acisD2000-01-29qeuN0ASC00046",
                    "HISTORY  CONT  :006.fits[AXAF_QEU3]                                     ASC00047",
                    "HISTORY  PARM  :ACIS Contamination File: /state/partition1/local/installASC00048",
                    "HISTORY  CONT  :.CAT3.0/CALDB/data/chandra/acis/bcf/contam/acisD1999-08-ASC00049",
                    "HISTORY  CONT  :13contamN0004.fits[AXAF_CONTAM3]                        ASC00050",
                    "HISTORY  PARM  :ACIS 2 Badpix File: /state/partition1/tmp.ascdsl3/L3ASRCASC00051",
                    "HISTORY  CONT  :____338268443n247/input/acisf01878_000N001_bpix3.fits[BAASC00052",
                    "HISTORY  CONT  :DPIX2]                                                  ASC00053",
                    "HISTORY  PARM  :File used for CALDB info: /state/partition1/tmp.ascdsl3/ASC00054",
                    "HISTORY  CONT  :L3ASRC____338268443n247/output/acisf01878_000N001_r0100_ASC00055",
                    "HISTORY  CONT  :wmap3.fits                                              ASC00056",
                    "HISTORY  PARM  :Note: CTI_APP = PPPPPBPBPP                              ASC00057",
                    "HISTORY  PARM  :File used for CALDB info: /state/partition1/tmp.ascdsl3/ASC00058",
                    "HISTORY  CONT  :L3ASRC____338268443n247/output/acisf01878_000N001_r0100_ASC00059",
                    "HISTORY  CONT  :wmap3.fits                                              ASC00060",
                    "HISTORY  PARM  :Note: CTI_APP = PPPPPBPBPP                              ASC00061"]
        
        tool    = "ARDLIB"
        version = "Version: 0.7.1-4"
        params = [["File used for CALDB info: /state/partition1/tmp.ascdsl3/L3ASRC____338268443n247/output/acisf01878_000N001_r0100_wmap3.fits",None,None],
                  ["Note: CTI_APP = PPPPPBPBPP",None,None],
                  ["Parameter file: /state/partition1/tmp.ascdsl3/L3ASRC____338268443n247/output/acisf01878_000N001_r0100_ardlib3.par",None,None],
                  ["Vignetting File: /state/partition1/local/install.CAT3.0/CALDB/data/chandra/tel/hrma/bcf/vignet/hrmaD1996-12-20vignetN0003.fits[AXAF_VIGNET1]",None,None],
                  ["Onaxis Area File: /state/partition1/local/install.CAT3.0/CALDB/data/chandra/tel/hrma/bcf/effarea/hrmaD1996-12-20axeffaN0007.fits[AXAF_AXEFFA1]",None,None],
                  ["ACIS QE File: /state/partition1/local/install.CAT3.0/CALDB/data/chandra/acis/bcf/qe/acisD1997-04-17qeN0006.fits[AXAF_QE3]",None,None],
                  ["ACIS Uniformity File: /state/partition1/local/install.CAT3.0/CALDB/data/chandra/acis/bcf/qe/acisD2000-01-29qeuN0006.fits[AXAF_QEU3]",None,None],
                  ["ACIS Contamination File: /state/partition1/local/install.CAT3.0/CALDB/data/chandra/acis/bcf/contam/acisD1999-08-13contamN0004.fits[AXAF_CONTAM3]",None,None],
                  ["ACIS 2 Badpix File: /state/partition1/tmp.ascdsl3/L3ASRC____338268443n247/input/acisf01878_000N001_bpix3.fits[BADPIX2]",None,None],
                  ["File used for CALDB info: /state/partition1/tmp.ascdsl3/L3ASRC____338268443n247/output/acisf01878_000N001_r0100_wmap3.fits",None,None],
                  ["Note: CTI_APP = PPPPPBPBPP",None,None],
                  ["File used for CALDB info: /state/partition1/tmp.ascdsl3/L3ASRC____338268443n247/output/acisf01878_000N001_r0100_wmap3.fits",None,None],
                  ["Note: CTI_APP = PPPPPBPBPP",None,None],
                 ]
        seqno   = 28

        result = ascfits.as_cards( tool, version, params, seqno, with_tag=True  )
        for card in result:
            self.assertEqual( card, expected[ii] )
            ii += 1


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