#
# Copyright (C) 2012 - 2021 Satoru SATOH <satoru.satoh@gmail.com>
# SPDX-License-Identifier: MIT
#
"""Utility funtions to process file and file paths.
"""
import functools
import os.path
import os
import re
import pathlib
import typing

from ..common import (
    GLOB_MARKER, IOInfo, PathOrIOInfoT
)
from .detectors import (
    is_file_stream, is_ioinfo
)


def get_file_extension(file_path: str) -> str:
    """
    Get file extension from the path `file_path`.
    """
    default = ''

    if not file_path:
        return default

    _ext = os.path.splitext(file_path)[-1]
    if _ext:
        return _ext[1:] if _ext.startswith('.') else _ext

    return default


def get_path_from_stream(strm: typing.IO, safe: bool = False) -> str:
    """
    Try to get file path from given file or file-like object 'strm'.

    :param strm: A file or file-like object might have its file path info
    :return: file path or None
    :raises: ValueError
    """
    if not is_file_stream(strm) and not safe:
        raise ValueError(f'It does not look a file[-like] object: {strm!r}')

    path = getattr(strm, 'name', None)
    if path is not None:
        try:
            return str(pathlib.Path(path).resolve())
        except (TypeError, ValueError):
            pass

    return ''


@functools.lru_cache()
def split_re(marker: str, sep: str = os.path.sep) -> typing.Pattern:
    """Generate a regexp pattern object to split path by marker.
    """
    return re.compile(r'([^{0}]+){1}(.*\{0}.*)'.format(marker, sep))


def split_path_by_marker(path: str, marker: str = GLOB_MARKER,
                         sep: str = os.path.sep
                         ) -> typing.Tuple[str, str]:
    """
    Split given path string by the marker.

    >>> split_path_by_marker('a.txt')
    ('a.txt', '')
    >>> split_path_by_marker('*.txt')
    ('', '*.txt')
    >>> split_path_by_marker('a/*.txt')
    ('a', '*.txt')
    >>> split_path_by_marker('a/b/*.txt')
    ('a/b', '*.txt')
    >>> split_path_by_marker('a/b/*/*.txt')
    ('a/b', '*/*.txt')
    """
    if marker not in path:
        return (path, '')

    if sep not in path:
        return ('', path)

    matched = split_re(marker, sep=sep).match(path)
    if not matched:
        raise ValueError(f'Invalid path: {path}')

    return typing.cast(typing.Tuple[str, str], matched.groups())


def expand_paths_itr(paths: typing.Union[typing.Iterable[PathOrIOInfoT],
                                         PathOrIOInfoT],
                     marker: str = GLOB_MARKER
                     ) -> typing.Iterator[PathOrIOInfoT]:
    """
    :param paths:
        A glob path pattern string or pathlib.Path object holding such path, or
        a list consists of path strings or glob path pattern strings or
        pathlib.Path object holding such ones.

    :param marker: A character or string to globbing paths
    """
    if isinstance(paths, (str, pathlib.Path)):
        if isinstance(paths, pathlib.Path):
            paths = str(paths)

        (base, pattern) = split_path_by_marker(paths, marker=marker)

        if not pattern:
            yield pathlib.Path(base)
            return

        base_2 = pathlib.Path(os.curdir if not base else base).resolve()
        for path in sorted(base_2.glob(pattern)):
            yield path

    elif is_file_stream(paths):
        yield paths  # type: ignore

    elif is_ioinfo(paths):
        yield typing.cast(IOInfo, paths)

    else:
        for path in paths:  # type: ignore
            for cpath in expand_paths_itr(path, marker=marker):
                yield cpath


def expand_paths(paths: typing.Union[typing.Iterable[PathOrIOInfoT],
                                     PathOrIOInfoT],
                 marker: str = GLOB_MARKER
                 ) -> typing.Iterable[PathOrIOInfoT]:
    """
    :param paths:
        A glob path pattern string or pathlib.Path object holding such path, or
        a list consists of path strings or glob path pattern strings or
        pathlib.Path object holding such ones, or file objects
    :param marker: Glob marker character or string, e.g. '*'
    """
    return list(expand_paths_itr(paths, marker=marker))


def are_same_file_types(objs: typing.List[IOInfo]) -> bool:
    """
    Are given objects, pathlib.Path or io, same type (have same extension)?
    """
    if not objs:
        return False

    ext = objs[0].extension
    return all(p.extension == ext for p in objs[1:])

# vim:sw=4:ts=4:et:
