"""
fs.mountfs
==========

Contains MountFS class which is a virtual filesystem which can have other filesystems linked as branched directories.

For example, lets say we have two filesystems containing config files and resources respectively::

   [config_fs]
   |-- config.cfg
   `-- defaults.cfg 

   [resources_fs]
   |-- images
   |   |-- logo.jpg
   |   `-- photo.jpg 
   `-- data.dat

We can combine these filesystems in to a single filesystem with the following code::

    from fs.mountfs import MountFS
    combined_fs = MountFS
    combined_fs.mountdir('config', config_fs)
    combined_fs.mountdir('resources', resources_fs)

This will create a single filesystem where paths under `config` map to `config_fs`, and paths under `resources` map to `resources_fs`::

    [combined_fs]
    |-- config
    |   |-- config.cfg
    |   `-- defaults.cfg
    `-- resources
        |-- images
        |   |-- logo.jpg    
        |   `-- photo.jpg
        `-- data.dat

Now both filesystems can be accessed with the same path structure::

    print combined_fs.getcontents('/config/defaults.cfg')
    read_jpg(combined_fs.open('/resources/images/logo.jpg')
    
"""

from fs.base import *
from fs.errors import *
from fs.path import *
from fs import _thread_synchronize_default


class DirMount(object):
    def __init__(self, path, fs):
        self.path = path
        self.fs = fs

    def __str__(self):
        return "Mount point: %s"%self.path


class FileMount(object):
    def __init__(self, path, open_callable, info_callable=None):
        self.open_callable = open_callable
        def no_info_callable(path):
            return {}
        self.info_callable = info_callable or no_info_callable


class MountFS(FS):
    """A filesystem that delegates to other filesystems."""


    _meta = { 'virtual': True,
              'read_only' : False,
              'unicode_paths' : True,
              'case_insensitive_paths' : False,              
              }

    DirMount = DirMount
    FileMount = FileMount

    def __init__(self, thread_synchronize=_thread_synchronize_default):
        super(MountFS, self).__init__(thread_synchronize=thread_synchronize)
        self.mount_tree = PathMap()

    def __str__(self):
        return "<MountFS>"

    __repr__ = __str__

    def __unicode__(self):
        return unicode(self.__str__())

    def _delegate(self, path):
        path = abspath(normpath(path))
        object = None
        head_path = "/"
        tail_path = path

        for prefix in recursepath(path):
            try:
                object = self.mount_tree[prefix]
            except KeyError:
                pass
            else:
                head_path = prefix
                tail_path = path[len(head_path):]

        if type(object) is MountFS.DirMount:
            return object.fs, head_path, tail_path

        if type(object) is MountFS.FileMount:
            return self, "/", path

        try:
            self.mount_tree.iternames(path).next()
        except StopIteration:
            return None, None, None
        else: