"""Simplified input and output."""
import contextlib
import os
import pathlib
import re
import shutil
import sys
from io import StringIO
from pathlib import Path
import requests
from .grep import *
from .head import *
from .sed import *
from .tail import *
[docs]def read(to_be_read: str):
    """
    Open either a file or URI and return the content.
    Reads the file ``to_be_read``.
    Parameters
    ----------
    fname : str
        file name.
    Returns
    -------
    str
        content of the file.
    Examples
    --------
    >>> read("nonexistent.txt")
    ''
    >>> write("test.out","hi")
    >>> read("test.out")
    'hi'
    >>> read("https://raw.githubusercontent.com/APN-Pucky/smpl_io/master/LICENSE").split("\\n")[0].strip()
    'GNU GENERAL PUBLIC LICENSE'
    """
    if to_be_read.startswith("http"):
        return requests.get(to_be_read).text
    elif to_be_read == "-":
        return sys.stdin.read()
    else:
        if not os.path.exists(to_be_read):
            return ""
        with open(to_be_read, "r") as f:
            return f.read() 
[docs]@contextlib.contextmanager
def pushd(new_dir, tmp=False, cd=True):
    """
    Move to a new directory and return to the previous one after context.
    Parameters
    ----------
    new_dir : str
        new directory.
    tmp: bool, optional
        create the directory if it does not exist and delete after context, by default False
    cd: bool, optional
        change to (new) directory, by default True
    Examples
    --------
    >>> import os
    >>> p = os.getcwd()
    >>> with pushd("tmptest",tmp=True):
    ...     pp = os.getcwd()
    ...     pp.startswith(p) and pp.endswith("tmptest")
    True
    >>> import os
    >>> p = os.getcwd()
    >>> with pushd("tmptest",tmp=True,):
    ...     pp = os.getcwd()
    ...     pp.startswith(p) and pp.endswith("tmptest")
    True
    """
    previous_dir = os.getcwd()
    if tmp:
        abspath = os.path.abspath(new_dir)
        os.makedirs(abspath, exist_ok=False)
    if cd:
        os.chdir(new_dir)
    try:
        yield
    finally:
        if cd:
            os.chdir(previous_dir)
        if tmp:
            shutil.rmtree(abspath, ignore_errors=True) 
[docs]def glob_re(pattern, path):
    """
    Returns all strings that match the regex pattern.
    Parameters
    ----------
    pattern : str
        regex pattern.
    path : str
        path to search in.
    Returns
    -------
    list
        list of filenames that match the regex pattern.
    Examples
    --------
    >>> import os
    >>> with pushd("tmptest",tmp=True,cd=False):
    ...     write("tmptest/test.txt","hi\\nho1\\n2\\n3\\n4\\n")
    ...     glob_re(".*[xt][xt][xt]", "tmptest")
    ['test.txt']
    """
    return list(filter(re.compile(pattern).match, os.listdir(path))) 
[docs]def write(destination, content, mode="w+", create_dir=True):
    """
    Write to file by string or writable :obj:`destiantion`.
    Parameters
    ----------
    destination : str, writeable
        destination to write to.
    content : str
        text to be written.
    mode : str
        mode to open the file.
        Default is 'w+' (write and read).
    create_dir : bool
        create directory if it does not exist.
    Examples
    --------
    >>> write(sys.stdout,"hi")
    hi
    >>> write("test.out","hi")
    >>> read("test.out")
    'hi'
    """
    # TODO add http and other string based write methodes
    if isinstance(destination, str):
        if destination == "-":
            print(content)
            return
        if create_dir:
            try:
                os.makedirs(os.path.dirname(destination), exist_ok=True)
            except FileNotFoundError:
                # this happens when the path is a local path, ie. a single file
                pass
        with open(destination, mode) as f:
            f.write(content)
    else:
        destination.write(content) 
[docs]def append(destination, content, mode="a+"):
    """
    Appends to file by string or writable :obj:`destiantion`.
    Parameters
    ----------
    destination : str, writeable
        destination to write to.
    content : str
        text to be written.
    mode : str
        mode to open the file.
        Default is 'a+' (append and read).
    Examples
    --------
    >>> append(sys.stdout,"hi")
    hi
    """
    write(destination, content, mode) 
