from email.utils import mktime_tz, parsedate_tz
import re

from django.core.files.base import File
from django.http import HttpResponse, HttpResponseNotModified
from django.utils.http import http_date


def sendfile(request, filepath, **kwargs):
    '''Use the SENDFILE_ROOT value composed with the path arrived as argument
    to build an absolute path with which resolve and return the file contents.

    If the path points to a file out of the root directory (should cover both
    situations with '..' and symlinks) then a 404 is raised.
    '''
    statobj = filepath.stat()

    # Respect the If-Modified-Since header.
    if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
                              statobj.st_mtime, statobj.st_size):
        return HttpResponseNotModified()

    with File(filepath.open('rb')) as f:
        response = HttpResponse(f.chunks())

    response["Last-Modified"] = http_date(statobj.st_mtime)
    return response


def was_modified_since(header=None, mtime=0, size=0):
    """
    Was something modified since the user last downloaded it?

    header
      This is the value of the If-Modified-Since header.  If this is None,
      I'll just return True.

    mtime
      This is the modification time of the item we're talking about.

    size
      This is the size of the item we're talking about.
    """
    try:
        if header is None:
            raise ValueError
        matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header,
                           re.IGNORECASE)
        header_date = parsedate_tz(matches.group(1))
        if header_date is None:
            raise ValueError
        header_mtime = mktime_tz(header_date)
        header_len = matches.group(3)
        if header_len and int(header_len) != size:
            raise ValueError
        if mtime > header_mtime:
            raise ValueError
    except (AttributeError, ValueError, OverflowError):
        return True
    return False