# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/46_helper.files_and_folders.ipynb.

# %% auto 0
__all__ = ['existing_path', 'file_existence_test', 'path_name_no_ext', 'path_no_ext', 'text_from_file', 'files_of_format_sorted',
           'file_is_compressed', 'uncompress_file', 'get_download_path', 'get_huggingface_cache_dir']

# %% ../../nbs/46_helper.files_and_folders.ipynb 1
import bz2
import errno
import gzip
import lzma
import os
from os import PathLike
from pathlib import Path
import platform
import sys
import tarfile
from typing import Optional
import zipfile

from deprecated import deprecated
import glob
from natsort import natsorted


# %% ../../nbs/46_helper.files_and_folders.ipynb 2
if sys.platform.startswith('win'):
    import winreg
else:
    winreg = None

# %% ../../nbs/46_helper.files_and_folders.ipynb 6
def existing_path(
        path: PathLike,  # A file or directory path. Either absolute or relative to `relative_to`.
        relative_to: Optional[PathLike] = None  # Path to the directory that `file` is relative to.  If `None`, then `path` is an absolute path.
        ) -> Path: # The path formed by `relative_to` adjoined with `path`.  Defaults to `None`
    """Returns a path relative to a specified path as an absolute path
    that exists.

    **Raises**

    - `FileNotFoundError`
        - If `relative_to` is not `None` but does not exist, or if
        `file` does not exist.
    - `ValueError`
        - If `relative_to` is not `None` and yet not an absolute path, or
        if `relative_to` is `None` at yet `path` is not an absolute path.
    
    **Notes**
    - This function may add the string `'\\\\?\\'` in front, which identifies
    very long paths.
    """
    if relative_to is not None:
        if not os.path.isabs(relative_to):
            raise ValueError(
                f'The parameter `relative_to` is expected to be an'
                f' absolute path, but it is not: {relative_to}')
        if not os.path.exists(relative_to):
            raise FileNotFoundError(
                errno.ENOENT, os.strerror(errno.ENOENT), relative_to)
        path = Path(relative_to) / path
    elif not os.path.isabs(path):
        raise ValueError(
            f'The parameter `path` is expected to be an absolute path,'
            f' but it is not: {path}')
    if not os.path.exists(path) and platform.system() == 'Windows':
        path = f'\\\\?\\{str(path)}'  # For long file names
    if not os.path.exists(path):
        raise FileNotFoundError(
            errno.ENOENT, os.strerror(errno.ENOENT), path)
    return Path(path)


@deprecated(reason="The function has been renamed to `existing_path`")
def file_existence_test(
        path: PathLike,  # A file or directory path. Either absolute or relative to `relative_to`.
        relative_to: Optional[PathLike] = None  # Path to the directory that `file` is relative to.  If `None`, then `path` is an absolute path.
        ) -> Path: # The path formed by `relative_to` adjoined with `path`.  Defaults to `None`
    """
    **Deprecated. Use `existing_path` instead.**
    
    Returns a path relative to a specified path as an absolute path
    that exists.

    **Raises**
    - `FileNotFoundError`
        - If `relative_to` is not `None` but does not exist, or if
        `file` does not exist.
    
    **Notes**
    - This function may add the string `'\\\\?\\'` in front, which identifies
    very long paths.
    """
    if relative_to is not None:
        if not os.path.isabs(relative_to):
            raise ValueError(
                f'The parameter `relative_to` is expected to be an'
                f' absolute path, but it is not: {relative_to}')
        if not os.path.exists(relative_to):
            raise FileNotFoundError(
                errno.ENOENT, os.strerror(errno.ENOENT), relative_to)
        path = Path(relative_to) / path
    elif not os.path.isabs(path):
        raise ValueError(
            f'The parmaeter `path` is expected to be an absolute path,'
            f' but it is not: {path}')
    if not os.path.exists(path) and platform.system() == 'Windows':
        path = f'\\\\?\\{str(path)}'  # For long file names
    if not os.path.exists(path):
        raise FileNotFoundError(
            errno.ENOENT, os.strerror(errno.ENOENT), path)
    return Path(path)

