#
# -*- coding: utf-8 -*-

'''
$RCSfile: datasource.py,v $
$Revision: 1130 $
$Author: markus $
$Date: 2007-04-10 00:05:14 +0200 (Tue, 10 Apr 2007) $

Simple general functions dealing with files, directories and alike.
'''

import os, stat
from datetime   import datetime
import urlparse, urllib, shutil
import urllib2
import logging
log = logging.getLogger("biocase.tools")

import biocase.configuration
cfg=biocase.configuration.Cfg()




################
# file caching
# ------------------------------------------------------------------------------------
def getPickleFilename(filename):
    '''Takes an absolute filename and creates the filename of a pickle for serialization.
    Function used to standardize the naming of pickles.'''
    return getCachedFilename(filename+".pick")
def getCachedFilename(absFilename, cachedir=cfg.cacheLocator):
    '''Returns path to file in cache dir and creates non-existing intermediary directories.
    If absolute filepath is pointing within the installation dir, only the relative path is used for the cachefile.
    If its a filepath outside of the installation, then the entire path is used for caching.'''
    fn=os.path.normcase(os.path.normpath(absFilename))
    home=os.path.normcase(cfg.homeLocator)
    if fn.startswith(home):
        fn = absFilename[len(home)+1:]
    elif fn.startswith(os.sep):
        fn = fn[1:]
    else:
        # escape characters?
        pass
    path=os.path.join(cachedir,fn)
    dirPath = os.path.split(path)[0]
    if not os.path.isdir(dirPath):
        log.debug("Create cache dir %s"%dirPath)
        os.makedirs(dirPath)    
    log.debug("Cache filename: %s"%path)
    return path
def getCachedDsaFile(fileWithinDsa, dsa):
    return getCachedFilename(os.path.join(cfg.dsaLocator,dsa,fileWithinDsa))

def clearCache(cachedir=cfg.cacheLocator):
    log.debug("Clearing cache %s..."%cachedir)
    for cdir in os.listdir(cachedir):
        if cdir not in ['.','..']:
            try:
                shutil.rmtree(cdir)
            except:
                log.debug("Could not remove cached data in %s"%cachedir)

def getModifiedDatetime(filename):
    '''Returns datetime object for the date-last-modified of a file if it exists, otherwise None'''
    if os.path.isfile(filename):
        return datetime.fromtimestamp(os.stat(filename)[stat.ST_MTIME])
    else:
        return None



################
# URL caching
# ------------------------------------------------------------------------------------
def URL2Filename(source):
    '''Takes a URL and transforms it into a filename with subfolders and the domain as the root folder.
    E.g.  www.biocase.org/schemas/abcd  becomes www.biocase.org/schemas/abcd'''
    (addressing_scheme, network_location, path, query, fragment_identifier) = urlparse.urlsplit(source)
    network_location = network_location.replace(':','_')
    if len(path) > 0 and path[0]=="/":
        path = path[1:]
    return os.path.normpath( os.path.join( addressing_scheme, network_location, urllib.url2pathname(path) ) )

def cacheUrl(url, forceReload=False):
    return getCachedUrlFilename(url=url, cachedir=cfg.cacheLocator, forceReload=forceReload)

def getCachedURL(url, cachedir=cfg.cacheLocator, refreshDuration=None, forceReload=False):
    '''Get the filename of the cached file for a given URL. 
    If it doesnt exist yet, the refreshDuration in minutes (floats accepted) is passed or if a forceReload is specified, download the file into the cache.
    '''
    log.debug("get cached file. URL %s  refreshDuration=%s, forceReload=%s"%(url,str(refreshDuration),str(forceReload)))
    filename = os.path.join( cachedir, URL2Filename(url) )
    # does cache exist and is the cached file not older than expiry date?
    cacheDate  = getModifiedDatetime(filename)
    # reload/refresh webcache?
    if forceReload or not os.path.isfile(filename) or (refreshDuration is not None and cacheDate - datetime.now() + refreshDuration*60 < 0):
        if os.path.isfile(filename):
            refreshString = ""
            cachedString  = ""
            if refreshDuration is not None:
                refreshString = " after %i minutes (%f days)"%(int(refreshDuration), refreshDuration/1440.0)
            if cacheDate is not None:
                cachedString  = " Last time cached = %s"%(str(cacheDate))
            log.debug( "Force file refreshing%s.%s"%(refreshString, cachedString) )
        # download file. If download failed, reuse cache if it exists
        if not downloadFile(url, filename):
            if os.path.isfile(filename):
                # cached version exists. 
                log.debug("File not retrievable, use outdated cached version of file %s" % filename)
            else:
                log.debug("File not retrievable and not cached %s" % filename)
                return None
    else:
        log.debug( "Up to date version of webresource %s exists. Last caching: %s, Refresh at %s"%(url, str(cacheDate), str(refreshDuration)) )
    return filename

def downloadFile(url, filename, restrictContentType=None):
    '''Tries to download a file and returns the content type given in the headers.
    If http error occurs, return None.
    if restrictContentType is set, check through http headers that downloaded file has the same type, otherwise return None'''
    log.info( "Downloading URL %s to file %s"%(url, filename) )
    try:
        asock = urllib2.urlopen(url)
    except urllib2.HTTPError, e:
        log.info("File not accessible. HTTP Error %s" % e.code)
        return None
    except:
        log.info("File not accessible. Unknown urllib2 Error.")
        return None
    # check content type?
    headers=asock.info()
    if restrictContentType:
        if restrictContentType.lower() <> headers.type:
            log.debug("Content-Type of downloaded file is wrong: %s" % headers.type)
            return None
    # create potentially non existing directories to target file
    if not os.path.isdir(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
    # write file
    fic=open(filename,'w')
    fic.write(asock.read())
    fic.close()
    asock.close()
    return headers.type





if __name__ ==  "__main__":
    # start logging
    import biocase.initlogs
    biocase.initlogs.initWebappLogging()
    
    f='http://www.google.de/fgdhsjk6.html'
    f='http://rs.tdwg.org/tapir/cs/dwc/1.0/model/dwc.xml'
    #f='http://www.thescripts.com/forum/thread26715.html'
    #print downloadFile(f, '/Users/markus/Desktop/tmp.txt')
    clearCache()
    print getCachedFilename("tellme.txt")
    print getCachedFilename('/Users/markus/Desktop/tmp.txt')
    print getCachedFilename("/Users/markus/Sites/bps2/log/pywrapper_error.log")
    print getPickleFilename(os.path.join(cfg.dsaLocator,'ponti','cmf_abcd.xml'))