# Source code for drizzlepac.outputimage

    """
    This module manages the creation of the output image FITS file.

    :Authors: Warren Hack

    :License: :doc:`LICENSE`

    """
    from __future__ import absolute_import, division, print_function # confidence medium

    from astropy.io import fits
    from stsci.tools import fileutil, readgeis, logutil

    from . import wcs_functions
    from . import version

    from fitsblender import blendheaders

    yes = True
    no = False

    RESERVED_KEYS = ['NAXIS','BITPIX','DATE','IRAF-TLM','XTENSION','EXTNAME','EXTVER']

    EXTLIST = ('SCI', 'WHT', 'CTX')

    WCS_KEYWORDS=['CD1_1','CD1_2', 'CD2_1', 'CD2_2', 'CRPIX1',
    'CRPIX2','CRVAL1', 'CRVAL2', 'CTYPE1', 'CTYPE2','WCSNAME']

    # fits.CompImageHDU() crashes with default arguments.
    # Instead check that fits module has *attribute* 'CompImageHDU':
    PYFITS_COMPRESSION = hasattr(fits, 'CompImageHDU')

    # Set up dictionary of default keywords to be written out to the header
    # of the output drizzle image using writeDrizKeywords()
    DRIZ_KEYWORDS = {
                    'VER':{'value':"",'comment':'Drizzle, task version'},
                    'GEOM':{'value':"wcs",'comment':'Drizzle, source of geometric information'},
                    'DATA':{'value':"",'comment':'Drizzle, input data image'},
                    'DEXP':{'value':"",'comment':'Drizzle, input image exposure time (s)'},
                    'OUDA':{'value':"",'comment':'Drizzle, output data image'},
                    'OUWE':{'value':"",'comment':'Drizzle, output weighting image'},
                    'OUCO':{'value':"",'comment':'Drizzle, output context image'},
                    'MASK':{'value':"",'comment':'Drizzle, input weighting image'},
                    'WTSC':{'value':"",'comment':'Drizzle, weighting factor for input image'},
                    'KERN':{'value':"",'comment':'Drizzle, form of weight distribution kernel'},
                    'PIXF':{'value':"1.0",'comment':'Drizzle, linear size of drop'},
                    'COEF':{'value':"SIP",'comment':'Drizzle, source of coefficients'},
                    'OUUN':{'value':"cps",'comment':'Drizzle, units of output image - counts or cps'},
                    'FVAL':{'value':"INDEF",'comment':'Drizzle, fill value for zero weight output pix'},
                    'WKEY':{'value':"",'comment':'Input image WCS Version used'}
                    }

    log = logutil.create_logger(__name__, level=logutil.logging.NOTSET)

    class OutputImage:
        """
        This class manages the creation of the array objects
        which will be used by Drizzle. The three arrays, SCI/WHT/CTX,
        will be setup either as extensions in a
        single multi-extension FITS file, or as separate FITS
        files.
        """
        def __init__(self, plist, input_pars, build=yes, wcs=None, single=no, blot=no):
            """
            The object 'plist' must contain at least the following members::

                plist['output']   - name of output FITS image (for SCI)
                plist['outnx']    - size of X axis for output array
                plist['outny']    - size of Y axis for output array

            If 'single=yes', then 'plist' also needs to contain::

                plist['outsingle']
                plist['outsweight']
                plist['outscontext']

            If 'blot=yes', then 'plist' also needs::

                plist['data']
                plist['blotImage']
                plist['blotnx'],plist['blotny']

            If 'build' is set to 'no', then each extension/array must be
            in separate FITS objects.  This would also require::

              plist['outdata']    - name of output SCI FITS image
              plist['outweight']  - name of output WHT FITS image
              plist['outcontext'] - name of output CTX FITS image

            Optionally, the overall exposure time information can be passed as::

                plist['texptime'] - total exptime for output
                plist['expstart'] - start time of combined exposure
                plist['expend']   - end time of combined exposure

            """
            self.build = build
            self.single = single
            self.parlist = plist
            self.input_pars = input_pars
            _nimgs = len(self.parlist)
            self.bunit = None
            self.units = 'cps'
            self.blot = blot

            if PYFITS_COMPRESSION and 'compress' in input_pars:
                self.compress = input_pars['compress'] # Control creation of compressed FITS files
            else:
                self.compress = False

            # Merge input_pars with each chip's outputNames object
            for p in self.parlist:
                p.update(input_pars)

            if not blot:
                self.output = plist[0]['output']
                self.shape = (plist[0]['outny'],plist[0]['outnx'])
            else:
                self.output = plist[0]['blotImage']
                self.shape = (plist[0]['blotny'],plist[0]['blotnx'])

            # Keep track of desired output WCS computed by PyDrizzle
            self.wcs = wcs

            #
            # Apply special operating switches:
            #   single - separate output for each input
            #
            if single:
                _outdata = plist[0]['outSingle']
                _outweight = plist[0]['outSWeight']
                _outcontext = plist[0]['outSContext']
                # Only report values appropriate for single exposure
                self.texptime = plist[0]['exptime']
                self.expstart = plist[0]['expstart']
                self.expend = plist[0]['expend']
            else:
                if self.build:
                    _outdata = plist[0]['outFinal']
                else:
                    _outdata = plist[0]['outSci']
                _outweight = plist[0]['outWeight']
                _outcontext = plist[0]['outContext']
                # Report values appropriate for entire combined product
                self.texptime = plist[0]['texptime']
                self.expstart = plist[0]['texpstart']
                self.expend = plist[_nimgs-1]['texpend']

            if blot:
                _outdata = plist[0]['blotImage']
                self.outblot_key = 'blotImage'

            if not self.build or single:
                self.output = _outdata

            self.outdata = _outdata
            self.outweight = _outweight
            self.outcontext = _outcontext

        def set_bunit(self,bunit):
            """
            Method used to update the value of the bunit attribute.
            """
            self.bunit = bunit

        def set_units(self,units):
            """
            Method used to record what units were specified by the user for the output product.
            """
            self.units = units

        def writeFITS(self, template, sciarr, whtarr, ctxarr=None,
                    versions=None, overwrite=yes, blend=True, virtual=False):
            """
            Generate PyFITS objects for each output extension
            using the file given by 'template' for populating
            headers.

            The arrays will have the size specified by 'shape'.
            """
            if not isinstance(template, list):
                template = [template]

            if fileutil.findFile(self.output):
                if overwrite:
                    log.info('Deleting previous output product: %s' % self.output)
                    fileutil.removeFile(self.output)

                else:
                    log.warning('Output file %s already exists and overwrite not '
                                'specified!' % self.output)
                    log.error('Quitting... Please remove before resuming '
                              'operations.')
                    raise IOError

            # initialize output value for this method
            outputFITS = {}
            # Default value for NEXTEND when 'build'== True
            nextend = 3
            if not self.build:
                nextend = 0
                if self.outweight:
                    if overwrite:
                        if fileutil.findFile(self.outweight):
                            log.info('Deleting previous output WHT product: %s' %
                                     self.outweight)
                        fileutil.removeFile(self.outweight)
                    else:
                        log.warning('Output file %s already exists and overwrite '
                                    'not specified!' % self.outweight)
                        log.error('Quitting... Please remove before resuming '
                                  'operations.')
                        raise IOError

                if self.outcontext:
                    if overwrite:
                        if fileutil.findFile(self.outcontext):
                            log.info('Deleting previous output CTX product: %s' %
                                     self.outcontext)
                        fileutil.removeFile(self.outcontext)
                    else:
                        log.warning('Output file %s already exists and overwrite '
                                    'not specified!' % self.outcontext)
                        log.error('Quitting... Please remove before resuming '
                                  'operations.')
                        raise IOError

            # Get default headers from multi-extension FITS file
            # If only writing out single drizzle product, blending needs to be
            # forced off as there is only 1 input to report, no blending needed
            if self.single:
                blend=False

            # If input data is not in MEF FITS format, it will return 'None'
            # and those headers will have to be generated from drizzle output
            # file FITS headers.
            # NOTE: These are HEADER objects, not HDUs
            #prihdr,scihdr,errhdr,dqhdr = getTemplates(template)
            self.fullhdrs, intab = getTemplates(template, blend=False)

            newhdrs, newtab = getTemplates(template,blend=blend)
            if newtab is not None: nextend += 1 # account for new table extn

            prihdr = newhdrs[0]
            scihdr = newhdrs[1]
            errhdr = newhdrs[2]
            dqhdr = newhdrs[3]

            # Setup primary header as an HDU ready for appending to output FITS file
            prihdu = fits.PrimaryHDU(header=prihdr, data=None)

            # Start by updating PRIMARY header keywords...
            prihdu.header.set('EXTEND', value=True, after='NAXIS')
            prihdu.header['NEXTEND'] = nextend
            prihdu.header['FILENAME'] = self.output
            prihdu.header['PROD_VER'] = 'DrizzlePac {}'.format(version.__version__)

            # Update the ROOTNAME with the new value as well
            _indx = self.output.find('_drz')
            if _indx < 0:
                rootname_val = self.output
            else:
                rootname_val = self.output[:_indx]
            prihdu.header['ROOTNAME'] = rootname_val

            # Get the total exposure time for the image
            # If not calculated by PyDrizzle and passed through
            # the pardict, then leave value from the template image.
            if self.texptime:
                prihdu.header['EXPTIME'] = self.texptime
                prihdu.header.set('TEXPTIME', value=self.texptime, after='EXPTIME')
                prihdu.header['EXPSTART'] = self.expstart
                prihdu.header['EXPEND'] = self.expend

            #Update ASN_MTYPE to reflect the fact that this is a product
            # Currently hard-wired to always output 'PROD-DTH' as MTYPE
            prihdu.header['ASN_MTYP'] = 'PROD-DTH'

            # Update DITHCORR calibration keyword if present
            # Remove when we can modify FITS headers in place...
            if 'DRIZCORR' in prihdu.header:
                prihdu.header['DRIZCORR'] = 'COMPLETE'
            if 'DITHCORR' in prihdu.header:
                prihdu.header['DITHCORR'] = 'COMPLETE'

            prihdu.header['NDRIZIM'] =(len(self.parlist),
                                       'Drizzle, No. images drizzled onto output')

            # Only a subset of these keywords makes sense for the new WCS based
            # transformations. They need to be reviewed to decide what to keep
            # and what to leave out.
            if not self.blot:
                self.addDrizKeywords(prihdu.header,versions)

            if scihdr:
                try:
                    del scihdr['OBJECT']
                except KeyError:
                    pass

                if 'CCDCHIP' in scihdr: scihdr['CCDCHIP'] = '-999'
                if 'NCOMBINE' in scihdr:
                    scihdr['NCOMBINE'] = self.parlist[0]['nimages']

                # If BUNIT keyword was found and reset, then
                bunit_last_kw = self.find_kwupdate_location(scihdr,'bunit')
                if self.bunit is not None:
                    comment_str = "Units of science product"
                    if self.bunit.lower()[:5] == 'count':
                        comment_str = "counts * gain = electrons"
                    scihdr.set('BUNIT', value=self.bunit,
                               comment=comment_str,
                               after=bunit_last_kw)
                else:
                    # check to see whether to update already present BUNIT comment
                    if 'bunit' in scihdr and scihdr['bunit'].lower()[:5] == 'count':
                        comment_str = "counts * gain = electrons"
                        scihdr.set('BUNIT', value=scihdr['bunit'],
                                   comment=comment_str,
                                   after=bunit_last_kw)

                # Add WCS keywords to SCI header
                if self.wcs:
                    pre_wcs_kw = self.find_kwupdate_location(scihdr,'CD1_1')
                    addWCSKeywords(self.wcs,scihdr,blot=self.blot,
                                    single=self.single, after=pre_wcs_kw)
                    # Recompute this after removing distortion kws
                    pre_wcs_kw = self.find_kwupdate_location(scihdr,'CD1_1')

            ##########
            # Now, build the output file
            ##########
            if self.build:
                print('-Generating multi-extension output file: ',self.output)
                fo = fits.HDUList()

                # Add primary header to output file...
                fo.append(prihdu)

                if self.single and self.compress:
                    hdu = fits.CompImageHDU(data=sciarr, header=scihdr, name=EXTLIST[0])
                else:
                    hdu = fits.ImageHDU(data=sciarr, header=scihdr, name=EXTLIST[0])
                last_kw = self.find_kwupdate_location(scihdr,'EXTNAME')
                hdu.header.set('EXTNAME', value='SCI', after=last_kw)
                hdu.header.set('EXTVER', value=1, after='EXTNAME')
                fo.append(hdu)

                # Build WHT extension here, if requested...
                if errhdr:
                    errhdr['CCDCHIP'] = '-999'

                if self.single and self.compress:
                    hdu = fits.CompImageHDU(data=whtarr, header=errhdr, name=EXTLIST[1])
                else:
                    hdu = fits.ImageHDU(data=whtarr, header=errhdr, name=EXTLIST[1])
                last_kw = self.find_kwupdate_location(errhdr,'EXTNAME')
                hdu.header.set('EXTNAME', value='WHT', after=last_kw)
                hdu.header.set('EXTVER', value=1, after='EXTNAME')
                if self.wcs:
                    pre_wcs_kw = self.find_kwupdate_location(hdu.header,'CD1_1')
                    # Update WCS Keywords based on PyDrizzle product's value
                    # since 'drizzle' itself doesn't update that keyword.
                    addWCSKeywords(self.wcs,hdu.header,blot=self.blot,
                                   single=self.single, after=pre_wcs_kw)
                fo.append(hdu)

                # Build CTX extension here
                # If there is only 1 plane, write it out as a 2-D extension
                if self.outcontext:
                    if ctxarr.shape[0] == 1:
                        _ctxarr = ctxarr[0]
                    else:
                        _ctxarr = ctxarr
                else:
                    _ctxarr = None

                if self.single and self.compress:
                    hdu = fits.CompImageHDU(data=_ctxarr, header=dqhdr, name=EXTLIST[2])
                else:
                    hdu = fits.ImageHDU(data=_ctxarr, header=dqhdr, name=EXTLIST[2])
                last_kw = self.find_kwupdate_location(dqhdr,'EXTNAME')
                hdu.header.set('EXTNAME', value='CTX', after=last_kw)
                hdu.header.set('EXTVER', value=1, after='EXTNAME')

                if self.wcs:
                    pre_wcs_kw = self.find_kwupdate_location(hdu.header,'CD1_1')
                    # Update WCS Keywords based on PyDrizzle product's value
                    # since 'drizzle' itself doesn't update that keyword.
                    addWCSKeywords(self.wcs,hdu.header,blot=self.blot,
                                   single=self.single, after=pre_wcs_kw)
                fo.append(hdu)

                # remove all alternate WCS solutions from headers of this product
                wcs_functions.removeAllAltWCS(fo,[1])

                # add table of combined header keyword values to FITS file
                if newtab is not None:
                    fo.append(newtab)

                if not virtual:
                    print('Writing out to disk:',self.output)
                    # write out file to disk
                    fo.writeto(self.output)
                    fo.close()
                    del fo, hdu
                    fo = None
                # End 'if not virtual'
                outputFITS[self.output]= fo

            else:
                print('-Generating simple FITS output: %s' % self.outdata)

                fo = fits.HDUList()
                hdu_header = prihdu.header.copy()
                del hdu_header['nextend']

                # Append remaining unique header keywords from template DQ
                # header to Primary header...
                if scihdr:
                    for _card in scihdr.cards:
                        if _card.keyword not in RESERVED_KEYS and _card.keyword not in hdu_header:
                            hdu_header.append(_card)
                for kw in ['PCOUNT', 'GCOUNT']:
                    try:
                        del kw
                    except KeyError:
                        pass
                hdu_header['filename'] = self.outdata

                if self.compress:
                    hdu = fits.CompImageHDU(data=sciarr, header=hdu_header)
                    wcs_ext = [1]
                else:
                    hdu = fits.ImageHDU(data=sciarr, header=hdu_header)
                    wcs_ext = [0]

                # explicitly set EXTEND to FALSE for simple FITS files.
                dim = len(sciarr.shape)
                hdu.header.set('extend', value=False, after='NAXIS%s'%dim)

                # Add primary header to output file...
                fo.append(hdu)

                # remove all alternate WCS solutions from headers of this product
                logutil.logging.disable(logutil.logging.INFO)
                wcs_functions.removeAllAltWCS(fo,wcs_ext)
                logutil.logging.disable(logutil.logging.NOTSET)

                # add table of combined header keyword values to FITS file
                if newtab is not None:
                    fo.append(newtab)

                if not virtual:
                    print('Writing out image to disk:',self.outdata)
                    # write out file to disk
                    fo.writeto(self.outdata)
                    del fo,hdu
                    fo = None
                # End 'if not virtual'
                outputFITS[self.outdata]= fo

                if self.outweight and whtarr is not None:
                    # We need to build new PyFITS objects for each WHT array
                    fwht = fits.HDUList()

                    if errhdr:
                        errhdr['CCDCHIP'] = '-999'

                    if self.compress:
                        hdu = fits.CompImageHDU(data=whtarr, header=prihdu.header)
                    else:
                        hdu = fits.ImageHDU(data=whtarr, header=prihdu.header)
                    # Append remaining unique header keywords from template DQ
                    # header to Primary header...
                    if errhdr:
                        for _card in errhdr.cards:
                            if _card.keyword not in RESERVED_KEYS and _card.keyword not in hdu.header:
                                hdu.header.append(_card)
                    hdu.header['filename'] = self.outweight
                    hdu.header['CCDCHIP'] = '-999'
                    if self.wcs:
                        pre_wcs_kw = self.find_kwupdate_location(hdu.header,'CD1_1')
                        # Update WCS Keywords based on PyDrizzle product's value
                        # since 'drizzle' itself doesn't update that keyword.
                        addWCSKeywords(self.wcs,hdu.header, blot=self.blot,
                                       single=self.single, after=pre_wcs_kw)

                    # Add primary header to output file...
                    fwht.append(hdu)
                    # remove all alternate WCS solutions from headers of this product
                    wcs_functions.removeAllAltWCS(fwht,wcs_ext)

                    if not virtual:
                        print('Writing out image to disk:',self.outweight)
                        fwht.writeto(self.outweight)
                        del fwht,hdu
                        fwht = None
                    # End 'if not virtual'
                    outputFITS[self.outweight]= fwht

                # If a context image was specified, build a PyFITS object
                # for it as well...
                if self.outcontext and ctxarr is not None:
                    fctx = fits.HDUList()

                    # If there is only 1 plane, write it out as a 2-D extension
                    if ctxarr.shape[0] == 1:
                        _ctxarr = ctxarr[0]
                    else:
                        _ctxarr = ctxarr

                    if self.compress:
                        hdu = fits.CompImageHDU(data=_ctxarr, header=prihdu.header)
                    else:
                        hdu = fits.ImageHDU(data=_ctxarr, header=prihdu.header)
                    # Append remaining unique header keywords from template DQ
                    # header to Primary header...
                    if dqhdr:
                        for _card in dqhdr.cards:
                            if ( (_card.keyword not in RESERVED_KEYS) and
                                 _card.keyword not in hdu.header):
                                hdu.header.append(_card)
                    hdu.header['filename'] = self.outcontext
                    if self.wcs:
                        pre_wcs_kw = self.find_kwupdate_location(hdu.header,'CD1_1')
                        # Update WCS Keywords based on PyDrizzle product's value
                        # since 'drizzle' itself doesn't update that keyword.
                        addWCSKeywords(self.wcs,hdu.header, blot=self.blot,
                                       single=self.single, after=pre_wcs_kw)

                    fctx.append(hdu)
                    # remove all alternate WCS solutions from headers of this product
                    wcs_functions.removeAllAltWCS(fctx,wcs_ext)
                    if not virtual:
                        print('Writing out image to disk:',self.outcontext)
                        fctx.writeto(self.outcontext)
                        del fctx,hdu
                        fctx = None
                    # End 'if not virtual'

                    outputFITS[self.outcontext]= fctx

            return outputFITS

        def find_kwupdate_location(self,hdr,keyword):
            """
            Find the last keyword in the output header that comes before the new
            keyword in the original, full input headers.
            This will rely on the original ordering of keywords from the original input
            files in order to place the updated keyword in the correct location in case
            the keyword was removed from the output header prior to calling this method.
            """
            # start by looping through the full templates
            kw_list = None
            last_kw = None
            for extn in self.fullhdrs:
                if keyword in extn:
                    #indx = extn.ascard.index_of(keyword)
                    indx = extn.index(keyword)
                    kw_list = list(extn.keys())[:indx]
                    break
            if kw_list:
                # find which keyword from this list exists in header to be updated
                for kw in kw_list[::-1]:
                    if kw in hdr:
                        last_kw = kw
                        break
            # determine new value for the last keyword found before the HISTORY kws
            if last_kw is None:
                hdrkeys = list(hdr.keys())
                i = -1
                last_kw = hdrkeys[i]
                while last_kw == 'HISTORY':
                    i -= 1
                    last_kw = hdrkeys[i]

            return last_kw

        def addDrizKeywords(self,hdr,versions):
            """ Add drizzle parameter keywords to header. """

            # Extract some global information for the keywords
            _geom = 'User parameters'

            _imgnum = 0
            for pl in self.parlist:

                # Start by building up the keyword prefix based
                # on the image number for the chip
                #_keyprefix = 'D%03d'%_imgnum
                _imgnum += 1

                drizdict = DRIZ_KEYWORDS.copy()
                # Update drizdict with current values
                drizdict['VER']['value'] = pl['driz_version'][:44]
                drizdict['DATA']['value'] = pl['data'][:64]
                drizdict['DEXP']['value'] = pl['exptime']
                drizdict['OUDA']['value'] = pl['outFinal'][:64]
                drizdict['OUWE']['value'] = pl['outWeight'][:64]
                if pl['outContext'] is None:
                    outcontext = ""
                else:
                    outcontext = pl['outContext'][:64]
                drizdict['OUCO']['value'] = outcontext
                if self.single:
                    drizdict['MASK']['value'] = pl['singleDrizMask'][:64]
                else:
                    drizdict['MASK']['value'] = pl['finalMask'][:64]

                # Process the values of WT_SCL to be consistent with
                # what IRAF Drizzle would output
                if 'wt_scl_val' in pl:
                    _wtscl = pl['wt_scl_val']
                else:
                    if pl['wt_scl'] == 'exptime': _wtscl = pl['exptime']
                    elif pl['wt_scl'] == 'expsq': _wtscl = pl['exptime']*pl['exptime']
                    else: _wtscl = pl['wt_scl']

                drizdict['WTSC']['value'] = _wtscl
                drizdict['KERN']['value'] = pl['kernel']
                drizdict['PIXF']['value'] = pl['pixfrac']
                drizdict['OUUN']['value'] = self.units
                if pl['fillval'] is None:
                    _fillval = 'INDEF'
                else:
                    _fillval = pl['fillval']
                drizdict['FVAL']['value'] = _fillval
                drizdict['WKEY']['value'] = pl['driz_wcskey']

                drizdict['SCAL'] = {'value':pl['scale'],'comment':'Drizzle, pixel size (arcsec) of output image'}
                drizdict['ISCL'] = {'value':pl['idcscale'],'comment':'Drizzle, default IDCTAB pixel size(arcsec)'}

                # Now update header with values
                writeDrizKeywords(hdr,_imgnum,drizdict)
                del drizdict

            # Add version information as HISTORY cards to the header
            if versions is not None:
                ver_str = "AstroDrizzle processing performed using: "
                hdr.add_history(ver_str)
                for k in versions.keys():
                    ver_str = '    '+str(k)+' Version '+str(versions[k])
                    hdr.add_history(ver_str)

    def cleanTemplates(scihdr,errhdr,dqhdr):

        # Now, safeguard against having BSCALE and BZERO
        for kw in ['BSCALE', 'BZERO']:
            try:
                del scihdr[kw]
            except KeyError:
                pass
            try:
                del errhdr[kw]
            except KeyError:
                pass
            try:
                del dqhdr[kw]
            except KeyError:
                pass

        # At this point, check errhdr and dqhdr to make sure they
        # have all the requisite keywords (as listed in updateDTHKeywords).
        # Simply copy them from scihdr if they don't exist...
        if errhdr is not None and dqhdr is not None:
            for keyword in WCS_KEYWORDS:
                if keyword in scihdr:
                    if keyword not in errhdr:
                        errhdr[keyword] = scihdr[keyword]
                    if keyword not in dqhdr:
                        dqhdr[keyword]= scihdr[keyword]

    def getTemplates(fnames, blend=True):
        """ Process all headers to produce a set of combined headers
            that follows the rules defined by each instrument.

        """
        if not blend:
            newhdrs =  blendheaders.getSingleTemplate(fnames[0])
            newtab = None
        else:
            # apply rules to create final version of headers, plus table
            newhdrs, newtab = blendheaders.get_blended_headers(inputs=fnames)

        cleanTemplates(newhdrs[1],newhdrs[2],newhdrs[3])

        return newhdrs, newtab

    def addWCSKeywords(wcs,hdr,blot=False,single=False,after=None):
        """ Update input header 'hdr' with WCS keywords.
        """
        wname = wcs.wcs.name
        if not single:
            wname = 'DRZWCS'

        # Update WCS Keywords based on PyDrizzle product's value
        # since 'drizzle' itself doesn't update that keyword.
        hdr['WCSNAME'] = wname
        hdr.set('VAFACTOR', value=1.0, after=after)
        hdr.set('ORIENTAT', value=wcs.orientat, after=after)

        # Use of 'after' not needed if these keywords already exist in the header
        if after in WCS_KEYWORDS:
            after = None

        if 'CTYPE1' not in hdr:
            hdr.set('CTYPE2', value=wcs.wcs.ctype[1], after=after)
            hdr.set('CTYPE1', value=wcs.wcs.ctype[0], after=after)
        hdr.set('CRPIX2', value=wcs.wcs.crpix[1], after=after)
        hdr.set('CRPIX1', value=wcs.wcs.crpix[0], after=after)
        hdr.set('CRVAL2', value=wcs.wcs.crval[1], after=after)
        hdr.set('CRVAL1', value=wcs.wcs.crval[0], after=after)
        hdr.set('CD2_2', value=wcs.wcs.cd[1][1], after=after)
        hdr.set('CD2_1', value=wcs.wcs.cd[1][0], after=after)
        hdr.set('CD1_2', value=wcs.wcs.cd[0][1], after=after)
        hdr.set('CD1_1', value=wcs.wcs.cd[0][0], after=after)

        # delete distortion model related keywords
        deleteDistortionKeywords(hdr)

        if not blot:
            blendheaders.remove_distortion_keywords(hdr)

    def deleteDistortionKeywords(hdr):
        """ Delete distortion related keywords from output drizzle science header
            since the drizzled image should have no remaining distortion.
        """
        dist_kws = ['D2IMERR1','D2IMERR2','D2IMDIS1','D2IMDIS2','D2IM1.*','D2IM2.*','D2IMEXT']
        for kw in dist_kws:
            try:
                del hdr[kw]
            except KeyError as e:
                pass

    def writeSingleFITS(data,wcs,output,template,clobber=True,verbose=True):
        """ Write out a simple FITS file given a numpy array and the name of another
        FITS file to use as a template for the output image header.
        """
        outname,outextn = fileutil.parseFilename(output)
        outextname,outextver = fileutil.parseExtn(outextn)

        if fileutil.findFile(outname):
            if clobber:
                log.info('Deleting previous output product: %s' % outname)
                fileutil.removeFile(outname)

            else:
                log.warning('Output file %s already exists and overwrite not '
                            'specified!' % outname)
                log.error('Quitting... Please remove before resuming operations.')
                raise IOError

        # Now update WCS keywords with values from provided WCS
        if hasattr(wcs.sip,'a_order'):
            siphdr = True
        else:
            siphdr = False
        wcshdr = wcs.wcs2header(sip2hdr=siphdr)

        if template is not None:
            # Get default headers from multi-extension FITS file
            # If input data is not in MEF FITS format, it will return 'None'
            # NOTE: These are HEADER objects, not HDUs
            (prihdr,scihdr,errhdr,dqhdr),newtab = getTemplates(template,EXTLIST)

            if scihdr is None:
                scihdr = fits.Header()
                indx = 0
                for c in prihdr.cards:
                    if c.keyword not in ['INHERIT','EXPNAME']: indx += 1
                    else: break
                for i in range(indx,len(prihdr)):
                    scihdr.append(prihdr.cards[i])
                for i in range(indx, len(prihdr)):
                    del prihdr[indx]
        else:
            scihdr = fits.Header()
            prihdr = fits.Header()
            # Start by updating PRIMARY header keywords...
            prihdr.set('EXTEND', value=True, after='NAXIS')
            prihdr['FILENAME'] = outname

        if outextname == '':
            outextname = 'sci'
        if outextver == 0: outextver = 1
        scihdr['EXTNAME'] = outextname.upper()
        scihdr['EXTVER'] = outextver

        for card in wcshdr.cards:
            scihdr[card.keyword] = (card.value, card.comment)

        # Create PyFITS HDUList for all extensions
        outhdu = fits.HDUList()
        # Setup primary header as an HDU ready for appending to output FITS file
        prihdu = fits.PrimaryHDU(header=prihdr)
        scihdu = fits.ImageHDU(header=scihdr,data=data)

        outhdu.append(prihdu)
        outhdu.append(scihdu)
        outhdu.writeto(outname)

        if verbose:
            print('Created output image: %s' % outname)

    def writeDrizKeywords(hdr,imgnum,drizdict):
        """ Write basic drizzle-related keywords out to image header as a record
            of the processing performed to create the image

            The dictionary 'drizdict' will contain the keywords and values to be
            written out to the header.
        """
        _keyprefix = 'D%03d'%imgnum

        for key in drizdict:
            val = drizdict[key]['value']
            if val is None: val = ""
            comment = drizdict[key]['comment']
            if comment is None: comment = ""
            hdr[_keyprefix+key] = (val, drizdict[key]['comment'])