import zlib
import sys
import ctypes
import datetime
import struct
import re
import os
import platform


# 从文件中读取block_size大小，计算CRC32
def crc32_simple(filepath):
    try:
        with open(filepath, 'rb') as f:
            s = f.read(1024 * 1024)
            return zlib.crc32(s, 0)
    except Exception as e:
        print(str(e))
        return 0


# 计算整个文件的crc32
def crc32_file(filepath):
    crc = 0
    try:
        fd = open(filepath, 'rb')
        while True:
            buffer = fd.read(1024 * 1024)
            if len(buffer) == 0:  # EOF or file empty. return hashes
                fd.close()
                if sys.version_info[0] < 3 and crc < 0:
                    crc += 2 ** 32
                return crc  # 返回的是十进制的值
            crc = zlib.crc32(buffer, crc)
    except Exception as e:
        if sys.version_info[0] < 3:
            error = unicode(e)
        else:
            error = str(e)
        print(error)
        return 0


# 根据文件大小 返回合理区间，16473740 bytes--> 15.727 MB
def FormatSize(size):
    # 递归实现，精确为最大单位值 + 小数点后三位
    def formatsize(integer, remainder, level):
        if integer >= 1024:
            remainder = integer % 1024
            integer //= 1024
            level += 1
            return formatsize(integer, remainder, level)
        else:
            return integer, remainder, level

    units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
    integer, remainder, level = formatsize(size, 0, 0)
    if level + 1 > len(units):
        level = -1
    return ('{}.{:>03d} {}'.format(integer, remainder, units[level]))


# 转换时间
def get_time(filetime):
    # convert a windows FILETIME to a python datetime
    # https://stackoverflow.com/questions/39481221/convert-datetime-back-to-windows-64-bit-filetime
    WINDOWS_TICKS = int(1 / 10 ** -7)  # 10,000,000 (100 nanoseconds or .1 microseconds)
    WINDOWS_EPOCH = datetime.datetime.strptime('1601-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
    POSIX_EPOCH = datetime.datetime.strptime('1970-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
    EPOCH_DIFF = (POSIX_EPOCH - WINDOWS_EPOCH).total_seconds()  # 11644473600.0
    WINDOWS_TICKS_TO_POSIX_EPOCH = EPOCH_DIFF * WINDOWS_TICKS  # 116444736000000000.0

    """Convert windows filetime winticks to python datetime.datetime."""
    winticks = struct.unpack('<Q', filetime)[0]
    microsecs = (winticks - WINDOWS_TICKS_TO_POSIX_EPOCH) / WINDOWS_TICKS
    return datetime.datetime.fromtimestamp(microsecs).strftime('%Y-%m-%d %H:%M:%S')


class Everything:
    # defines 定义参看Everything.h
    EVERYTHING_REQUEST_FILE_NAME = 0x00000001
    EVERYTHING_REQUEST_PATH = 0x00000002
    EVERYTHING_REQUEST_SIZE = 0x00000010
    EVERYTHING_REQUEST_DATE_MODIFIED = 0x00000040

    EVERYTHING_SORT_SIZE_DESCENDING = 6

    def __init__(self):
        # dll imports
        pwd = os.path.dirname(__file__)
        if platform.architecture()[0] == '32bit':
            self.everything_dll = ctypes.WinDLL(os.path.join(pwd, "Everything32.dll"))
        else:
            self.everything_dll = ctypes.WinDLL(os.path.join(pwd, "Everything64.dll"))
        # everything_dll = ctypes.WinDLL(r"E:\\PYProject\\pyqt5\\everything\\Everything64.dll")
        self.everything_dll.Everything_GetResultDateModified.argtypes = [ctypes.c_int,
                                                                         ctypes.POINTER(ctypes.c_ulonglong)]
        self.everything_dll.Everything_GetResultSize.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_ulonglong)]
        self.everything_dll.Everything_GetResultFileNameW.argtypes = [ctypes.c_int]
        self.everything_dll.Everything_GetResultFileNameW.restype = ctypes.c_wchar_p

    # 关键词搜索
    def searchfile(self, bookName):
        recom = re.compile(r'[《》:：、；.，,;—— -()（）【】\'\"]')
        keyword = recom.sub(' ', bookName).strip()
        if len(keyword) < 1:
            return
        # 文件大小倒序
        self.everything_dll.Everything_SetSort(self.EVERYTHING_SORT_SIZE_DESCENDING)
        self.everything_dll.Everything_SetSearchW(keyword)
        self.everything_dll.Everything_SetRequestFlags(
            self.EVERYTHING_REQUEST_FILE_NAME | self.EVERYTHING_REQUEST_PATH | self.EVERYTHING_REQUEST_SIZE | self.EVERYTHING_REQUEST_DATE_MODIFIED)

        # execute the query
        self.everything_dll.Everything_QueryW(1)

        # get the number of results
        num_results = self.everything_dll.Everything_GetNumResults()

        # show the number of results
        result = "\nResult Count: {}\n".format(num_results)
        print(keyword, result)

        # create buffers
        file_name = ctypes.create_unicode_buffer(260)
        file_modi = ctypes.c_ulonglong(1)
        file_size = ctypes.c_ulonglong(1)

        # show results
        results = []  # 创建一个空列表来保存所有结果
        for i in range(num_results):
            self.everything_dll.Everything_GetResultFullPathNameW(i, file_name, 260)
            self.everything_dll.Everything_GetResultDateModified(i, file_modi)
            self.everything_dll.Everything_GetResultSize(i, file_size)
            filepath = ctypes.wstring_at(file_name)
            if filepath.endswith('.lnk') or filepath.endswith('.txt'):
                continue
            # 计算文件crc32,格式化为0x1122AAFF
            filecrc = hex(crc32_file(filepath)).upper().replace("0X", "0x")
            filesize = FormatSize(file_size.value)
            modtime = get_time(file_modi)
            # strInfo = "\nFilePath: {}\nSize: {}    CRC32:{}    modtime:{}".format(filepath, filesize, filecrc, modtime)
            # print(strInfo)
            # 创建一个字典包含文件的所有相关信息
            file_info = {
                "file_name": os.path.basename(filepath),
                "file_path": os.path.dirname(filepath),
                "file_size": filesize,
                "modified_time": modtime
            }

            results.append(file_info)  # 将字典添加到结果列表中
        return results


if __name__ == "__main__":
    everythine = Everything()
    everythine.searchfile("科大讯飞")