# %% ../../nbs/46_helper.files_and_folders.ipynb 19
def path_name_no_ext(
        path: PathLike # The path of the file or directory. This may be absolute or relative to any directory.
        ) -> str: # The name of the file or directory without the extension.
    """Return the name of a file or directory from its path without the
    extension.
    
    The file or directory does not have to exist.
    """
    name_with_extension = os.path.basename(path)
    return os.path.splitext(name_with_extension)[0]

# %% ../../nbs/46_helper.files_and_folders.ipynb 26
def path_no_ext(
    path: PathLike # The path of the file or directory. This may be absolute or relative to any directory.
    ) -> str: # The path of the file or directory without the extension. If `path` is a path to a directory, then the output should be essentially the same as `path`.
    """Returns the path of a file or directory without the extension.
    
    The file or directory does not have to exist.
    """
    return os.path.splitext(str(path))[0]

# %% ../../nbs/46_helper.files_and_folders.ipynb 30
def text_from_file(
        path: PathLike, # The absolute path of the file.
        encoding: str = 'utf8' # The encoding of the file to be read. Defaults to `'utf8'`.
        ) -> str: # The entire text from a file
    """Return the entire text from a file.

    Assuems that the file can be encoded in the specified `encoding`
    """
    with open(path, 'r', encoding=encoding) as file:
        text = file.read()
        file.close()
    return text

# %% ../../nbs/46_helper.files_and_folders.ipynb 34
def files_of_format_sorted(
        directory: PathLike, # The directory in which to find the files
        extension: str = 'txt' # Extension of the files to find. Defaults to 'txt'.
        ) -> list[str]:
    """Return a list of path str of files in the directory (but not subdirectories)
    sorted via `natsort`.
    """
    return natsorted(glob.glob(str(Path(directory) / f'*.{extension}')))

# %% ../../nbs/46_helper.files_and_folders.ipynb 38
def file_is_compressed(
        filename: str
        ):
    # Define a set of common compressed file extensions
    compressed_extensions = {
        '.zip',
        '.gz',
        '.tar',
        '.tar.gz',
        '.tgz',
        '.bz2',
        '.xz',
        '.7z',
        '.rar',
        '.z',
    }

    # Get the file extension
    _, file_extension = os.path.splitext(filename)

    # Check if the file extension is in the set of compressed extensions
    return file_extension.lower() in compressed_extensions

# %% ../../nbs/46_helper.files_and_folders.ipynb 40
def uncompress_file(
        file_path: PathLike,
        verbose: bool = False
        ):
    # Check the file extension
    file_path = str(file_path)
    _, file_extension = os.path.splitext(file_path)
    uncompressed_files = []

    try:
        if file_extension == '.zip':
            with zipfile.ZipFile(file_path, 'r') as zip_ref:
                zip_ref.extractall(os.path.dirname(file_path))
                uncompressed_files = [os.path.join(os.path.dirname(file_path), name) for name in zip_ref.namelist()]
            if verbose:
                print(f"Uncompressed {file_path} into {os.path.dirname(file_path)}")

        elif file_extension in ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz']:
            with tarfile.open(file_path, 'r:*') as tar_ref:
                tar_ref.extractall(os.path.dirname(file_path))
                uncompressed_files = [os.path.join(os.path.dirname(file_path), name) for name in tar_ref.getnames()]
            if verbose:
                print(f"Uncompressed {file_path} into {os.path.dirname(file_path)}")

        elif file_extension == '.gz':
            output_file_path = file_path[:-3]  # Remove the .gz extension
            with gzip.open(file_path, 'rb') as gz_file:
                with open(output_file_path, 'wb') as out_file:
                    out_file.write(gz_file.read())
            uncompressed_files.append(output_file_path)
            if verbose:
                print(f"Uncompressed {file_path} into {output_file_path}")

        elif file_extension == '.bz2':
            output_file_path = file_path[:-4]  # Remove the .bz2 extension
            with bz2.open(file_path, 'rb') as bz2_file:
                with open(output_file_path, 'wb') as out_file:
                    out_file.write(bz2_file.read())
            uncompressed_files.append(output_file_path)
            if verbose:
                print(f"Uncompressed {file_path} into {output_file_path}")

        elif file_extension == '.xz':
            output_file_path = file_path[:-3]  # Remove the .xz extension
            with lzma.open(file_path, 'rb') as xz_file:
                with open(output_file_path, 'wb') as out_file:
                    out_file.write(xz_file.read())
            uncompressed_files.append(output_file_path)
            if verbose:
                print(f"Uncompressed {file_path} into {output_file_path}")

        else:
            if verbose:
                print(f"Unsupported file format: {file_extension}")

    except Exception as e:
        if verbose:
            print(f"An error occurred while uncompressing {file_path}: {e}")

    return uncompressed_files

