#!/usr/bin/env python

#
# Copyright (c) 2009-2010 Evert Pot
# All rights reserved.
# http://www.rooftopsolutions.nl/
#
# This utility is distributed along with SabreDAV
# license: http://code.google.com/p/sabredav/wiki/License Modified BSD License

import os
from optparse import OptionParser
import time 

def getfreespace(path):
    stat = os.statvfs(path)
    return stat.f_frsize * stat.f_bavail

def getbytesleft(path,treshold):
    return getfreespace(path)-treshold

def run(cacheDir, treshold, sleep=5, simulate=False, min_erase = 0):

    bytes = getbytesleft(cacheDir,treshold)
    if (bytes>0):
        print "Bytes to go before we hit treshhold:", bytes
    else:
        print "Treshold exceeded with:", -bytes, "bytes" 
        dir = os.listdir(cacheDir)
        dir2 = []
        for file in dir:
            path = cacheDir + '/' + file
            dir2.append({
                "path" : path,
                "atime": os.stat(path).st_atime,
                "size" : os.stat(path).st_size
            })

        dir2.sort(lambda x,y: int(x["atime"]-y["atime"]))
        
        filesunlinked = 0
        gainedspace = 0

        # Left is the amount of bytes that need to be freed up
        # The default is the 'min_erase setting'
        left = min_erase

        # If the min_erase setting is lower than the amount of bytes over
        # the treshold, we use that number instead.
        if left < -bytes :
            left = -bytes

        print "Need to delete at least:", left;

        for file in dir2:

            # Only deleting files if we're not simulating
            if not simulate: os.unlink(file["path"])
            left = int(left - file["size"])
            gainedspace = gainedspace + file["size"]
            filesunlinked = filesunlinked + 1
            
            if(left<0):
                break

        print "%d files deleted (%d bytes)" % (filesunlinked, gainedspace)
        
    
    time.sleep(sleep)

    

def main():
    parser = OptionParser(
        version="naturalselecton v0.3",
        description="Cache directory manager. Deletes cache entries based on accesstime and free space tresholds.\n" + 
            "This utility is distributed alongside SabreDAV.",
        usage="usage: %prog [options] cacheDirectory",
    )
    parser.add_option(
        '-s',
        dest="simulate",
        action="store_true",
        help="Don't actually make changes, but just simulate the behaviour",
    )
    parser.add_option(
        '-r','--runs',
        help="How many times to check before exiting. -1 is infinite, which is the default",
        type="int",
        dest="runs",
        default=-1
    )
    parser.add_option(
        '-n','--interval',
        help="Sleep time in seconds (default = 5)",
        type="int",
        dest="sleep",
        default=5
    )
    parser.add_option(
        '-l','--treshold',
        help="Treshhold in bytes (default = 10737418240, which is 10GB)",
        type="int",
        dest="treshold",
        default=10737418240
    )
    parser.add_option(
        '-m', '--min-erase',
        help="Minimum number of bytes to erase when the treshold is reached. " +
            "Setting this option higher will reduce the amount of times the cache directory will need to be scanned. " +
            "(the default is 1073741824, which is 1GB.)", 
        type="int",
        dest="min_erase",
        default=1073741824
    )

    options,args = parser.parse_args()
    if len(args)<1:
        parser.error("This utility requires at least 1 argument")    
    cacheDir = args[0]

    print "Natural Selection"
    print "Cache directory:", cacheDir
    free = getfreespace(cacheDir);
    print "Current free disk space:", free

    runs = options.runs;
    while runs!=0 :
        run(
            cacheDir,
            sleep=options.sleep,
            simulate=options.simulate,
            treshold=options.treshold,
            min_erase=options.min_erase
        )
        if runs>0:
            runs = runs - 1

if __name__ == '__main__' :
    main()
