#2024-12-04 add forehead_chin_points_pair,estimate_rotatios
#formart is first,second,middle
#2024-12-05 deg to rad
#2024-12-06 get_feature_ratios_cordinate
#2024-12-08 create_detail_labels
horizontal_points_pair = [
        [
            "inner-eye",133,362,6
        ],
        [
            "outer-eye",33,263,168
        ],
        [
            "mouth",61,291,13
        ],
         [
            "eyeblow",105,334,9
        ],[
            "nose",98,327,2
        ],[
            "contour",143,372,6
        ],
        [
            "chin",32,262,200
        ], [
            "cheek",123,352,5
        ], [
            "cheek2",192,416,0
        ], [
            "nose1",129,358,1
        ], [
            "nose2",47,277,195
        ], [
            "cheek3",206,426,2
        ], [
            "cheek4",101,330,5
        ], [
            "cheek5",153,380,6
        ]
    ]
def angle_between_points_and_x_axis(A, B):
        """
        2点A, Bを結ぶ線分とx軸の正方向との角度を計算する

        Args:
            A: A点の座標 (x, y) のタプルまたはNumPy配列
            B: B点の座標 (x, y) のタプルまたはNumPy配列

        Returns:
            角度(ラジアン)
        """
        x = B[0] - A[0]
        y = B[1] - A[1]
        return np.arctan2(y, x)

vertical_points_pair=[
    ["forehead-chin",8,1,199]
]
#formart is first,second,third
feature_ratios_indices=[
        ["forehead",67,69,66],
        ["forehead",10,151,9],
        ["forehead",297,299,296],
         #["forehead-chin",8,1,199],
         #["middle-chin",168,199,2],
        ["middle",168,195,2],
        ["right",153,101,206],
        ["right2",133,47,129],
        ["left",380,330,426],
        ["left2",362,277,358],
        ["right-contour",143,123,192],
        ["left-contour",372,352,416],
         ["nose",4,1,2],
    ]

feature_angles_indices =[
    ["forehead1",9,6],
    ["forehead2",69,299],
    ["eyes1",133,362],
    ["eyes2",133,33],
    ["eyes3",362,263],
    ["nose1",6,2],
    ["nose1",98,327],
    ["nose1",2,1],
    ["nose1",1,6],
    ["lip",61,291],
    ["lip",0,17],
    ["jaw",152,199],
    ["jaw",194,418],
    ["cheek",118,214],
    ["cheek",347,434],
    ["contour",389,397],
     ["contour",127,172],
]
def get_feature_angles_cordinate(face_landmarks,angles=feature_angles_indices):
    points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
    return get_feature_angles_cordinate_points(points,angles)

def get_feature_angles_cordinate_points(points,angles=feature_angles_indices):
    cordinates=[]
    result_angles = []
    for indices in angles:
        points_cordinate = get_points_by_indices(points,indices[1:])#first one is label
        angle_rad =angle_between_points_and_x_axis(points_cordinate[0][:2],points_cordinate[1][:2])
        result_angles.append(angle_rad)
        cordinates.append(points_cordinate)
    return cordinates,result_angles

def get_feature_ratios_cordinate(face_landmarks,ratios=feature_ratios_indices):
    points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
    return get_feature_angles_cordinate_points(points,ratios)

def ratios_cordinates(cordinates):
    
    distance_a = calculate_distance(cordinates[0],cordinates[1])
    distance_b = calculate_distance(cordinates[1],cordinates[2])
    if distance_a == 0 or distance_b == 0:
        return 0
    else:
        return distance_a/distance_b
    
def get_feature_ratios_cordinate_points(points,ratios=feature_ratios_indices):
    cordinates=[]
    result_ratios = []
    for indices in ratios:
        points_cordinate = get_points_by_indices(points,indices[1:])#first one is label
        result_ratios.append(ratios_cordinates(points_cordinate))
        cordinates.append(points_cordinate)
    return cordinates,result_ratios


#vertical-format
forehead_chin_points_pair=[
    [
        "forehead-chin",8,1,199
    ]
]
horizontal_contour_points_pair=[
    [
        "contour",143,6,372
    ]
]
import math
def calculate_distance(xy, xy2):
    return math.sqrt((xy2[0] - xy[0])**2 + (xy2[1] - xy[1])**2)

def create_detail_labels(values,radian=False,pair_data=horizontal_points_pair):
    assert len(values) == len(pair_data)
    lines = []
    for i,value in enumerate(values):
        if radian:
            value=math.degrees(value)
        lines.append(f"{pair_data[i][0]} = {value:.2f}")
    return "\n".join(lines)

