# Zip archive tools
import os
import zipfile as ZP
from datetime import date as DT
import time
import fnmatch

class Archive(object):
    """Archive(name, dataDir[, archiveDir, fnameList, ageLimit, archiveCount])
    Parameters:
        Required:
            name : the name of the archive 
            dataDir : the directory for the data
        Optional:
            archiveDir: the directory for the archives
            fnameList: an optional list of file names (not full paths) * see SetFileListByPattern
            ageLimit: the number of days between creating a new archive
            archiveCount: the number of archives to hold onto
                Note: if archiveCount is less than 1, no old archives will be removed
    Methods:
        GetCurrentArchives -- returns a list of file names or paths
        SetFileListByPattern -- determines the files to archive by pattern
        SetFileList -- overrides the file list
        AppendFile -- adds a file name to the file list
        CreateNewArchive -- forces the creation of the archive
        GetYoungestArchiveAge -- returns the age and fullpath to the youngest archive
    """
    def __init__(self, name, dataDir, archiveDir= None, fnameList=[], ageLimit=3, archiveCount = 3):
        self.name= name
        self.dataDir = dataDir
        self.archiveDir = archiveDir if archiveDir else dataDir
        self.fnameList = fnameList
        self.ageLimit = ageLimit
        self.archiveCount = archiveCount
    
    def GetCurrentArchives(self, namesOnly=False, oldestLast = False):
        """GetCurrentArchives(namesOnly = False, oldestLast = False)
        Returns a list of the current archive names in chronological order.
        
        If namesOnly is True, only the file names will be appended to the 
        list, otherwise, the list will be full paths.
        
        If oldestLast is True, the list will be in reverse chronological order
        """
        archives = []
        now = time.time()
        for fname in os.listdir(self.archiveDir):
            #~ print fname, fnmatch.fnmatch(fname, '%s_archive*.zip' % self.name)
            if fnmatch.fnmatch(fname, '%s_archive*.zip' % self.name):
                fullPath = os.path.join(self.archiveDir, fname)
                stat = os.stat(fullPath)
                age = now - stat.st_ctime
                if namesOnly:
                    archives.append( (age, fname ) )
                else:
                    archives.append( (age, fullPath) )
        return [b for a,b in sorted(archives, reverse = oldestLast)]
            
    def SetFileListByPattern(self, pattern):
        """SetFileListByPattern(pattern)
        Replaces the current file list with files in the dataDir that match
        the given pattern. The pattern used according to fnmatch.fnmatch
        """
        res = []
        for fname in os.listdir(self.dataDir):
            #~ print fname, fnmatch.fnmatch(fname, pattern)
            if fnmatch.fnmatch(fname, pattern):
                res.append(fname)
        self.fnameList = res

    def CreateNewArchive(self):
        """CreateNewArchive
        Creates an archive from the fnameList and writes the archive
        to the archiveDir.
        """
        if not self.fnameList: 
            return None
        archiveName = "%s_archive_%s.zip" % (self.name, DT.today().isoformat())
        archivePath = os.path.join(self.archiveDir, archiveName)
        za = ZP.ZipFile(archivePath, 'w', ZP.ZIP_DEFLATED)
        for fname in self.fnameList:
            tname =os.path.join(self.dataDir, fname)
            if os.path.exists(tname):
                za.write(tname, fname)
        za.close()
        return True
    
    def GetYoungestArchiveAge(self):
        """GetYoungestArchiveAge
        Returns the age and full path to the youngest archive
        """
        archives = self.GetCurrentArchives(namesOnly = False, oldestLast = False)
        stat = os.stat(archives[0])
        age = time.time() - stat.st_ctime
        return age, archives[0]
    
    def ManageArchives(self):
        """ManageArchives
        If the youngest archive is too old, make a new one.
        If there are more archives than needed, delete the older ones.
        """
        archives = self.GetCurrentArchives()
        # newest archive at front of list
        if not archives:
            self.CreateNewArchive()
            return True
            
        youngest = archives[0]
        stat = os.stat(youngest)
        age = stat.st_ctime
        days = age / (60 * 60 * 24)
        if days > self.ageLimit:
            self.CreateNewArchive()
        
        archives = self.GetCurrentArchives()
        if self.archiveCount <= 0:
            return True
            
        while len(archives) > self.archiveCount:
            os.remove(archives[0])
            archives = self.GetCurrentArchives()
        
if __name__=='__main__':
    import HCTools as hct
    opts = hct.HeadCountOptions()
    a = Archive('headcount', opts.GetDataFolder(), archiveCount = -1 )
    
    