import sys
import cv2
import time
import numpy as np
import threading
import socket
import struct, os, select


class GlobVar:
    server_ip = '192.168.1.17'
    server_port = 8000
    name_namelist = ''  # 人脸匹配结果

    event2 = threading.Event()  # 用于告知socket进程，数据已准备好，可以发送给服务器了
    event3 = threading.Event()  # 用于告知socket进程，允许或不允许修改name_namelist

    flag = 0

    st_time = 0  # 记录定时1秒的起始时间
    nd_time = 0  # 记录定时1秒的结束时间

    frame_new = []
    frame_old = []

    sys_path = sys.path[0].replace("\\", "/")
    
    # 加载人脸检测模型
    tensorflowWeightFile = sys_path + "/opencv_face_detector.pbtxt"
    tensorflowModelFile = sys_path + "/opencv_face_detector_uint8.pb"

    conf_threshold = 0.7

    # net = cv2.dnn.readNetFromCaffe(caffeWeightFile, caffeModel=None)   # 未找到caffee的模型文件
    net = cv2.dnn.readNetFromTensorflow(tensorflowModelFile, tensorflowWeightFile)

    faces = np.array([])


class ClientNetworkThread(threading.Thread):

    def __init__(self):
        self._running = True  # 定义线程状态变量
        super().__init__()

    def terminate(self):
        self._running = False

    def run(self):
        while self._running:
            if GlobVar.event2.isSet():
                sock_client_image()
            time.sleep(0.02)


def sock_client_image():
    # 1、建立socket
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((GlobVar.server_ip, GlobVar.server_port))
        s.setblocking(0)  # 设置为非阻塞模式
    except socket.error as msg:
        print("%s L102" % msg)
        print("未连接到服务器 L103")
        try:
            s.close()
        except socket.error as msg:
            print(msg)
        return
    # 2、发送图片数据，如果发送成功则等待接收，如果不成功则不进行接收
    flag = socket_send(s)
    # 3、接收识别结果
    if flag: socket_recv(s)
    # 4、关闭socket
    try:
        s.close()
    except socket.error as msg:
        print(msg)


def action():
    if len(GlobVar.faces) == 1:  # 检测到有且仅有一张人脸，并且保持静止1秒
        if GlobVar.flag == 0:
            GlobVar.flag = 1
            GlobVar.frame_old = GlobVar.frame_new
            GlobVar.st_time = time.time()  # 开始计时
        else:
            if moving_detect(GlobVar.frame_old, GlobVar.frame_new):  # 画面静止
                GlobVar.nd_time = time.time()  # 计算时间是否达到1秒
                # print("已保持不动 {0} 秒".format(end - start))
                if GlobVar.nd_time - GlobVar.st_time >= 1 and GlobVar.flag == 1:  # 计时1秒，时间到
                    GlobVar.flag = 2
                    print('存在单张人脸L55')

            else:  # 画面有变动
                GlobVar.flag = 0
    else:  # 无人脸或有多张人脸
        GlobVar.flag = 0
        if len(GlobVar.faces) > 1:
            print("Error: More than one person！！！")
        else:
            print("No person！")


def function(image):
    GlobVar.faces = []  # 清空
    GlobVar.name_namelist = ""  # 清空
    GlobVar.event2.clear()
    GlobVar.event3.set()
    GlobVar.frame_new = image

    (h, w) = image.shape[:2]
    blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0,
                                 (300, 300), (104.0, 177.0, 123.0))

    GlobVar.net.setInput(blob)
    detections = GlobVar.net.forward()

    for i in range(0, detections.shape[2]):
        # extract the confidence (i.e., probability) associated with the
        # prediction
        confidence = detections[0, 0, i, 2]
        # filter out weak detections by ensuring the `confidence` is
        # greater than the minimum confidence
        if confidence < GlobVar.conf_threshold:
            continue
        # compute the (x, y)-coordinates of the bounding box for the
        # object
        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
        (startX, startY, endX, endY) = box.astype("int")

        GlobVar.faces.append(i)
        GlobVar.faces[i] = [startX, startY, endX, endY]
        # cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 255), 1)

    # 再根据人脸个数做出判断
    action()

    # 若存在单张人脸且静止时间达到1秒
    if GlobVar.flag == 2:
        print("返回人脸位置坐标，并且发送图片到server")
        GlobVar.flag = 0
        saveimage(h, w)
        GlobVar.event2.set()  # 告知子进程数据已准备好

    GlobVar.event3.clear()
    name = GlobVar.name_namelist
    GlobVar.event3.set()

    # 返回faces和name
    return GlobVar.faces, name


def moving_detect(img1, img2):  # 比较两幅画面，判断是否静止
    grey_diff = cv2.absdiff(img1, img2)  # 计算两幅图的像素差
    change = np.average(grey_diff)
    if change > 10:
        return False  # 画面有明显变动，返回False
    else:
        return True  # 画面无明显变动，返回True


def saveimage(height, width):
    # 提取人脸区域并保存成图片
    for (x, y, endx, endy) in GlobVar.faces:
        (w, h) = (endx - x, endy - y)
        left = int(x - 0.25 * w)
        if left < 0: left = 0
        right = int(x + 1.25 * w)
        if right > width: right = width
        top = int(y - 0.25 * h)
        if top < 0: top = 0
        bottom = int(y + 1.25 * h)
        if bottom > height: bottom = height

    cropped_image = GlobVar.frame_new[top:bottom, left:right]
    cv2.imwrite("singleface.jpg", cropped_image)


def socket_send(s):
    file_path = 'singleface.jpg'  # 输入当前目录下的图片名
    # pack按照图片格式、图片名字、图片大小

    fsend = struct.pack(b'128sq',  # 图片打包的格式为128sq
                        bytes(os.path.basename(file_path), encoding='utf-8'),  # 返回图片文件名，编码字节化
                        os.stat(file_path).st_size)  # 返回读取文件的相关属性，然后得到其中st_size的数据(记录文件大小)
    try:
        s.send(fsend)
    except socket.error as msg:
        print(msg)
        return 0  # 数据发送失败，返回0

    fp = open(file_path, 'rb')  # 打开要传输的图片

    while True:
        data = fp.read(1024)  # 读入图片数据,data是图片的数据字节流(由于图片字节很多，这里一次只读取1024个字节)
        if not data:  # 如果读到字节的数据没有了
            print('{0} send over...'.format(len(data)))
            break
        try:
            s.send(data)  # 以二进制格式发送图片数据(每次发1024个字节)
        except socket.error as msg:
            print(msg)
            return 0  # 数据发送出错，返回0

    print("数据已发送,开始接收...")
    GlobVar.event2.clear()
    return 1  # 数据发送完毕，返回1


def socket_recv(s):
    st1 = time.time()
    response = ""

    while True:
        infds, outfds, errfds = select.select([s, ], [], [])
        if len(infds) > 0:  # 有数据返回，不管是否成功接收，均跳出while循环
            try:
                buf = s.recv(1024).decode('utf-8')
                print("received: " + str(buf))
                response += str(buf)
                break
            except socket.error as msg:
                print(msg)
                break
            except BlockingIOError:
                break

        else:
            print("no data comes back")

        st2 = time.time()
        if st2 - st1 > 0.5:
            break  # 等待超时
    if GlobVar.event3.isSet():
        GlobVar.name_namelist = response
