from copy import deepcopy
from dataclasses import dataclass, field
from typing import Dict


@dataclass
class BoneDefinition:
    head: str
    tail: str
    lengths: list = field(default_factory=list)
    median: float = 0.0
    stdev: float = 0.0
    category: str = ''
    parent_bone: str = ''
    x_rotation_limit_min: float = 0.0
    x_rotation_limit_max: float = 0.0
    z_rotation_limit_min: float = 0.0
    z_rotation_limit_max: float = 0.0
    new_head_metacarpal_ratio: float = 0.0,
    angle_offset: float = 0.0

# TODO: Adjust the x and z rotation limits for hand bones to make the
# Range of motion limit less stiff
_BONE_DEFINITIONS: Dict[str, BoneDefinition] = {
    'pelvis.R': BoneDefinition(
        head='hips_center',
        tail='right_hip',
        parent_bone='pelvis',
    ),
    'pelvis.L': BoneDefinition(
        head='hips_center',
        tail='left_hip',
        parent_bone='pelvis',
    ),
    'spine': BoneDefinition(
        head='hips_center',
        tail='trunk_center',
        parent_bone='pelvis',
    ),
    'spine.001': BoneDefinition(
        head='trunk_center',
        tail='neck_center',
        parent_bone='spine',
    ),
    'neck': BoneDefinition(
        head='neck_center',
        tail='head_center',
        parent_bone='spine.001',
    ),
    'head_nose': BoneDefinition(
        head='head_center',
        tail='nose',
        parent_bone='neck',
    ),  # Auxiliary bone from head center to nose tip to align the face bones
    'shoulder.R': BoneDefinition(
        head='neck_center',
        tail='right_shoulder',
        parent_bone='spine.001',
    ),
    'shoulder.L': BoneDefinition(
        head='neck_center',
        tail='left_shoulder',
        parent_bone='spine.001',
    ),
    'upper_arm.R': BoneDefinition(
        head='right_shoulder',
        tail='right_elbow',
        parent_bone='shoulder.R',
    ),
    'upper_arm.L': BoneDefinition(
        head='left_shoulder',
        tail='left_elbow',
        parent_bone='shoulder.L',
    ),
    'forearm.R': BoneDefinition(
        head='right_elbow',
        tail='right_wrist',
        parent_bone='upper_arm.R',
    ),
    'forearm.L': BoneDefinition(
        head='left_elbow',
        tail='left_wrist',
        parent_bone='upper_arm.L',
    ),
    'hand.R': BoneDefinition(
        head='right_wrist',
        tail='right_hand_middle',
        category='',
        parent_bone='forearm.R',
    ),
    'hand.L': BoneDefinition(
        head='left_wrist',
        tail='left_hand_middle',
        category='',
        parent_bone='forearm.L',
    ),
    'thumb.carpal.R': BoneDefinition(
        head='right_hand_wrist',
        tail='right_hand_thumb_cmc',
        category='palm',
        parent_bone='hand.R',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=40,
        z_rotation_limit_max=80,
    ),  # Auxiliary bone to align the right_hand_thumb_cmc empty
    'thumb.carpal.L': BoneDefinition(
        head='left_hand_wrist',
        tail='left_hand_thumb_cmc',
        category='palm',
        parent_bone='hand.L',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=40,
        z_rotation_limit_max=80,
    ),  # Auxiliary bone to align the left_hand_thumb_cmc empty
    'thumb.01.R': BoneDefinition(
        head='right_hand_thumb_cmc',
        tail='right_hand_thumb_mcp',
        category='palm',
        parent_bone='thumb.carpal.R',
        x_rotation_limit_min=-50,
        x_rotation_limit_max=50,
        z_rotation_limit_min=-60,
        z_rotation_limit_max=30,
    ),
    'thumb.01.L': BoneDefinition(
        head='left_hand_thumb_cmc',
        tail='left_hand_thumb_mcp',
        category='palm',
        parent_bone='thumb.carpal.L',
        x_rotation_limit_min=-50,
        x_rotation_limit_max=50,
        z_rotation_limit_min=-60,
        z_rotation_limit_max=30,
    ),
    'thumb.02.R': BoneDefinition(
        head='right_hand_thumb_mcp',
        tail='right_hand_thumb_ip',
        category='proximal_phalanx',
        parent_bone='thumb.01.R',
        x_rotation_limit_min=-20,
        x_rotation_limit_max=40,
        z_rotation_limit_min=-50,
        z_rotation_limit_max=50,
    ),
    'thumb.02.L': BoneDefinition(
        head='left_hand_thumb_mcp',
        tail='left_hand_thumb_ip',
        category='proximal_phalanx',
        parent_bone='thumb.01.L',
        x_rotation_limit_min=-40,
        x_rotation_limit_max=20,
        z_rotation_limit_min=-50,
        z_rotation_limit_max=50,
    ),
    'thumb.03.R': BoneDefinition(
        head='right_hand_thumb_ip',
        tail='right_hand_thumb_tip',
        category='distal_phalanx',
        parent_bone='thumb.02.R',
        x_rotation_limit_min=-10,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'thumb.03.L': BoneDefinition(
        head='left_hand_thumb_ip',
        tail='left_hand_thumb_tip',
        category='distal_phalanx',
        parent_bone='thumb.02.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=10,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'palm.01.R': BoneDefinition(
        head='right_hand_wrist',
        tail='right_hand_index_finger_mcp',
        category='palm',
        parent_bone='hand.R',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=12,
        z_rotation_limit_max=18,
        new_head_metacarpal_ratio=0.22,
        angle_offset=15.0,
    ),
    'palm.01.L': BoneDefinition(
        head='left_hand_wrist',
        tail='left_hand_index_finger_mcp',
        category='palm',
        parent_bone='hand.L',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=12,
        z_rotation_limit_max=18,
        new_head_metacarpal_ratio=0.22,
        angle_offset=15.0,
    ),
    'f_index.01.R': BoneDefinition(
        head='right_hand_index_finger_mcp',
        tail='right_hand_index_finger_pip',
        category='proximal_phalanx',
        parent_bone='palm.01.R',
        x_rotation_limit_min=-30,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_index.01.L': BoneDefinition(
        head='left_hand_index_finger_mcp',
        tail='left_hand_index_finger_pip',
        category='proximal_phalanx',
        parent_bone='palm.01.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=30,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_index.02.R': BoneDefinition(
        head='right_hand_index_finger_pip',
        tail='right_hand_index_finger_dip',
        category='intermediate_phalanx',
        parent_bone='f_index.01.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-2, # Setting a few degrees to have a minimum range to scale
        z_rotation_limit_max=2,
    ),
    'f_index.02.L': BoneDefinition(
        head='left_hand_index_finger_pip',
        tail='left_hand_index_finger_dip',
        category='intermediate_phalanx',
        parent_bone='f_index.01.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-2,
        z_rotation_limit_max=2,
    ),
    'f_index.03.R': BoneDefinition(
        head='right_hand_index_finger_dip',
        tail='right_hand_index_finger_tip',
        category='distal_phalanx',
        parent_bone='f_index.02.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=60,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'f_index.03.L': BoneDefinition(
        head='left_hand_index_finger_dip',
        tail='left_hand_index_finger_tip',
        category='distal_phalanx',
        parent_bone='f_index.02.L',
        x_rotation_limit_min=-60,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'palm.02.R': BoneDefinition(
        head='right_hand_wrist',
        tail='right_hand_middle_finger_mcp',
        category='palm',
        parent_bone='hand.R',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'palm.02.L': BoneDefinition(
        head='left_hand_wrist',
        tail='left_hand_middle_finger_mcp',
        category='palm',
        parent_bone='hand.L',
        x_rotation_limit_min=-20,
        x_rotation_limit_max=20,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'f_middle.01.R': BoneDefinition(
        head='right_hand_middle_finger_mcp',
        tail='right_hand_middle_finger_pip',
        category='proximal_phalanx',
        parent_bone='palm.02.R',
        x_rotation_limit_min=-30,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_middle.01.L': BoneDefinition(
        head='left_hand_middle_finger_mcp',
        tail='left_hand_middle_finger_pip',
        category='proximal_phalanx',
        parent_bone='palm.02.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=30,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_middle.02.R': BoneDefinition(
        head='right_hand_middle_finger_pip',
        tail='right_hand_middle_finger_dip',
        category='intermediate_phalanx',
        parent_bone='f_middle.01.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-2,
        z_rotation_limit_max=2,
    ),
    'f_middle.02.L': BoneDefinition(
        head='left_hand_middle_finger_pip',
        tail='left_hand_middle_finger_dip',
        category='intermediate_phalanx',
        parent_bone='f_middle.01.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-2,
        z_rotation_limit_max=2,
    ),
    'f_middle.03.R': BoneDefinition(
        head='right_hand_middle_finger_dip',
        tail='right_hand_middle_finger_tip',
        category='distal_phalanx',
        parent_bone='f_middle.02.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=60,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'f_middle.03.L': BoneDefinition(
        head='left_hand_middle_finger_dip',
        tail='left_hand_middle_finger_tip',
        category='distal_phalanx',
        parent_bone='f_middle.02.L',
        x_rotation_limit_min=-60,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'palm.03.R': BoneDefinition(
        head='right_hand_wrist',
        tail='right_hand_ring_finger_mcp',
        category='palm',
        parent_bone='hand.R',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=-18,
        z_rotation_limit_max=-12,
        new_head_metacarpal_ratio=0.24,
        angle_offset=15.0,
    ),
    'palm.03.L': BoneDefinition(
        head='left_hand_wrist',
        tail='left_hand_ring_finger_mcp',
        category='palm',
        parent_bone='hand.L',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=-18,
        z_rotation_limit_max=-12,
        new_head_metacarpal_ratio=0.24,
        angle_offset=15.0,
    ),
    'f_ring.01.R': BoneDefinition(
        head='right_hand_ring_finger_mcp',
        tail='right_hand_ring_finger_pip',
        category='proximal_phalanx',
        parent_bone='palm.03.R',
        x_rotation_limit_min=-30,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_ring.01.L': BoneDefinition(
        head='left_hand_ring_finger_mcp',
        tail='left_hand_ring_finger_pip',
        category='proximal_phalanx',
        parent_bone='palm.03.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=30,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_ring.02.R': BoneDefinition(
        head='right_hand_ring_finger_pip',
        tail='right_hand_ring_finger_dip',
        category='intermediate_phalanx',
        parent_bone='f_ring.01.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-2,
        z_rotation_limit_max=2,
    ),
    'f_ring.02.L': BoneDefinition(
        head='left_hand_ring_finger_pip',
        tail='left_hand_ring_finger_dip',
        category='intermediate_phalanx',
        parent_bone='f_ring.01.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-2,
        z_rotation_limit_max=2,
    ),
    'f_ring.03.R': BoneDefinition(
        head='right_hand_ring_finger_dip',
        tail='right_hand_ring_finger_tip',
        category='distal_phalanx',
        parent_bone='f_ring.02.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=60,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'f_ring.03.L': BoneDefinition(
        head='left_hand_ring_finger_dip',
        tail='left_hand_ring_finger_tip',
        category='distal_phalanx',
        parent_bone='f_ring.02.L',
        x_rotation_limit_min=-60,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'palm.04.R': BoneDefinition(
        head='right_hand_wrist',
        tail='right_hand_pinky_mcp',
        category='palm',
        parent_bone='hand.R',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=-30,
        z_rotation_limit_max=-20,
        new_head_metacarpal_ratio=0.30,
        angle_offset=25.0,
    ),
    'palm.04.L': BoneDefinition(
        head='left_hand_wrist',
        tail='left_hand_pinky_mcp',
        category='palm',
        parent_bone='hand.L',
        x_rotation_limit_min=-180,
        x_rotation_limit_max=180,
        z_rotation_limit_min=-30,
        z_rotation_limit_max=-20,
        new_head_metacarpal_ratio=0.30,
        angle_offset=25.0,
    ),
    'f_pinky.01.R': BoneDefinition(
        head='right_hand_pinky_mcp',
        tail='right_hand_pinky_pip',
        category='proximal_phalanx',
        parent_bone='palm.04.R',
        x_rotation_limit_min=-30,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_pinky.01.L': BoneDefinition(
        head='left_hand_pinky_mcp',
        tail='left_hand_pinky_pip',
        category='proximal_phalanx',
        parent_bone='palm.04.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=30,
        z_rotation_limit_min=-10,
        z_rotation_limit_max=10,
    ),
    'f_pinky.02.R': BoneDefinition(
        head='right_hand_pinky_pip',
        tail='right_hand_pinky_dip',
        category='intermediate_phalanx',
        parent_bone='f_pinky.01.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=90,
        z_rotation_limit_min=-2,
        z_rotation_limit_max=2,
    ),
    'f_pinky.02.L': BoneDefinition(
        head='left_hand_pinky_pip',
        tail='left_hand_pinky_dip',
        category='intermediate_phalanx',
        parent_bone='f_pinky.01.L',
        x_rotation_limit_min=-90,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-2,
        z_rotation_limit_max=2,
    ),
    'f_pinky.03.R': BoneDefinition(
        head='right_hand_pinky_dip',
        tail='right_hand_pinky_tip',
        category='distal_phalanx',
        parent_bone='f_pinky.02.R',
        x_rotation_limit_min=0,
        x_rotation_limit_max=60,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'f_pinky.03.L': BoneDefinition(
        head='left_hand_pinky_dip',
        tail='left_hand_pinky_tip',
        category='distal_phalanx',
        parent_bone='f_pinky.02.L',
        x_rotation_limit_min=-60,
        x_rotation_limit_max=0,
        z_rotation_limit_min=-1,
        z_rotation_limit_max=1,
    ),
    'thigh.R': BoneDefinition(
        head='right_hip',
        tail='right_knee',
        parent_bone='pelvis.R',
    ),
    'thigh.L': BoneDefinition(
        head='left_hip',
        tail='left_knee',
        parent_bone='pelvis.L',
    ),
    'shin.R': BoneDefinition(
        head='right_knee',
        tail='right_ankle',
        parent_bone='thigh.R',
    ),
    'shin.L': BoneDefinition(
        head='left_knee',
        tail='left_ankle',
        parent_bone='thigh.L',
    ),
    'foot.R': BoneDefinition(
        head='right_ankle',
        tail='right_foot_index',
        parent_bone='shin.R',
    ),
    'foot.L': BoneDefinition(
        head='left_ankle',
        tail='left_foot_index',
        parent_bone='shin.L',
    ),
    'heel.02.R': BoneDefinition(
        head='right_ankle',
        tail='right_heel',
        parent_bone='shin.R',
    ),
    'heel.02.L': BoneDefinition(
        head='left_ankle',
        tail='left_heel',
        parent_bone='shin.L',
    ),
}

def get_bone_definitions():
    """
    Returns a deep copy of the bone definitions, to ensure the base definitions isn't modified.
    """
    return deepcopy(_BONE_DEFINITIONS)