# -*- coding: utf-8 -*-

import os
import threading
import tkinter
from tkinter import LEFT, RIGHT, filedialog, messagebox, DISABLED, NORMAL, TOP

import cv2
from PIL import Image, ImageDraw, ImageFont

ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")  # 所用字符列表


# 将256灰度映射到70个字符上
def get_char(r, g, b, alpha=256):
    if alpha == 0:
        return ' '
    length = len(ascii_char)
    gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b)
    unit = (256.0 + 1) / length
    return ascii_char[int(gray / unit)]


def single_picture2char_img(origin_img_path, out_img_path):
    origin_img_width = 0
    origin_img_height = 0
    if os.path.exists(origin_img_path):
        im = Image.open(origin_img_path)
        origin_img_width = im.width
        origin_img_height = im.height
        new_img_width = int(im.width / 6)
        new_img_height = int(im.height / 15)
        im_txt = Image.new("RGB", (origin_img_width, origin_img_height), (255, 255, 255))
        im = im.resize((new_img_width, new_img_height), Image.NEAREST)
        txt = ""
        colors = []
        for i in range(new_img_height):
            for j in range(new_img_width):
                pixel = im.getpixel((j, i))
                colors.append((pixel[0], pixel[1], pixel[2]))
                if len(pixel) == 4:
                    txt += get_char(pixel[0], pixel[1], pixel[2], pixel[3])
                else:
                    txt += get_char(pixel[0], pixel[1], pixel[2])
            txt += '\n'
            colors.append((255, 255, 255))
        dr = ImageDraw.Draw(im_txt)
        font = ImageFont.load_default().font
        x = y = 0
        font_w, font_h = font.getsize(txt[1])
        font_h *= 1.37
        for i in range(len(txt)):
            if txt[i] == '\n':
                x += font_h
                y = -font_w
            dr.text((y, x), txt[i], colors[i])
            y += font_w
        im_txt.save(out_img_path)
    return origin_img_width, origin_img_height


def picture2char_img(img_dir, char_img_dir):
    origin_img_width = 0
    origin_img_height = 0
    files = os.listdir(img_dir)
    for file in files:
        file_path = os.path.join(img_dir, file)
        if os.path.isfile(file_path):
            out_img_path = f"{char_img_dir}/{file}"
            origin_img_width, origin_img_height = single_picture2char_img(file_path, out_img_path)
    return origin_img_width, origin_img_height


def char_img2video(char_img_dir, video_dir, origin_img_width, origin_img_height):
    fourcc = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X')
    video_writer = cv2.VideoWriter(f"{video_dir}/output.mp4", fourcc, 30.0, (origin_img_width, origin_img_height))
    files = sorted(os.listdir(char_img_dir), key=lambda x: int(x[:-4]))
    for file in files:
        filename = os.path.join(char_img_dir, file)
        if os.path.exists(filename):
            img = cv2.imread(filename=filename)
            cv2.waitKey(100)
            video_writer.write(img)
    video_writer.release()


def video2picture(video_path, img_dir):
    vc = cv2.VideoCapture(video_path)
    c = 0
    ret = vc.isOpened()
    while ret:
        c = c + 1
        ret, frame = vc.read()
        if ret:
            img_path = f"{img_dir}/{c}.jpg"
            cv2.imwrite(img_path, frame)
            cv2.waitKey(1)
        else:
            break
    # 视频释放
    vc.release()


def check_or_create_dir(dir_path):
    if os.path.exists(dir_path):
        files = os.listdir(dir_path)
        if files:
            return False
        else:
            return True
    else:
        os.makedirs(dir_path, exist_ok=True)
        return True


def video2char_video(video_path):
    video_dir = os.path.dirname(os.path.abspath(video_path))
    img_dir = f"{video_dir}/img"
    char_img_dir = f"{video_dir}/char_img"
    output_dir = f'{video_dir}/output'
    img_dir_flag = check_or_create_dir(img_dir)
    char_img_dir_flag = check_or_create_dir(char_img_dir)
    output_dir_flag = check_or_create_dir(output_dir)
    if not img_dir_flag:
        messagebox.showerror('错误', f'{img_dir} 不是空的,请删除或者清空此目录')
    elif not char_img_dir_flag:
        messagebox.showerror('错误', f'{char_img_dir} 不是空的,请删除或者清空此目录')
    elif not output_dir_flag:
        messagebox.showerror('错误', f'{output_dir} 不是空的,请删除或者清空此目录')
    if img_dir_flag and char_img_dir_flag and output_dir_flag:
        video2picture(video_path, img_dir)
        origin_img_width, origin_img_height = picture2char_img(img_dir, char_img_dir)
        char_img2video(char_img_dir, output_dir, origin_img_width, origin_img_height)
        label["text"] = "转换完成"
        choose_file_button['state'] = NORMAL
        convert_button['state'] = NORMAL


def choose_video_file_path():
    global filepath
    filepath = filedialog.askopenfilename()


def get_video_file_path():
    if not filepath:
        messagebox.showerror('错误', '没有选择有效的路径,请重新选择')
    else:
        if os.path.isfile(filepath):
            choose_file_button['state'] = DISABLED
            convert_button['state'] = DISABLED
            label["text"] = "视频正在转换,请稍等!!!"
            t = threading.Thread(target=video2char_video, args=(filepath,))
            t.setDaemon(True)
            t.start()
        else:
            messagebox.showerror('错误', '没有选择有效的路径,请重新选择')


if __name__ == '__main__':
    root = tkinter.Tk()
    filepath = None
    label = tkinter.Label(root, text="欢迎使用海军专属的字符视频转换器")
    label.pack(side=TOP)
    choose_file_button = tkinter.Button(root, text="选择文件", command=choose_video_file_path)
    choose_file_button.pack(side=LEFT)
    convert_button = tkinter.Button(root, text="开始转换", command=get_video_file_path)
    convert_button.pack(side=RIGHT)
    screen_width, screen_height = root.maxsize()  # 获取屏幕最大长宽
    w = int((screen_width - 240) / 2)
    h = int((screen_height - 480) / 2)
    root.geometry(f'+{w}+{h}')
    root.resizable(width=False, height=False)
    root.title('字符视频转换器')
    root.mainloop()