[docs]def gf(i=3):
    """
    Scientific number format.
    Parameters
    ----------
    i : int
        Number of digits.
    Returns
    -------
    str
        Scientific number format string.
    Examples
    --------
    >>> gf(2)
    '{0:.2g}'
    >>> gf(2).format(789234578934)
    '7.9e+11'
    >>> gf(5).format(789234578934)
    '7.8923e+11'
    """
    return "{0:." + str(i) + "g}" 
[docs]def find_file(fname, up=0):
    """
    Searches for ``fname`` in all down folders or up folder to given order respectively.
    Parameters
    ----------
    fname : str
        file name.
    up : int
        number of up folders to search.
    Returns
    -------
    str
        path to the file.
    Examples
    --------
    >>> import os
    >>> find_file("io.py",0)
    'smpl_io/io.py'
    >>> os.chdir("smpl_io")
    >>> find_file("io.py",0)
    'io.py'
    >>> find_file("Makefile",1)
    '../Makefile'
    """
    p = Path(os.path.abspath(fname)).parent
    n = Path(os.path.abspath(fname)).name
    if (p / n).is_file():
        return str(os.path.relpath(p / n, os.path.abspath(".")))
    for _ in range(up):
        p = p.parent
    for f in p.rglob(n):
        if f.is_file():
            return str(os.path.relpath(f, os.path.abspath(".")))
    return None 
[docs]def pwd() -> str:
    """
    Returns the path to the path of current file (in linux format).
    Returns
    -------
    str
        path to the path of current file.
    Examples
    --------
    >>> pwd().endswith("smpl_io")
    True
    """
    # pwd_ = "/".join(debug.get_line_number_file(split=False, _back=1)[1].split("/")[:-1])
    path = Path(__file__).parent.absolute()
    return str(path) 
[docs]def import_path(path="../.."):
    """
    Adds ``path`` to the ``sys.path``.
    Parameters
    ----------
    path : str
        path to add.
    Examples
    --------
    >>> import_path('../../smpl')
    """
    sys.path.insert(0, os.path.abspath(path)) 
[docs]def mkdirs(fn):
    """
    Creates the neccessary directories above ``fn``.
    Parameters
    ----------
    fn : str
        file name.
    Examples
    --------
    >>> mkdirs("test.out")
    """
    pathlib.Path(fn).parent.mkdir(parents=True, exist_ok=True) 
[docs]def pr(a, nnl=False):
    """
    Prints the passed ``a``.
    Parameters
    ----------
    nnl : bool
        no-new-line
    Returns
    -------
    a : any
        unchanged ``a``.
    Examples
    --------
    >>> 5 + pr(4)
    4
    9
    >>> 5 + pr(4, nnl=True)
    49
    """
    if nnl:
        print(a, end="")
    else:
        print(a)
    return a 
[docs]def files(ending, folder="."):
    """
    Get all the files in ``folder`` ending with ``ending``.
    Parameters
    ----------
    folder : str
        folder name.
    ending : str
        ending of the files.
    Returns
    -------
    list
        list of files.
    Examples
    --------
    >>> files(".ini")
    [(0, 'pytest', './pytest.ini')]
    """
    r = []
    i = 0
    for file in os.scandir(folder):
        if file.path.endswith(ending):
            r.append((i, os.path.splitext(os.path.basename(file.path))[0], file.path))
            i = i + 1
    return r 
[docs]def pn(a, nnl=False):
    """
    Find variable with the same value as ``a`` in globals.
    Then print its name and value.
    Parameters
    ----------
    a : any
        variable to find.
    nnl : bool
        no-new-line
    Returns
    -------
    a : any
        unchanged ``a``.
    """
    gl = globals()
    for key in gl:
        if gl[key] == a:
            print(key)
    if nnl:
        print(f"{a.__name__}={a}", end="")
    else:
        print(f"{a.__name__}={a}")
    return a 
[docs]def remove(file):
    """
    Removes ``file``.
    Parameters
    ----------
    file : str
        file name.
    Examples
    --------
    >>> remove("test.out")
    """
    if os.path.exists(file):
        os.remove(file) 
alias_open = open
[docs]def open(to_be_read: str, mode="r"):
    """
    Return opened buffer of either file or URI.
    Parameters
    ----------
    to_be_read : str
        file name or URI.
    mode : str
        mode to open the file.
    Returns
    -------
    buffer
        opened buffer.
    """
    if mode != "r":
        return alias_open(to_be_read, mode)
    if to_be_read.startswith("http"):
        return StringIO(requests.get(to_be_read).text)
    else:
        if not os.path.exists(to_be_read):
            return StringIO("")
        return alias_open(to_be_read, "r")