"""
resolvers.py
---------------

Tools to load assets referenced in meshes, like MTL files
or texture images.
"""
import os

# URL parsing for remote resources via WebResolver
try:
    # Python 3
    from urllib.parse import urlparse, urljoin
except ImportError:
    # Python 2
    from urlparse import urlparse, urljoin


class Resolver(object):
    """
    The base class for resolvers.
    """

    def __init__(self, *args, **kwargs):
        raise NotImplementedError('Use a resolver subclass!')

    def __getitem__(self, key):
        return self.get(key)


class FilePathResolver(Resolver):
    """
    Resolve files from a source path on the file system.
    """

    def __init__(self, source):
        """
        Resolve files based on a source path.

        Parameters
        ------------
        source : str
          File path where mesh was loaded from
        """
        # remove everything other than absolute path
        clean = os.path.expanduser(os.path.abspath(str(source)))

        if os.path.isdir(clean):
            self.parent = clean
        # get the parent directory of the
        elif os.path.isfile(clean):
            split = os.path.split(clean)
            self.parent = split[0]
        else:
            raise ValueError('path not a file or directory!')

    def get(self, name):
        """
        Get an asset.

        Parameters
        -------------
        name : str
          Name of the asset

        Returns
        ------------
        data : bytes
          Loaded data from asset
        """
        # load the file by path name
        with open(os.path.join(self.parent,
                               name.strip()), 'rb') as f:
            data = f.read()
        return data


class ZipResolver(Resolver):
    """
    Resolve files inside a ZIP archive.
    """

    def __init__(self, archive):
        """
        Resolve files inside a ZIP archive as loaded by
        trimesh.util.decompress

        Parameters
        -------------
        archive : dict
          Contains resources as file object
        """
        self.archive = archive

    def get(self, name):
        """
        Get an asset from the ZIP archive.

        Parameters
        -------------
        name : str
          Name of the asset

        Returns
        -------------
        data : bytes
          Loaded data from asset
        """
        # not much we can do with that
        if name is None:
            return

        # if name isn't in archive try some similar values
        if name not in self.archive:
            if hasattr(name, 'decode'):
                name = name.decode('utf-8')
            # try with cleared whitespace, split paths
            for option in [name,
                           name.lstrip('./'),
                           name.strip(),
                           name.split('/')[-1]]:
                if option in self.archive:
                    name = option
                    break
        # get the stored data
        obj = self.archive[name]
        # if the dict is storing data as bytes just return
        if isinstance(obj, (bytes, str)):
            return obj

        # otherwise get it as a file object
        # read file object from beginning
        obj.seek(0)
        # data is stored as a file object
        data = obj.read()
        obj.seek(0)
        return data


class WebResolver(Resolver):
    """
    Resolve assets from a remote URL.
    """

    def __init__(self, url):
        """
        Resolve assets from a base URL.

        Parameters
        --------------
        url : str
          Location where a mesh was stored or
          directory where mesh was stored
        """
        if hasattr(url, 'decode'):
            url = url.decode('utf-8')

        # parse string into namedtuple
        parsed = urlparse(url)

        # we want a base url where the mesh was located
        path = parsed.path
        if path[-1] != '/':
            # clip off last item
            path = '/'.join(path.split('/')[:-1]) + '/'

        # store the base url
        self.base_url = '{scheme}://{netloc}/{path}'.format(
            scheme=parsed.scheme,
            netloc=parsed.netloc,
            path=path)

    def get(self, name):
        """
        Get a resource from the remote site.

        Parameters
        -------------
        name : str
          Asset name, i.e. 'quadknot.obj.mtl'
        """
        # do import here to keep soft dependency
        import requests

        # remove leading and trailing whitespace
        name = name.strip()
        # fetch the data from the remote url
        response = requests.get(urljoin(
            self.base_url, name))

        if response.status_code != 200:
            # try to strip off filesystem crap
            response = requests.get(urljoin(
                self.base_url,
                name.lstrip('./')))

        if response.status_code == '404':
            raise ValueError(response.content)

        # return the bytes of the response
        return response.content
