# - * - coding:utf - 8 - * -
import os
from flask import Flask, request, jsonify, render_template, url_for, send_from_directory
import numpy as np
import face_recognition
from PIL import Image

# 初始化 Flask 应用
app = Flask(__name__)

# 定义路径常量
npy_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data/face_data.npy'))
image_folder_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data'))

# 加载 face_data 数据
def load_face_data():
    print(f"加载人脸数据: {npy_file_path}")  # 打印加载数据的路径
    if os.path.exists(npy_file_path):
        try:
            face_data = np.load(npy_file_path, allow_pickle=True).item()
            return face_data
        except Exception as e:
            print(f"加载人脸编码时出错: {e}")
            return {}
    print("人脸数据文件不存在。")
    return {}

# 保存 face_data 数据
def save_face_data(face_data):
    print(f"保存人脸数据到: {npy_file_path}")  # 打印保存数据的路径
    np.save(npy_file_path, face_data)
    print("人脸数据保存成功。")

# 获取图片的 URL
def get_image_url(name):
    # 使用 Flask 的自定义路由返回图片
    return url_for('serve_face_image', filename=f'{name}.jpg')

# 获取图片的绝对路径
def get_image_path(name):
    image_path = os.path.join(image_folder_path, f'{name}.jpg')
    return os.path.abspath(image_path)  # 返回绝对路径

# 比较人脸编码之间的相似度
def is_same_person(new_encoding, existing_encoding, threshold=0.5):
    """
    判断两个人脸编码是否属于同一个人
    threshold: 距离小于该阈值认为是同一个人
    """
    distance = np.linalg.norm(np.array(new_encoding) - np.array(existing_encoding))
    return distance < threshold

# 检查上传的图片是否有效
def check_image_validity(file_path):
    try:
        img = Image.open(file_path)
        img.verify()  # 验证图片是否有效
        return True
    except (IOError, SyntaxError):
        return False

# 保存图片为标准的 .jpg 格式
def save_image_as_jpeg(file, file_path):
    img = Image.open(file)
    img.convert("RGB").save(file_path, "JPEG")

# 自定义路由来返回图片
@app.route('/face_image/<filename>')
def serve_face_image(filename):
    try:
        return send_from_directory(image_folder_path, filename)
    except Exception as e:
        return jsonify({"error": "Image not found"}), 404

# 检查并创建 image_folder_path
if not os.path.exists(image_folder_path):
    print(f"创建人脸图片文件夹: {image_folder_path}")  # 打印创建的文件夹路径
    os.makedirs(image_folder_path)

# 检查上传的图片是否有效
def check_image_validity(file_path):
    try:
        img = Image.open(file_path)
        img.verify()  # 验证图片是否有效
        print(f"图片 {file_path} 是有效的图像文件。")
    except (IOError, SyntaxError) as e:
        print(f"无效的图片文件: {file_path}, 错误信息: {e}")
        return False
    return True


@app.route('/')
def index():
    print("访问首页，加载已注册人脸...")
    face_data = load_face_data()
    faces_and_images = [
        {"name": name, "image": get_image_url(name)}  # 使用统一的函数生成路径
        for name in face_data.keys()
        if os.path.exists(os.path.join(image_folder_path, f"{name}.jpg"))
    ]
    print(f"加载的已注册人脸: {faces_and_images}")  # 打印加载的所有人脸
    return render_template('index.html', faces_and_images=faces_and_images, error_message=None)

@app.route('/add_face', methods=['POST'])
def add_face():
    print("接收到添加人脸请求...")
    face_data = load_face_data()
    if 'file' not in request.files or 'name' not in request.form:
        return jsonify({"error": "请求中缺少 'file' 或 'name' 参数。"}), 400

    file = request.files['file']
    name = request.form['name']
    print(f"上传的人脸名称: {name}, 文件: {file.filename}")

    try:
        image = face_recognition.load_image_file(file)
        face_encodings = face_recognition.face_encodings(image)

        if not face_encodings:
            return jsonify({"error": "上传的图片中没有检测到人脸。"}), 400

        new_face_encoding = face_encodings[0]
        existing_faces = [face_data[stored_name] for stored_name in face_data.keys()]

        # 检查是否有已存在的相似编码
        for existing_encoding in existing_faces:
            if is_same_person(new_face_encoding, existing_encoding):
                return jsonify({"error": "已有相似的人脸编码，无法添加。"}), 400

        # 检查是否已有相同名字的图片
        file_path = os.path.join(image_folder_path, f"{name}.jpg")
        if os.path.exists(file_path):
            return jsonify({"error": f"'{name}' 的图片已经存在。"}), 400

        # 保存图片为 JPEG 格式
        save_image_as_jpeg(file, file_path)

        # 检查图片是否有效
        if not check_image_validity(file_path):
            return jsonify({"error": "上传的图片无效或损坏。"}), 400

        # 将人脸编码保存
        face_data[name] = new_face_encoding.tolist()
        save_face_data(face_data)

        return jsonify({"success": "人脸添加成功。"}), 200

    except Exception as e:
        return jsonify({"error": f"发生错误: {str(e)}"}), 500

@app.route('/delete_face', methods=['POST'])
def delete_face():
    print("接收到删除人脸请求...")
    face_data = load_face_data()
    name = request.form.get('name')

    if not name:
        print("缺少 'name' 参数。")
        return jsonify({"error": "缺少 'name' 参数。"}), 400

    if name not in face_data:
        print(f"未找到名为 '{name}' 的人脸数据。")
        return jsonify({"error": f"未找到名为 '{name}' 的人脸数据。"}), 404

    try:
        # 删除面部数据
        del face_data[name]
        save_face_data(face_data)

        # 删除图片
        image_path = os.path.join(image_folder_path, f"{name}.jpg")
        if os.path.exists(image_path):
            os.remove(image_path)
            print(f"成功删除图片: {image_path}")
        else:
            print(f"未找到图片文件: {image_path}")

        print(f"成功删除人脸: {name}")
        return jsonify({"success": f"人脸 '{name}' 删除成功。"}), 200

    except Exception as e:
        print(f"发生错误: {str(e)}")
        return jsonify({"error": f"发生错误: {str(e)}"}), 500

@app.route('/list_faces', methods=['GET'])
def list_faces():
    print("获取已注册人脸列表...")
    face_data = load_face_data()
    faces_with_images = [
        {"name": name, "image": get_image_url(name)}  # 使用统一的函数生成路径
        for name in face_data.keys()
        if os.path.exists(os.path.join(image_folder_path, f'{name}.jpg'))
    ]
    print(f"已注册人脸列表: {faces_with_images}")  # 打印已注册人脸
    return jsonify({"faces": faces_with_images}), 200

if __name__ == '__main__':
    print("启动 Flask 应用...")
    app.run(host='0.0.0.0', port=5000, debug=False)
