import bpy
import bpy_extras.object_utils
import mathutils
import numpy as np
from ..cAPI import SimulationAPI

# we assume the verts are already part of the object
# and the object passed in has already been evaluated
def createPointConstraintObject(obj, verts, name, operator = None):
    if not obj.hi_phy:
        if operator:
            operator.report({"ERROR"}, obj.name + " is not a valid hi phy object")
        return False

    pointcloud = bpy.data.meshes.new(name + "Mesh")
    n_verts = obj.data.attributes.domain_size('POINT')
    positions_data = np.zeros((n_verts, 3), dtype=np.float32)
    obj.data.attributes['position'].data.foreach_get('vector', np.ravel(positions_data))
    matrix_world = obj.matrix_world;
    vs_world = [matrix_world @ mathutils.Vector((v[0], v[1], v[2])) for v in positions_data[verts]]

    n_points = len(verts)
    if (n_points == 0):
        if operator:
            operator.report({"ERROR"}, "Nothing was bond")
            return False

    pointcloud.from_pydata(vs_world, [], []) # Create a mesh with only verts

    obj_new = bpy.data.objects.new(name, pointcloud)
    obj_new.hi_phy.is_active = True
    obj_new.hi_phy.object_type = "CONSTRAINT"
    # Use the original object. We will evaluate it when we need it
    obj_new.hi_phy.constraint_object0 = obj.original
    obj_new.hi_phy.constraint_object1 = obj.original
    obj_new.hi_phy.constraint_type = SimulationAPI.ConstraintType.PointConstraint
    obj_new.hi_phy.solver = obj.hi_phy.solver

    bpy.context.collection.objects.link(obj_new)

    weight_data = [1, 0, 0] * n_points
    zero_data = [0] * n_points
    zero_data_3 = [0, 0, 0] * n_points
    # weight_data_flat = [v for w in weight_data for v in w]

    # Do not create multiple attributes at the same time
    # It will make the previous handle invalid
    index0_0_attr = pointcloud.attributes.new("hi_phy_constraint_point_indices0_0", 'INT', 'POINT')
    index0_0_attr.data.foreach_set('value', verts);

    index0_1_attr = pointcloud.attributes.new("hi_phy_constraint_point_indices0_1", 'INT', 'POINT')
    index0_1_attr.data.foreach_set('value', verts);

    index0_2_attr = pointcloud.attributes.new("hi_phy_constraint_point_indices0_2", 'INT', 'POINT')
    index0_2_attr.data.foreach_set('value', verts);

    index1_0_attr = pointcloud.attributes.new("hi_phy_constraint_point_indices1_0", 'INT', 'POINT')
    index1_0_attr.data.foreach_set('value', zero_data);

    index1_1_attr = pointcloud.attributes.new("hi_phy_constraint_point_indices1_1", 'INT', 'POINT')
    index1_1_attr.data.foreach_set('value', zero_data);

    index1_2_attr = pointcloud.attributes.new("hi_phy_constraint_point_indices1_2", 'INT', 'POINT')
    index1_2_attr.data.foreach_set('value', zero_data);

    weight0_attr = pointcloud.attributes.new("hi_phy_constraint_weight0", 'FLOAT_VECTOR', 'POINT')
    weight0_attr.data.foreach_set('vector', weight_data);

    weight1_attr = pointcloud.attributes.new("hi_phy_constraint_weight1", 'FLOAT_VECTOR', 'POINT')
    weight1_attr.data.foreach_set('vector', weight_data);

    rest_length_attr = pointcloud.attributes.new("hi_phy_constraint_rest_length", 'FLOAT', 'POINT')
    rest_length_attr.data.foreach_set('value', zero_data);

    positions_np = np.array(vs_world, dtype=np.float32)
    positions0_attr = pointcloud.attributes.new("hi_phy_constraint_point_positions_0", 'FLOAT_VECTOR', 'POINT')
    positions0_attr.data.foreach_set('vector', np.ravel(positions_np));

    positions1_attr = pointcloud.attributes.new("hi_phy_constraint_point_positions_1", 'FLOAT_VECTOR', 'POINT')
    positions1_attr.data.foreach_set('vector', np.ravel(positions_np));

    pointcloud.update()
    return True
