import os, time
from .memoize import memoize
from . import util

__all__ = [
    "clean_v_file",
    "clean_extra_coq_files",
    "write_bytes_to_file",
    "read_bytes_from_file",
    "write_to_file",
    "read_from_file",
    "restore_file",
]


def clean_extra_coq_files(v_file_name, extra_exts=tuple()):
    for pre in ("", "."):
        for ext in tuple(list(extra_exts) + [".glob", ".vo", ".d", ".v.d", ".aux", ".vos", ".vok"]):
            name = "".join((os.path.dirname(v_file_name[:-2]), os.sep, pre, os.path.basename(v_file_name[:-2]), ext))
            if os.path.exists(name):
                os.remove(name)


def clean_v_file(file_name):
    clean_extra_coq_files(file_name, extra_exts=(".v",))


def backup(file_name, ext=".bak"):
    if not ext:
        raise ValueError
    if os.path.exists(file_name):
        backup(file_name + ext, ext=ext)
        os.rename(file_name, file_name + ext)


@memoize
def memobackup(file_name, ext=".bak"):
    return backup(file_name, ext)


FILE_CACHE = {}


def write_bytes_to_file(file_name, contents, do_backup=False, backup_ext=".bak", memoize=False):
    assert contents is bytes(contents)
    backed_up = False
    while not backed_up:
        try:
            if do_backup:
                if memoize:
                    memobackup(file_name, ext=backup_ext)
                else:
                    backup(file_name, ext=backup_ext)
            backed_up = True
        except IOError as e:
            print("Warning: f.write(%s) failed with %s\nTrying again in 10s" % (file_name, repr(e)))
            time.sleep(10)
    written = False
    while not written:
        try:
            with open(file_name, "wb") as f:
                f.write(contents)
            written = True
            FILE_CACHE[file_name] = (os.stat(file_name).st_mtime, contents)
        except IOError as e:
            print("Warning: f.write(%s) failed with %s\nTrying again in 10s" % (file_name, repr(e)))
            time.sleep(10)


def write_to_file(file_name, contents, *args, **kwargs):
    return write_bytes_to_file(file_name, contents.replace("\n", os.linesep).encode("utf-8"), *args, **kwargs)


def restore_file(file_name, backup_ext=".bak", backup_backup_ext=".unbak"):
    if not os.path.exists(file_name + backup_ext):
        raise IOError
    if os.path.exists(file_name):
        if backup_backup_ext:
            backup(file_name, backup_backup_ext)
        else:
            os.remove(file_name)
    os.rename(file_name + backup_ext, file_name)


def read_bytes_from_file(file_name):
    if file_name in FILE_CACHE.keys() and os.stat(file_name).st_mtime == FILE_CACHE[file_name][0]:
        return FILE_CACHE[file_name][1]
    with open(file_name, "rb") as f:
        FILE_CACHE[file_name] = (os.stat(file_name).st_mtime, f.read())
        return FILE_CACHE[file_name][1]


def read_from_file(file_name):
    return util.normalize_newlines(read_bytes_from_file(file_name).decode("utf-8"))
