# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/11_dcom.ipynb.

# %% auto 0
__all__ = ['DecompressionStrategy', 'DecompArgs', 'StatusArgs', 'RichTaskQ', 'RichProgQ', 'GuiArgs',
           'DecompressionStrategyKeywords', 'StatusKeywords', 'GuiKeywords', 'decompress', 'decompresser']

# %% ../nbs/11_dcom.ipynb 5
import os
from pathlib import Path

from typing import TypeAlias, Callable, TypedDict, Optional

# %% ../nbs/11_dcom.ipynb 6
try: from rich.progress import Progress, Task
except ImportError: pass

try: from tqdm.auto import tqdm
except ImportError: pass

# %% ../nbs/11_dcom.ipynb 8
from chck import isnone, notnone
from quac import pathlike, richtask, richprog, tqdmauto

# %% ../nbs/11_dcom.ipynb 10
from .util import path2str

# %% ../nbs/11_dcom.ipynb 13
DecompressionStrategy: TypeAlias = Callable[[Path, bool, bool, bool], pathlike]
'''Decompression Strategy functions have methods with signature: `(path, hush, exists_ok, cleanup)`'''

DecompArgs: TypeAlias = tuple[bool, bool, bool]
'''
`psrc` decompression method positional arguments are 
`(hush: bool = False, exists_ok: bool = False, cleanup: bool = False)`
'''

StatusArgs: TypeAlias = tuple[int, int, list, bool]
'''
`psrc` recursive status keywords arguments are 
`(done: int, total: int, entries: list, remove: bool)`
'''
RichTaskQ: TypeAlias = Optional[richtask]
RichProgQ: TypeAlias = Optional[richprog]
GuiArgs: TypeAlias = tuple[RichTaskQ, RichProgQ, tqdmauto]
'''
`psrc` recursive gui keywords arguments are 
`(task: rich.progress.Task, progress: rich.progress.Progress, tqdm_bar: tqdm.auto.tqdm)`
'''

class DecompressionStrategyKeywords(TypedDict):
    '''`psrc` recursive typed dict keywords arguments are 
    `{'hush': bool, 'exists_ok': bool, 'cleanup': bool}`'''
    hush: bool; exists_ok: bool; cleanup: bool;

class StatusKeywords(TypedDict):
    '''`psrc` recursive typed dict keywords arguments are 
    `{'done': int, 'total': int, 'entries': list, 'remove': bool}`'''
    done: int; total: int; entries: list; remove: bool;

class GuiKeywords(TypedDict):
    '''`psrc` recursive typed dict keywords arguments are 
    `{'task': rich.progress.Task, 'progress': rich.progress.Progress, 'tqdm_bar': tqdm.auto.tqdm}`'''
    task: RichTaskQ; progress: RichProgQ; tqdm_bar: tqdmauto;



# %% ../nbs/11_dcom.ipynb 15
def refresh_gui(total: int, gui_kws: GuiKeywords = dict(task=None, progress=None, tqdm_bar=None)):
    if notnone(progress := gui_kws.get('progress', None)):
        progress.update(gui_kws.get('task', None), total=total)
        gui_kws.update(progress=progress)
    if notnone(tqdm_bar := gui_kws.get('tqdm_bar', None)):
        tqdm_bar.total = total
        tqdm_bar.refresh()
        gui_kws.update(tqdm_bar=tqdm_bar)
        return gui_kws

def increment_gui(total: int, inc: int = 1, gui_kws: GuiKeywords = dict(task=None, progress=None, tqdm_bar=None)) -> int:
    total += inc
    gui_kws = refresh_gui(total, gui_kws)
    return total, gui_kws

def decrement_gui(total: int, dec: int = 1, gui_kws: GuiKeywords = dict(task=None, progress=None, tqdm_bar=None)) -> int:
    total -= dec
    gui_kws = refresh_gui(total, gui_kws)
    return total, gui_kws

# %% ../nbs/11_dcom.ipynb 17
def decompress(
    root: Path, strategy: DecompressionStrategy, 
    strategy_kws: DecompressionStrategyKeywords = dict(hush=False, exists_ok=False, cleanup=False),
    recurser_kws: StatusKeywords = dict(done=0, total=0, entries=None, remove=True),
    gui_kws: GuiKeywords = dict(task=None, progress=None, tqdm_bar=None),
):
    
    if isnone(entries := recurser_kws.get('entries', None)):
        recurser_kws.update(
            entries = (entries := os.listdir(root)),
            total = (total := len(entries))
        )

    files = [Path(root) / entry for entry in entries]

    if notnone(progress := gui_kws.get('progress', None)):
        with progress:            
            gui_kws.update(task=(task := progress.add_task("[cyan]Extracting...", total=total)), progress=progress)
            for file in files:
                decompresser(file, strategy, strategy_kws, recurser_kws, gui_kws)
                progress.advance(task)
    else:
        with tqdm(total=total, desc='Extracting') as tqdm_bar:            
            gui_kws.update(task=None, tqdm_bar=tqdm_bar)
            for file in files:
                decompresser(file, strategy, strategy_kws, recurser_kws, gui_kws)
                tqdm_bar.update()


def decompresser(
    current: pathlike, strategy: DecompressionStrategy, 
    strategy_kws: DecompressionStrategyKeywords = dict(hush=False, exists_ok=False, cleanup=False),
    recurser_kws: StatusKeywords = dict(done=0, total=0, entries=None, remove=True),
    gui_kws: GuiKeywords = dict(task=None, progress=None, tqdm_bar=None),
):    
    if isnone(entries := recurser_kws.get('entries', None)):
        recurser_kws.update(
            entries = (entries := os.listdir(root)),
            total = (total := len(entries))
        )
    
    filename = path2str(current)
    nextfile = strategy(filename, **strategy_kws)
    recurser_kws.update(done = recurser_kws.get('done', 0) + 1)
    nextfile = Path(nextfile)
    
    # If the decompressed file is a directory, recursively decompress it
    if nextfile.is_dir():
        for root, dirs, files in os.walk(nextfile):
            for f in files:
                total, gui_kws = increment_gui(total, 1, gui_kws)
                recurser_kws.update(total=total)
                decompresser(Path(root) / f, strategy, strategy_kws, recurser_kws, gui_kws)
                
    elif recurser_kws.get('remove', False):
        if filename in entries:
            entries.remove(filename)
            total, gui_kws = decrement_gui(total, 1, gui_kws)
            recurser_kws.update(total=total, entries=entries)            
