import io
from pathlib import Path
from typing import List, Dict, Any, Union, Generator
import urllib.request as request
import urllib.parse as urlparse
import zlib
from os.path import splitext
import pickle
import fnmatch

"""
   Copyright 2017 David B. Bracewell

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""


class Resource(object):
    """
    Abstraction of a resource (e.g. file, string, url) that can be read from or written to.
    """

    def reader(self, params=None):
        """
        Opens the resource in read mode
        :param params: parameters to be used in the open (specific to each implementation)
        :return: a stream that can be read from
        """
        raise NotImplementedError

    def writer(self, params=None):
        """
        Opens the resource in write mode
        :param params: parameters to be used in the open (specific to each implementation)
        :return: a stream that can be written to
        """
        raise NotImplementedError

    def read(self, params=None) -> str:
        """
        Reads the resource into a string
        :param params: parameters to be used in the open (specific to each implementation)
        :return: the string contents of the resource
        """
        params = self._mkparams(params)
        encoding = params["encoding"] if "encoding" in params else "utf-8"
        compression = params["compress"] if "compress" in params else False
        if compression:
            params["mode"] = "rb"
        with self.reader(params) as reader:
            if compression:
                return zlib.decompress(reader.read()).decode(encoding)
            return reader.read()

    def readlines(self, params=None) -> List[str]:
        """
        Reads the resource line by line into a list of strings
        :param params: parameters to be used in the open (specific to each implementation)
        :return: the string contents of the resource
        """
        return self.read(params).splitlines()

    def lines(self, params=None):
        with self.reader(params) as rdr:
            for line in rdr:
                yield line

    def write(self, content: str, params=None) -> None:
        """
        Writes the given content to the resource
        :param content: The content to write
        :param params: parameters to be used in the open (specific to each implementation)
        :return: None
        """
        encoding = params["encoding"] if "encoding" in params else "utf-8"
        compression = params["compress"] if "compress" in params else False
        if compression:
            params["mode"] = "wb"
        with self.writer(params) as writer:
            if compression:
                writer.write(zlib.compress(content.encode(encoding)))
            else:
                writer.write(content)

    def descriptor(self) -> str:
        raise NotImplementedError

    def __str__(self) -> str:
        return self.descriptor()

    def __repr__(self) -> str:
        return self.descriptor()

    def __iter__(self):
        with self.reader() as rdr:
            for line in rdr:
                yield line

    def path(self) -> str:
        """
        :return: The path of the resource
        """
        return ""

    def ext(self) -> str:
        """
        :return: the extension of the file
        """
        return ""

    def basename(self) -> str:
        """
        :return: the basename of the file
        """
        return ""

    def children(self, recursive: bool = False, pattern='*.*', include_dirs=True):
        """
        :return: child resources
        """
        return []

    def child(self, subpath: str) -> 'Resource':
        """
        Gets a resource relative to this one
        :param subpath: The subpath
        :return: A resource relative to this one
        """
        return self

    def parent(self) -> 'Resource':
        """
        Gets the parent (resource) (i.e. one level up)
        :return:  The parent resource
        """
        return self

    def is_dir(self) -> bool:
        """
        :return: True if this resource represents a directory
        """
        return False

    def exists(self) -> bool:
        """
        :return: True if the resource exists
        """
        return True

    def mkdirs(self) -> None:
        """
        Makes all directories down to and including this one
        :return: None
        """
        pass

    def write_object(self, object):
        with self.writer({"mode": "wb"}) as w:
            w.write(zlib.compress(pickle.dumps(object)))

    def read_object(self):
        with self.reader({"mode": "rb"}) as r:
            return pickle.load(zlib.decompress(r.read()))

    def _mkparams(self, params: Dict[str, Any]):
        if params is None:
            return {}
        return dict([(k.lower(), v) for k, v in params.items()])


class FileResource(Resource):