# import os
# import zipfile
# import tarfile
# import gzip
# import bz2
# import lzma
# from pathlib import Path

# def uncompress_file(
#         file_path: PathLike,
#         verbose: bool = False
#         ):
#     file_path = Path(file_path)
#     output_dir = file_path.parent
#     uncompressed_files = []

#     try:
#         if file_path.suffix == '.zip':
#             with zipfile.ZipFile(file_path, 'r') as zip_ref:
#                 zip_ref.extractall(output_dir)
#                 uncompressed_files = [output_dir / name for name in zip_ref.namelist()]

#         elif file_path.suffix in ['.tar', '.gz', '.tgz', '.bz2', '.tbz']:
#             if file_path.suffix == '.gz' and not tarfile.is_tarfile(file_path):
#                 # Handle single gzipped file
#                 with gzip.open(file_path, 'rb') as gz_file:
#                     content = gz_file.read()
#                     # Try to get the original filename from the gzip header
#                     original_name = gz_file.name
#                     if original_name:
#                         output_file = output_dir / Path(original_name).name
#                     else:
#                         output_file = output_dir / file_path.stem
#                     output_file.write_bytes(content)
#                 uncompressed_files.append(output_file)
#             else:
#                 # Handle tar archives (including .tar.gz, .tgz, .tar.bz2, .tbz)
#                 with tarfile.open(file_path, 'r:*') as tar_ref:
#                     tar_ref.extractall(output_dir)
#                     uncompressed_files = [output_dir / name for name in tar_ref.getnames()]

#         elif file_path.suffix == '.bz2':
#             with bz2.open(file_path, 'rb') as bz2_file:
#                 content = bz2_file.read()
#                 output_file = output_dir / file_path.stem
#                 output_file.write_bytes(content)
#             uncompressed_files.append(output_file)

#         elif file_path.suffix == '.xz':
#             with lzma.open(file_path, 'rb') as xz_file:
#                 content = xz_file.read()
#                 output_file = output_dir / file_path.stem
#                 output_file.write_bytes(content)
#             uncompressed_files.append(output_file)

#         else:
#             if verbose:
#                 print(f"Unsupported file format: {file_path.suffix}")

#         if verbose:
#             print(f"Uncompressed {file_path} into {output_dir}")

#     except Exception as e:
#         if verbose:
#             print(f"An error occurred while uncompressing {file_path}: {e}")

#     return uncompressed_files


# %% ../../nbs/46_helper.files_and_folders.ipynb 45
def get_download_path() -> str:
    """
    Return the user's download folder
    """
    if os.name == 'nt':  # For Windows
        sub_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
        downloads_guid = '{374DE290-123F-4565-9164-39C4925E467B}'
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key) as key:
            location = winreg.QueryValueEx(key, downloads_guid)[0]
        return location
    else:  # For Unix-based systems (Linux, macOS)
        return os.path.join(os.path.expanduser('~'), 'Downloads')

# %% ../../nbs/46_helper.files_and_folders.ipynb 47
def get_huggingface_cache_dir():
    # Determine the cache directory
    cache_dir = os.environ.get("HF_HOME") or os.environ.get("XDG_CACHE_HOME")
    if not cache_dir:
        home = Path.home()
        cache_dir = home / ".cache" / "huggingface" / "hub"
    else:
        cache_dir = Path(cache_dir) / "huggingface" / "hub"
    return cache_dir
