"""
>>> root = Path(getfixture('zipfile_abcde'))
>>> a, b = root.iterdir()
>>> a
Path('abcde.zip', 'a.txt')
>>> b
Path('abcde.zip', 'b/')
>>> b.name
'b'
>>> c = b / 'c.txt'
>>> c
Path('abcde.zip', 'b/c.txt')
>>> c.name
'c.txt'
>>> c.read_text()
'content of c'
>>> c.exists()
True
>>> (b / 'missing.txt').exists()
False
>>> str(c)
'abcde.zip/b/c.txt'
"""

from __future__ import division

import io
import sys
import posixpath
import zipfile
import operator
import functools

__metaclass__ = type


class Path:
    __repr = '{self.__class__.__name__}({self.root.filename!r}, {self.at!r})'

    def __init__(self, root, at=''):
        self.root = root if isinstance(root, zipfile.ZipFile) \
            else zipfile.ZipFile(self._pathlib_compat(root))
        self.at = at

    @staticmethod
    def _pathlib_compat(path):
        """
        For path-like objects, convert to a filename for compatibility
        on Python 3.6.1 and earlier.
        """
        try:
            return path.__fspath__()
        except AttributeError:
            return str(path)

    @property
    def open(self):
        return functools.partial(self.root.open, self.at)

    @property
    def name(self):
        return posixpath.basename(self.at.rstrip('/'))

    def read_text(self, *args, **kwargs):
        with self.open() as strm:
            return io.TextIOWrapper(strm, *args, **kwargs).read()

    def read_bytes(self):
        with self.open() as strm:
            return strm.read()

    def _is_child(self, path):
        return posixpath.dirname(path.at.rstrip('/')) == self.at.rstrip('/')

    def _next(self, at):
        return Path(self.root, at)

    def is_dir(self):
        return not self.at or self.at.endswith('/')

    def is_file(self):
        return not self.is_dir()

    def exists(self):
        return self.at in self.root.namelist()

    def iterdir(self):
        if not self.is_dir():
            raise ValueError("Can't listdir a file")
        names = map(operator.attrgetter('filename'), self.root.infolist())
        subs = map(self._next, names)
        return filter(self._is_child, subs)

    def __str__(self):
        return posixpath.join(self.root.filename, self.at)

    def __repr__(self):
        return self.__repr.format(self=self)

    def __truediv__(self, add):
        add = self._pathlib_compat(add)
        next = posixpath.join(self.at, add)
        next_dir = posixpath.join(self.at, add, '')
        names = self.root.namelist()
        return self._next(
            next_dir if next not in names and next_dir in names else next
        )

    if sys.version_info < (3,):
        __div__ = __truediv__
