#!/usr/bin/env python
# -*- coding:utf-8 -*-

# Copyright (c) 2020
# @Author :  GitOPEN
# @Email  :  gitopen@gmail.com
# @Date   :  2020-10-29 13:38:36
# @Description :  Powered by GitOPEN

__version__ = "0.5.01"

import asyncio
import json
from pathlib import Path
import time
from typing import Callable, Iterable, List, Optional, Union

from gitopenlib.utils import basics as gb


def if_path_exist_then_backup(pathes: Union[str, List[str]]) -> bool:
    """
    检查路径是否存在，如路径存在，则备份。

    Args:
        pathes: 路径，可以是单个字符串或者字符串的列表。

    Returns:
        bool: 如果有文件被备份了，则为True，否则为False
    """
    if isinstance(pathes, str):
        pathes = [pathes]

    has_backup_files = False
    for path in pathes:
        path = Path(path)
        if path.exists():
            path.rename(
                path.with_suffix(
                    f'.{str(time.strftime("%Y%m%d_%H%M%S",time.localtime()))}{path.suffix}'
                )
            )
            has_backup_files = True
    return has_backup_files


def new_dirs(dir_paths: Union[str, List[str]]) -> List[str]:
    """
    初始化文件夹，检验文件夹的存在状态，并返回准备好的文件夹路径。

    Args:
        dir_paths: 字符串类型的全路径，可以为单个路径，也可以放入列表中，批量创建。

    Returns:
        包含创建的文件夹路径字符串的list
    """
    if isinstance(dir_paths, str):
        dir_paths = [dir_paths]

    for dir in dir_paths:
        dir_path = Path(dir)
        dir_path.mkdir(parents=True, exist_ok=True)

    return dir_paths


def get_paths_from_dir(
    dirs: Union[str, List[str]],
    types: Optional[Union[str, List[str]]] = None,
    recusive: bool = False,
) -> List[str]:
    """
    从指定目录下获取所有指定扩展名文件的路径

    Args:
        dirs: 文件夹路径（绝对路径），单个用str表示，多个用list
        types: 指定文件的扩展名，单个用str表示，多个用list，默认为None，表示所有类型文件
        recusive: 默认为False，不递归子文件夹

    Returns:
        文件路径字符串列表
    """

    if isinstance(dirs, str):
        dirs = [dirs]
    if types is not None:
        if isinstance(types, str):
            types = [types]
        rule = "**/*." if recusive else "*."
    else:
        rule = "**/*" if recusive else "*"
        types = [""]

    result = []
    for d in dirs:
        path = Path(d).resolve()
        for t in types:
            res = path.glob(rule + t)
            result.extend(list(res))

    return [str(item) for item in result]


def file_writer(
    lines: Union[str, Iterable[str]],
    dir_path: Union[str, Path],
    file_name: str = "file_writer.txt",
    mode: str = "a+",
    separator: str = "\n",
    encoding: str = "utf-8",
    backup: bool = True,
) -> None:
    """向文件中写内容

    Args:
        lines: 可以是单个字符串或者字符串的列表
        dir_path: 文件的绝对路径
        file_name: 文件名称，有默认值
        mode: 写文件的的模式，默认为 a+
        separator: 每一行末尾的分隔符，有默认值
        encoding: 文件的编码格式，默认为utf-8
        backup: 如果文件已经存在，是否对原文件进行备份，默认为True；
            如果为False，当文件已经存在的情况下，会追加到存在的文件中。
    """
    dir_path: Path = Path(dir_path) if isinstance(dir_path, str) else dir_path

    if not dir_path.exists():
        print(
            "## Warning: The dir path you submit is not exist, "
            + "it will be created automatically."
        )
        dir_path.mkdir(parents=True, exist_ok=True)

    file_path = dir_path / file_name

    if backup:
        if file_path.exists():
            file_path.rename(
                file_path.with_suffix(
                    f'.{str(time.strftime("%Y%m%d_%H%M%S",time.localtime()))}{file_path.suffix}'
                )
            )

    if isinstance(lines, str):
        lines = [lines]

    with open(file_path, mode=mode, encoding=encoding) as file:
        for line in lines:
            if separator != "":
                file.write(f"{line}{separator}")
            else:
                file.write(line)


