#!/usr/bin/env python3
#
#
#   Windlib (Useful Functions Library)
#
#
#   Copyright (C) 2021 SNWCreations. All rights reserved.
#
#


# import libraries for functions
import contextlib
import gzip
import os
import platform
import shutil
import sys
import tarfile
import urllib
import zipfile
import requests
import hashlib
from pathlib import Path
from clint.textui import progress

# the copyright message
print('windlib by SNWCreations')
print('Copyright (C) 2021 SNWCreations. All rights reserved.')


def typeof(variate) -> str:
    """
    检测一个变量的类型，返回值是一个字符串。

    :param variate: 被检测的变量
    :return: 一个字符串
    """
    var_type = None
    if isinstance(variate, int):
        var_type = 'int'
    elif isinstance(variate, str):
        var_type = 'str'
    elif isinstance(variate, float):
        var_type = 'float'
    elif isinstance(variate, list):
        var_type = 'list'
    elif isinstance(variate, tuple):
        var_type = 'tuple'
    elif isinstance(variate, dict):
        var_type = 'dict'
    elif isinstance(variate, set):
        var_type = 'set'
    return var_type


def extract(filename: str, target_dir: str) -> None:
    """
    解压缩文件。

    :param filename: 被解压的文件名
    :param target_dir: 解压到
    :return:
    """
    if file_or_dir_exists(target_dir) == 'NOT_FOUND':
        os.mkdir(target_dir)
    if filename.endswith('.zip'):
        zip_file = zipfile.ZipFile(filename)
        for names in zip_file.namelist():
            zip_file.extract(names, target_dir)
        zip_file.close()
    elif filename.endswith('.gz'):
        f_name = filename.replace(".gz", "")
        g_file = gzip.GzipFile(filename)
        open(f_name, "w+").write(g_file.read())
        g_file.close()
    elif filename.endswith('.tar'):
        tar = tarfile.open(filename)
        names = tar.getnames()
        for name in names:
            tar.extract(name, target_dir)
        tar.close()
    elif filename.endswith('.rar'):
        try:
            import rarfile
        except:
            return
        rar = rarfile.RarFile(filename)
        with pushd(target_dir):
            rar.extractall()
        rar.close()
    elif filename.endswith("tar.gz"):
        tar = tarfile.open(filename, "r:gz")
        with pushd(target_dir):
            tar.extractall()
        tar.close()
    else:
        return 'UNKNOWN_FORMAT'
    return target_dir


def get_file(url: str, save_path: str = '.', timeout: int = 10) -> str:
    """
    从互联网下载文件。

    :param url: 被下载文件的URL
    :param save_path: 保存路径，默认为当前路径
    :param timeout: 超时时长，单位为秒，默认为 10
    :return: 下载后的文件名，下载失败返回'DOWNLOAD_FAILED'
    """
    save_path = os.path.abspath(save_path)
    try:
        res = requests.get(url, stream=True, headers={
                           'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'}, timeout=timeout)
    except:
        return 'DOWNLOAD_FAILED'
    total_length = int(res.headers.get('content-length'))
    filename = save_path + '/' + os.path.basename(url)
    with open(filename, "wb") as pypkg:
        for chunk in progress.bar(res.iter_content(chunk_size=1024), expected_size=(total_length/1024) + 1, width=50):
            if chunk:
                pypkg.write(chunk)
    return os.path.abspath(filename)


def file_or_dir_exists(target: str) -> str:
    """
    检查指定的文件(或文件夹)是否存在。

    :param target: 目标路径

    :return: 当目标是目录时，会返回'IS_DIR'，当目标是文件时，会返回'IS_FILE'，当函数找不到目标时，会返回'NOT_FOUND'，当目标不是有效路径是，会返回'TARGET_INVAILD'。
    """
    try:
        f = Path(target)
    except:
        return 'TARGET_INVAILD'
    if f.is_file():
        return 'IS_FILE'
    elif f.is_dir():
        return 'IS_DIR'
    else:
        return 'NOT_FOUND'


