#!/usr/bin/env jython

import os
import re
import shutil

'''
FITS file class, for reading silly headers and things
'''
class Fits(object):


    '''
    Constructor

    '''
    def __init__(self, logger, config, originalPath):

       # set class variables
       self.originalPath = originalPath
       self.logger = logger
       self.config = config
       self.header = None

       # does this file even exist?
       if not os.path.isfile(self.originalPath): 
           self.logger.errorPair("Cannot read file", self.originalPath)
           return

       # ok, we have a file, now copy it locally to save on NFS overhead
       self.logger.debugPair("FITS file", self.originalPath)
       #self.localCopyPath = self.config.basePath + "/temp.fits"
       #shutil.copy2(self.originalPath, self.localCopyPath)

       # open the local copy and parse the ridiculou plain-text header
       #self.file = open(self.localCopyPath)
       self.file = open(self.originalPath)
       self.header = self.parseHeader()
       #self.logger.debugPair("FITS local copy", self.localCopyPath)
       self.logger.debugPair("FITS primary header", "%d cards found" % len(self.header))

       # move file pointer back to the start of the file
       self.rewindToStart()

    '''
    Returns count of 'cards' found in primary header
    '''
    def getPrimaryHeaderCardCount(self):
        return len(self.header)

    '''
    Returns the path to the LOCAL COPY of this FITS file
    '''
    def getPath(self):
        #return self.localCopyPath
        return self.originalPath

    '''
    Returns the path to the LOCAL COPY of this FITS file
    '''
    def getOriginalPath(self):
        return self.originalPath

    '''
    Returns the primary header as a dictionary object
    '''
    def getPrimaryHeader(self):
        return self.header

    '''
    Returns the value associated with this key in the primary header, or NULL (as a string) otherwise
    '''
    def getPrimaryHeaderValue(self, key):

        if key in self.header: return self.header[key]
        else: return "NULL"

    '''
    Moves the file pointer back to the start of this file
    '''
    def rewindToStart(self):
        self.file.seek(0, 0)


    '''
    Finds and reads a header extension.

    2880 is the magic number that the FITS format uses to break up data: extensions always begin on multiples of 2880 bytes
    '''
    def findAndReadHeader(self, name):

        found = False

        origIndex = self.file.tell()
        while True:

            index = self.file.tell()

            record = self.file.read(80)
            if not record: break;

            # quit when we reach 'END'
            if record.startswith("XTENSION= 'IMAGE"):

                header = self.parseHeader()
                if header['EXTNAME'] == name:
                    found = True
                    self.file.seek(index + 2880, 0)
                    break

            self.file.seek(index + 2880, 0)

        if found != True:

            self.file.seek(origIndex, 0)
            self.logger.errorPair("Could not read header", name)
            return
        else: 
            self.logger.debug("...read header at '" + name + "' and found " + str(len(header)) + " header cards") 

        return header


    '''
    Reads FITS header and stores all fields in a dictionary object
    '''
    def parseHeader(self):

        header = {}

        while (True):

            record = self.file.read(80)

            # quit when we reach 'END'
            if re.match('END\s+', record): break

            # this regex will get param/value pairs for all header cards, ignoring comments and parsing out 'HIERACH' prefixes
            match = re.match('^(HIERARCH )*([a-zA-Z0-9-_\.]+)\s*=\s+\'*([a-zA-Z0-9-+_\.:\s@#]+)\'*\\/*', record)
            if match:

                param = match.group(2)
                value = match.group(3).strip()
                if value == "NaN": value = "NULL"
                header[param] = value

                #print param + "|" + value + "|"

        return header