import numpy as np
from mp_utils import get_normalized_cordinate
def estimate_horizontal(face_landmarks,pair_data = horizontal_points_pair):
    points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
    return estimate_horizontal_points(points,pair_data)

def get_points_by_indices(face_landmark_points,indices):
    points = [face_landmark_points[index] for index in indices]
    return points

def normalized_to_pixel(cordinates,width,height):
    pixel_point = [[pt[0]*width,pt[1]*height] for pt in cordinates]
    return pixel_point

def estimate_horizontal_points(face_landmark_points,pair_data = horizontal_points_pair):
    z_angles=[]
    y_ratios = []
    cordinates = []
    for compare_point in pair_data:
        points_cordinate = get_points_by_indices(face_landmark_points,compare_point[1:])#first one is label
        cordinates.append(points_cordinate)
        angle_rad =angle_between_points_and_x_axis(points_cordinate[0][:2],points_cordinate[1][:2])
        #angle_deg = np.degrees(angle_rad)
        z_angles.append(angle_rad)
        right_distance = calculate_distance(points_cordinate[0],points_cordinate[2])
        left_distance = calculate_distance(points_cordinate[1],points_cordinate[2])
        y_ratios.append(left_distance/(right_distance+left_distance))
    return z_angles,y_ratios,cordinates,pair_data

def estimate_vertical(face_landmarks,pair_data = vertical_points_pair):
    points = [get_normalized_cordinate(face_landmarks,i) for i in range(468)]
    return estimate_vertical_points(points,pair_data)


def estimate_rotations_v2(face_landmarker_result):
    points = get_normalized_landmarks(face_landmarker_result.face_landmarks,True)
    values1_text=estimate_rotations_point(points)
    result3,ratios = get_feature_ratios_cordinate_points(points)
    key_cordinates,angles = get_feature_angles_cordinate_points(points)
    angles_str=[str(angle) for angle in angles]
    ratios_str=[str(ratio) for ratio in ratios]            
    return f"{values1_text},{','.join(angles_str)},{','.join(ratios_str)}"
    
from mp_utils import get_normalized_landmarks 
def estimate_rotations(face_landmarker_result):
    points = get_normalized_landmarks(face_landmarker_result.face_landmarks,True)
    return estimate_rotations_point(points)
def estimate_rotations_point(points):
    z_angles,y_ratios,h_cordinates,_ =estimate_horizontal_points(points)
    z_angle = np.mean(z_angles)
    y_ratio = np.mean(y_ratios)
    _,x_ratios,h_cordinates,_ =estimate_vertical_points(points)
    x_ratio = np.mean(x_ratios)

    x_angle,_,_,_ =estimate_vertical_points(points,forehead_chin_points_pair)
    x_angle=np.mean(x_angle)
    
    length_ratio = estimate_ratio(points)

    result = f"{x_ratio:.6f},{y_ratio:.6f},{z_angle:.6f},{x_angle:.6f},{length_ratio:.6f}"
    return result

def estimate_ratio(face_landmark_points,a_line=forehead_chin_points_pair,b_line=horizontal_contour_points_pair):
    points_cordinate_a = get_points_by_indices(face_landmark_points,a_line[0][1:])#for campatible
    points_cordinate_b = get_points_by_indices(face_landmark_points,b_line[0][1:])
    
    distance_a = calculate_distance(points_cordinate_a[0],points_cordinate_a[2])
    distance_b = calculate_distance(points_cordinate_b[0],points_cordinate_b[2])
    if distance_a == 0 or distance_b == 0:
        return 0
    else:
        return distance_a/distance_b

def estimate_vertical_points(face_landmarks,pair_data = vertical_points_pair):
    angles = []
    ratios = []
    cordinates = []
    for compare_point in pair_data:
        points_cordinate = get_points_by_indices(face_landmarks,compare_point[1:])#first one is label
        cordinates.append(points_cordinate)
        angle_rad =angle_between_points_and_x_axis(points_cordinate[0][:2],points_cordinate[2][:2])
        #angle_deg = np.degrees(angle_rad)
        angles.append(angle_rad)
        up_distance = calculate_distance(points_cordinate[0],points_cordinate[1])
        down_distance = calculate_distance(points_cordinate[1],points_cordinate[2])
        ratios.append(down_distance/(down_distance+up_distance))
    return angles,ratios,cordinates,pair_data
def mean_std_label(values,radian=False):
    mean_value = np.mean(values)
    std_value = np.std(values)
    if radian:
        mean_value = math.degrees(mean_value)
        std_value = math.degrees(std_value)
    value_text = f"mean:{mean_value:.3f} std:{std_value:.3f}"
    return value_text