"""
fs.wrapfs.limitsizefs
=====================

An FS wrapper class for limiting the size of the underlying FS.

This module provides the class LimitSizeFS, an FS wrapper that can limit the
total size of files stored in the wrapped FS.

"""

from __future__ import with_statement

from fs.errors import *
from fs.path import *
from fs.base import FS, threading, synchronize
from fs.wrapfs import WrapFS
from fs.filelike import FileWrapper


class LimitSizeFS(WrapFS):
    """FS wrapper class to limit total size of files stored."""

    def __init__(self, fs, max_size):
        super(LimitSizeFS,self).__init__(fs)
        if max_size < 0:
            try:
                max_size = fs.getmeta("total_space") + max_size
            except NotMetaError:
                msg = "FS doesn't report total_size; "\
                      "can't use negative max_size"
                raise ValueError(msg)
        self.max_size = max_size
        self._size_lock = threading.Lock()
        self._file_sizes = PathMap()
        self.cur_size = self._get_cur_size()

    def __getstate__(self):
        state = super(LimitSizeFS,self).__getstate__()
        del state["cur_size"]
        del state["_size_lock"]
        del state["_file_sizes"]
        return state

    def __setstate__(self, state):
        super(LimitSizeFS,self).__setstate__(state)
        self._size_lock = threading.Lock()
        self._file_sizes = PathMap()
        self.cur_size = self._get_cur_size()

    def _get_cur_size(self,path="/"):
        return sum(self.getsize(f) for f in self.walkfiles(path))

    def getsyspath(self, path, allow_none=False):
        #  If people could grab syspaths, they could route around our
        #  size protection; no dice!
        if not allow_none:
            raise NoSysPathError(path)
        return None

    def open(self, path, mode="r"):
        path = relpath(normpath(path))
        with self._size_lock:
            try:
                size = self.getsize(path)
            except ResourceNotFoundError:
                size = 0