def find_files_with_the_specified_extension(file_type: str, folder: str = '.') -> list:
    """
    在目标文件夹中找到具有指定扩展名的文件，返回值是一个列表。

    :param folder: 从哪里查找，默认值为当前目录。

    :param file_type: 一个扩展名，不需要带有 “.” 。例如 "txt", "jar", "md", "class" 或 ".txt" ".jar" ".md" ".class".
    """
    folder = os.path.abspath(folder)
    if not file_type[0] == '.':
        f_type = '.' + file_type
    items = os.listdir(folder)
    file_list = []
    for names in items:
        if names.endswith(f_type):
            file_list.append(names)
    return file_list


def copy_file(src: str or list, dst: str) -> str:
    """
    复制文件（或文件夹）到指定的目录。

    可以通过列表的方式同时将多个文件复制到指定目录。

    :param src: 源文件或目录
    :param dst: 目标路径
    :return: 默认无返回值，若源文件或目录没有找到，会返回'SRC_NOT_FOUND'。
    """
    src_type = typeof(src)
    if not os.path.exists(dst):
        os.makedirs(dst, exist_ok=True)
    if src_type == 'list':
        for tmp in src:
            ftmp = Path(tmp)
            if ftmp.is_file():
                shutil.copyfile(tmp, dst)
            elif ftmp.is_dir():
                shutil.copytree(tmp, dst)
    elif src_type == 'str':
        ftmp = Path(tmp)
        if ftmp.is_file():
            shutil.copyfile(tmp, dst)
        elif ftmp.is_dir():
            shutil.copytree(tmp, dst)
        else:
            return 'SRC_NOT_FOUND'


def is_it_broken(path: str or list) -> bool or list:
    """
    检查一个文件（或目录）是否损坏。

    允许调用时通过列表检查大量文件和目录。

    若使用列表来检查文件，则返回一个记录所有损坏的文件路径的列表。

    :param path: 将要检查的路径
    :return: 布尔值或列表
    """
    if typeof(path) == 'list':
        broken_files = []
        for tmp in path:
            if os.path.lexists(tmp) and not os.path.exists(path):
                broken_files.append(tmp)
        return broken_files
    elif typeof(path) == 'str':
        if os.path.lexists(path) == True:
            if os.path.exists(path):
                return True
            return False
        return False


@contextlib.contextmanager
def pushd(new_dir: str):
    """
    临时切换到一个目录，操作完成后自动返回调用前路径。

    此函数为生成器，请配合 with 语句使用。

    :param new_dir: 将要切换的路径
    """
    previous_dir = os.getcwd()
    os.chdir(new_dir)
    try:
        yield
    finally:
        os.chdir(previous_dir)


def compress_to_zip_file(input_path: str, output_name: str, output_path: str = '.') -> None:
    """
    压缩一个目录下的所有文件到一个zip文件，无返回值。
    :param input_path: 压缩的文件夹路径
    :param output_name: 压缩包名称
    :param output_path: 解压（输出）的路径
    :return:
    """
    f = zipfile.ZipFile(output_path + '/' + output_name,
                        'w', zipfile.ZIP_DEFLATED)
    filelists = []
    for root, dirs, files in os.walk(input_path, topdown=True):
        for name in files:
            filelists.append(os.path.join(root, name))
        for name in dirs:
            filelists.append(os.path.join(root, name))
    for fi in filelists:
        f.write(fi)
    f.close()


def get_sha1(path: str) -> str:
    """
    获取一个文件的SHA1校验值，返回值是一个字符串。

    :param path: 目标文件名
    """
    sha1_obj = hashlib.sha1()
    try:
        a = open(fr'{path}', 'rb')
    except:
        return 'FINVAILD'
    while True:
        b = a.read(128000)
        sha1_obj.update(b)
        if not b:
            break
    a.close()
    return sha1_obj.hexdigest()


def get_md5(path: str) -> str:
    """
    获取一个文件的MD5校验值，返回值是一个字符串。

    :param path: 目标文件名
    """
    md5_obj = hashlib.md5()
    try:
        a = open(path, 'rb')
    except:
        return 'FINVAILD'
    while True:
        b = a.read(128000)
        md5_obj.update(b)
        if not b:
            break
        a.close()
    return md5_obj.hexdigest()
