# Path: bodies/body_part_tree.py
# class BodypartTree(object):
#     CONNECTION_TYPE_CENTER = 0
#     CONNECTION_TYPE_ATTACHED = 1
#     CONNECTION_TYPE_INSERTED = 2
# 
#     def __init__(self, central_node_name, central_body_part_uid):
#         self.nodes = [BodypartTreeNode(central_node_name, central_body_part_uid, self.CONNECTION_TYPE_CENTER)]
# 
#     def copy(self):
#         return copy.deepcopy(self)
# 
#     def attach(self, parent_node_name, children_node_name, children_body_part_uid):
#         self._bind_new_child_to_parent(
#             parent_node_name, children_node_name, children_body_part_uid, self.CONNECTION_TYPE_ATTACHED)
# 
#     def insert(self, parent_node_name, children_node_name, children_body_part_uid):
#         self._bind_new_child_to_parent(
#             parent_node_name, children_node_name, children_body_part_uid, self.CONNECTION_TYPE_INSERTED)
# 
#     def _bind_new_child_to_parent(self, parent_node_name, children_node_name, children_body_part_uid, connection_type):
#         parent_node = next((node for node in self.nodes if node.name == parent_node_name), None)
#         if parent_node:
#             child_bodypart_node = BodypartTreeNode(children_node_name, children_body_part_uid, connection_type)
#             parent_node.add_child_node(child_bodypart_node)
#             self.nodes.append(child_bodypart_node)
#         else:
#             logger_.error("Tried to bind bodypart {} to non existing parent {}.".format(
#                 children_node_name, parent_node_name))
# 
# Path: components/body.py
# class Body(Component):
#     NAME = "body"
#     """
#     Height is in feet, Weight in pounds
#     """
#     def __init__(self, uid, bodypart_tree, name="", height=0, weight=0, outer_material_uid=None,
#                  inner_material_uid=None, structural_material_uid=None, blood_uid=None):
#         super().__init__()
#         self.uid = uid
#         self.bodypart_tree = bodypart_tree
#         self.name = name
#         self.height = height
#         self.weight = weight
#         self.outer_material_uid = outer_material_uid
#         self.inner_material_uid = inner_material_uid
#         self.structural_material_uid = structural_material_uid
#         self.blood_uid = blood_uid
# 
#     def copy(self):
#         return Body(
#             uid=self.uid,
#             name=self.name,
#             bodypart_tree=self.bodypart_tree.copy(),
#             height=self.height,
#             weight=self.weight,
#             outer_material_uid=self.outer_material_uid,
#             inner_material_uid=self.inner_material_uid,
#             structural_material_uid=self.structural_material_uid,
#             blood_uid=self.blood_uid,
#         )
# 
#     def __str__(self):
#         return "Body({})".format(self.name)
# 
#     def get_body_part(self, uid):
#         for node in self.bodypart_tree.nodes:
#             if node.instance.uid == uid:
#                 return node.instance
# 
#     def get_body_parts(self, uid):
#         body_parts = []
#         for node in self.bodypart_tree.nodes:
#             if not node.instance:
#                 print("WTF")
#             else:
#                 if node.instance.uid == uid:
#                     body_parts.append(node.instance)
# 
#         return body_parts
# 
#     @staticmethod
#     def _random_roll_body_part(body_parts):
#         tries = 0
#         max_tries = 3
#         while tries < max_tries:
#             tries += 1
#             for node in body_parts:
#                 if random.randrange(0, 100) <= node.instance.relative_size:
#                     return node
# 
#         return random.choice(body_parts)
# 
#     def get_random_body_part_for_threat_level(self, threat_level):
#         size_sorted_body_parts = [node for node in self.bodypart_tree.nodes
#                                   if node.instance.threat_level == threat_level]
#         if not size_sorted_body_parts:
#             if threat_level < ThreatLevel.Fatal:
#                 return self.get_random_body_part_for_threat_level(ThreatLevel[threat_level.value + 1])
#             else:
#                 return self.get_random_body_part_by_relative_size()
# 
#         return self._random_roll_body_part(size_sorted_body_parts)
# 
#     def get_random_body_part_by_relative_size(self):
#         size_sorted_body_parts = sorted([node for node in self.bodypart_tree.nodes
#                                          if node.connection_type == BodypartTree.CONNECTION_TYPE_ATTACHED],
#                                         key=lambda node: node.instance.relative_size, reverse=True)
# 
#         return self._random_roll_body_part(size_sorted_body_parts)
# 
#     def get_grasp_able_body_parts(self):
#         return [node.instance for node in self.bodypart_tree.nodes
#                 if PhysicalAbilities.GRASP in node.instance.physical_abilities]
# 
#     def get_physical_abilities(self):
#         abilities = {}
#         for node in self.bodypart_tree.nodes:
#             for ability_name, ability_value in six.iteritems(node.instance.physical_abilities):
#                 if ability_name not in abilities:
#                     abilities[ability_name] = ability_value
#                 else:
#                     if abilities[ability_name] < ability_value:
#                         abilities[ability_name] = ability_value
# 
#         return abilities