def read_txt_by_page(
    file_path: str,
    parse_func: Callable[[list], None],
    page_size: int = 100,
    encoding: str = "utf-8",
) -> None:
    """
    从文本文件中分页读取内容

    Args:
        file_path: 文件路径
        parse_func: 每一页数据处理函数，必须定义
        page_size: 每一页数据量
        encoding: 文本文件的编码格式
    """
    with open(file=file_path, encoding=encoding, mode="r") as file:
        data = list()
        curr_page_id = 0
        start_time = time.time()
        total_time = 0.0
        print("## read_txt_by_page is starting parse the data...")
        for line in file:
            data.append(line.strip())
            if len(data) == page_size:
                parse_func(data)
                data.clear()
                end_time = time.time()
                cost_time = float(end_time - start_time)
                total_time += cost_time
                start_time = end_time
                print(f"## -{curr_page_id}- page parsed done...{[cost_time]}s")
                curr_page_id += 1

            pass
        if len(data) > 0:
            parse_func(data)
            end_time = time.time()
            cost_time = float(end_time - start_time)
            total_time += cost_time
            start_time = end_time
            print(f"## -{curr_page_id}- page parsed done...{[cost_time]}s")
            curr_page_id += 1
    print(f"## All done. Total pages: {curr_page_id}. Elapsed time: {total_time}s")


def read_txt_by_page_asyncio(
    file_path: str,
    parse_func: Callable[[list], None],
    page_size: int = 100,
    encoding: str = "utf-8",
    open_async: bool = False,
    slave_num: int = 4,
) -> None:
    """
    从文本文件中分页读取内容，具备异步io处理功能

    Args:
        file_path: 文件路径
        parse_func: 每一页数据处理函数，必须定义
        page_size: 每一页数据量
        encoding: 文本文件的编码格式
        open_async: 是否开启异步io处理数据，默认不开启
        slave_num: 执行任务的协程数目，默认为4
    """

    async def parse_(loop, chunk):
        run_func = lambda: parse_func(chunk)
        await loop.run_in_executor(None, run_func)

    with open(file=file_path, encoding=encoding, mode="r") as file:
        data = list()
        curr_page_id = 0
        start_time = time.time()
        total_time = 0.0
        print("## read_txt_by_page is starting parse the data...")
        for line in file:
            data.append(line.strip())
            if len(data) == page_size:
                if open_async:
                    loop = asyncio.get_event_loop()
                    chunks = gb.chunks(data, slave_num)
                    loop.run_until_complete(
                        asyncio.gather(*[parse_(loop, chunk) for chunk in chunks])
                    )
                    chunks.clear()
                else:
                    parse_func(data)
                # parse_func(data)
                data.clear()
                end_time = time.time()
                cost_time = float(end_time - start_time)
                total_time += cost_time
                start_time = end_time
                print(f"## -{curr_page_id}- page parsed done...{[cost_time]}s")
                curr_page_id += 1

            pass
        if len(data) > 0:
            if open_async:
                loop = asyncio.get_event_loop()
                chunks = gb.chunks(data, slave_num)
                loop.run_until_complete(
                    asyncio.gather(*[parse_(loop, chunk) for chunk in chunks])
                )
                chunks.clear()
            else:
                parse_func(data)
            # parse_func(data)
            end_time = time.time()
            cost_time = float(end_time - start_time)
            total_time += cost_time
            start_time = end_time
            print(f"## -{curr_page_id}- page parsed done...{[cost_time]}s")
            curr_page_id += 1
    print(f"## All done. Total pages: {curr_page_id}. Elapsed time: {total_time}s")


def read_content(
    file_path: Union[str, Path],
    encoding: str = "utf-8",
) -> List[str]:
    """
    从文本文件中读取内容，将内容转换为list，list的元素为每行的字符串

    Args:
        file_path : 文件路径
        encoding : 文件的编码方式

    Returns:
        每行字符串组成的list
    """
    if isinstance(file_path, str):
        file_path = Path(file_path)
    result = gb.remove_0_str(
        [line.strip() for line in file_path.read_text(encoding=encoding).split("\n")]
    )
    return result


def read_contents(
    file_pathes: List,
    encoding: str = "utf-8",
) -> List[str]:
    """
    从[多个]文本文件中读取内容，将[所有]内容转换为list，list的元素为每行的字符串

    Args:
        file_pathes : 文件路径列表
        encoding : 文件的编码方式

    Returns:
        每行字符串组成的list
    """
    result = []
    for path in file_pathes:
        result.extend(read_content(path, encoding))
    return result


def read_jsons(file_path: Union[str, Path], encoding: str = "utf-8") -> List[dict]:
    """
    从存放json的文本文件中读取内容，并转化为dict组成的list

    Args:
        file_path: 文件路径
        encoding: 文件的编码方式

    Returns:
        dict组成的list
    """
    if isinstance(file_path, str):
        file_path = Path(file_path)
    result = gb.remove_0_str(
        [line.strip() for line in file_path.read_text(encoding=encoding).split("\n")]
    )

    return [json.loads(item) for item in result]