from bodies.body_part_tree import BodypartTree
from components.body import Body


def build_humanoid_body():
    humanoid_tree = BodypartTree('torso', 'humanoid_torso')
    humanoid_tree.insert('torso', 'heart', 'humanoid_heart')
    humanoid_tree.insert('torso', 'lungs', 'humanoid_lungs')
    humanoid_tree.attach('torso', 'head', 'humanoid_head')
    humanoid_tree.attach('head', 'left eye', 'humanoid_eye')
    humanoid_tree.attach('head', 'right eye', 'humanoid_eye')
    humanoid_tree.attach('head', 'left ear', 'humanoid_ear')
    humanoid_tree.attach('head', 'right ear', 'humanoid_ear')
    humanoid_tree.attach('head', 'mouth', 'humanoid_mouth')

# Path: bodies/body_part_tree.py
# class BodypartTree(object):
#     CONNECTION_TYPE_CENTER = 0
#     CONNECTION_TYPE_ATTACHED = 1
#     CONNECTION_TYPE_INSERTED = 2
# 
#     def __init__(self, central_node_name, central_body_part_uid):
#         self.nodes = [BodypartTreeNode(central_node_name, central_body_part_uid, self.CONNECTION_TYPE_CENTER)]
# 
#     def copy(self):
#         return copy.deepcopy(self)
# 
#     def attach(self, parent_node_name, children_node_name, children_body_part_uid):
#         self._bind_new_child_to_parent(
#             parent_node_name, children_node_name, children_body_part_uid, self.CONNECTION_TYPE_ATTACHED)
# 
#     def insert(self, parent_node_name, children_node_name, children_body_part_uid):
#         self._bind_new_child_to_parent(
#             parent_node_name, children_node_name, children_body_part_uid, self.CONNECTION_TYPE_INSERTED)
# 
#     def _bind_new_child_to_parent(self, parent_node_name, children_node_name, children_body_part_uid, connection_type):
#         parent_node = next((node for node in self.nodes if node.name == parent_node_name), None)
#         if parent_node:
#             child_bodypart_node = BodypartTreeNode(children_node_name, children_body_part_uid, connection_type)
#             parent_node.add_child_node(child_bodypart_node)
#             self.nodes.append(child_bodypart_node)
#         else:
#             logger_.error("Tried to bind bodypart {} to non existing parent {}.".format(
#                 children_node_name, parent_node_name))
# 
# Path: components/body.py
# class Body(Component):
#     NAME = "body"
#     """
#     Height is in feet, Weight in pounds
#     """
#     def __init__(self, uid, bodypart_tree, name="", height=0, weight=0, outer_material_uid=None,
#                  inner_material_uid=None, structural_material_uid=None, blood_uid=None):
#         super().__init__()
#         self.uid = uid
#         self.bodypart_tree = bodypart_tree
#         self.name = name
#         self.height = height
#         self.weight = weight
#         self.outer_material_uid = outer_material_uid
#         self.inner_material_uid = inner_material_uid
#         self.structural_material_uid = structural_material_uid
#         self.blood_uid = blood_uid
# 
#     def copy(self):
#         return Body(
#             uid=self.uid,
#             name=self.name,
#             bodypart_tree=self.bodypart_tree.copy(),
#             height=self.height,
#             weight=self.weight,
#             outer_material_uid=self.outer_material_uid,
#             inner_material_uid=self.inner_material_uid,
#             structural_material_uid=self.structural_material_uid,
#             blood_uid=self.blood_uid,
#         )
# 
#     def __str__(self):
#         return "Body({})".format(self.name)
# 
#     def get_body_part(self, uid):
#         for node in self.bodypart_tree.nodes:
#             if node.instance.uid == uid:
#                 return node.instance
# 
#     def get_body_parts(self, uid):
#         body_parts = []
#         for node in self.bodypart_tree.nodes:
#             if not node.instance:
#                 print("WTF")
#             else:
#                 if node.instance.uid == uid:
#                     body_parts.append(node.instance)
# 
#         return body_parts
# 
#     @staticmethod
#     def _random_roll_body_part(body_parts):
#         tries = 0
#         max_tries = 3
#         while tries < max_tries:
#             tries += 1
#             for node in body_parts:
#                 if random.randrange(0, 100) <= node.instance.relative_size:
#                     return node
# 
#         return random.choice(body_parts)
# 
#     def get_random_body_part_for_threat_level(self, threat_level):
#         size_sorted_body_parts = [node for node in self.bodypart_tree.nodes
#                                   if node.instance.threat_level == threat_level]
#         if not size_sorted_body_parts:
#             if threat_level < ThreatLevel.Fatal:
#                 return self.get_random_body_part_for_threat_level(ThreatLevel[threat_level.value + 1])
#             else:
#                 return self.get_random_body_part_by_relative_size()
# 
#         return self._random_roll_body_part(size_sorted_body_parts)
# 
#     def get_random_body_part_by_relative_size(self):
#         size_sorted_body_parts = sorted([node for node in self.bodypart_tree.nodes
#                                          if node.connection_type == BodypartTree.CONNECTION_TYPE_ATTACHED],
#                                         key=lambda node: node.instance.relative_size, reverse=True)
# 
#         return self._random_roll_body_part(size_sorted_body_parts)
# 
#     def get_grasp_able_body_parts(self):
#         return [node.instance for node in self.bodypart_tree.nodes
#                 if PhysicalAbilities.GRASP in node.instance.physical_abilities]
# 
#     def get_physical_abilities(self):
#         abilities = {}
#         for node in self.bodypart_tree.nodes:
#             for ability_name, ability_value in six.iteritems(node.instance.physical_abilities):
#                 if ability_name not in abilities:
#                     abilities[ability_name] = ability_value
#                 else:
#                     if abilities[ability_name] < ability_value:
#                         abilities[ability_name] = ability_value
# 
#         return abilities


# Path: data/python_templates/body.py
from bodies.body_part_tree import BodypartTree
from components.body import Body



def build_humanoid_body():
    humanoid_tree = BodypartTree('torso', 'humanoid_torso')
    humanoid_tree.insert('torso', 'heart', 'humanoid_heart')
    humanoid_tree.insert('torso', 'lungs', 'humanoid_lungs')
    humanoid_tree.attach('torso', 'head', 'humanoid_head')
    humanoid_tree.attach('head', 'left eye', 'humanoid_eye')
    humanoid_tree.attach('head', 'right eye', 'humanoid_eye')
    humanoid_tree.attach('head', 'left ear', 'humanoid_ear')
    humanoid_tree.attach('head', 'right ear', 'humanoid_ear')
    humanoid_tree.attach('head', 'mouth', 'humanoid_mouth')

    humanoid_tree.insert('head', 'brain', 'humanoid_brain')