from ..cAPI import SimulationAPI
from . import create_curve_parameterization_attribute
import numpy as np
import bpy

def report_error(m, operator):
    if operator:
        operator.report({"ERROR"}, m)
    else:
        bpy.ops.hi_phy.error_report('INVOKE_DEFAULT', message=m)

def evaluate_expressions(obj, operator = None):
    frame = bpy.context.scene.frame_current
    time = frame / bpy.context.scene.render.fps
    # check if we need to compute the parameterization for curves
    if (obj.type == 'CURVES'):
        if not "hi_phy_parameterization" in obj.data.attributes:
            if not (create_curve_parameterization_attribute.create(obj)):
                report_error("Failed to create parameterization attribute on curves " + obj.name, operator)
                return;
    succeed = True;
    for ea in obj.hi_phy.expression_attribute_list:
        expression_evaluator = SimulationAPI.PropertyExpression(ea.expression, SimulationAPI.ExpressionType.SeExpr)
        if (not expression_evaluator.IsValid()):
            report_error("\"" + ea.expression + "\" is not a valid expression, on object " + obj.name, operator)
            succeed = False
            continue;
        expression_evaluator.ClearArrays()
        # Set position and parameterization
        positions_attr = obj.data.attributes['position']
        n_verts = obj.data.attributes.domain_size('POINT')
        points = np.zeros((n_verts, 3), dtype=np.float32)
        positions_attr.data.foreach_get('vector', np.ravel(points))
        expression_evaluator.SetPositionArray(points)
        if (obj.type == 'CURVES'):
            parameterization = np.zeros(n_verts, dtype=np.float32)
            parameterization_attr = obj.data.attributes['hi_phy_parameterization']
            parameterization_attr.data.foreach_get('value', parameterization)
            expression_evaluator.SetParemeterizationArray(parameterization)
        # Try to find UV
        if ('hi_phy_u' in obj.data.attributes):
            u = np.zeros(n_verts, dtype=np.float32)
            u_attr = obj.data.attributes['hi_phy_u']
            u_attr.data.foreach_get('value', u)
            expression_evaluator.SetUArray(u)
        if ('hi_phy_v' in obj.data.attributes):
            v = np.zeros(n_verts, dtype=np.float32)
            v_attr = obj.data.attributes['hi_phy_v']
            v_attr.data.foreach_get('value', v)
            expression_evaluator.SetVArray(v)
        if ('hi_phy_v1' in obj.data.attributes):
            v1 = np.zeros(n_verts, dtype=np.float32)
            v1_attr = obj.data.attributes['hi_phy_v1']
            v1_attr.data.foreach_get('value', v1)
            expression_evaluator.SetV1Array(v1)
        if ('hi_phy_v2' in obj.data.attributes):
            v2 = np.zeros(n_verts, dtype=np.float32)
            v2_attr = obj.data.attributes['hi_phy_v2']
            v2_attr.data.foreach_get('value', v2)
            expression_evaluator.SetV2Array(v2)
        if ('hi_phy_v3' in obj.data.attributes):
            v3 = np.zeros(n_verts, dtype=np.float32)
            v3_attr = obj.data.attributes['hi_phy_v3']
            v3_attr.data.foreach_get('value', v3)
            expression_evaluator.SetV3Array(v3)
        if ('hi_phy_vv1' in obj.data.attributes):
            vv1 = np.zeros((n_verts, 3), dtype=np.float32)
            vv1_attr = obj.data.attributes['hi_phy_vv1']
            vv1_attr.data.foreach_get('value', np.ravel(vv1))
            expression_evaluator.SetVV1Array(vv1)
        if ('hi_phy_vv2' in obj.data.attributes):
            vv2 = np.zeros((n_verts, 3), dtype=np.float32)
            vv2_attr = obj.data.attributes['hi_phy_vv2']
            vv2_attr.data.foreach_get('value', np.ravel(vv2))
            expression_evaluator.SetVV2Array(vv2)
        if ('hi_phy_vv3' in obj.data.attributes):
            vv3 = np.zeros((n_verts, 3), dtype=np.float32)
            vv3_attr = obj.data.attributes['hi_phy_vv3']
            vv3_attr.data.foreach_get('value', np.ravel(vv3))
            expression_evaluator.SetVV3Array(vv3)
        # Check expression return type and dimension to set attribute types
        if (expression_evaluator.ReturnType() == SimulationAPI.ExpressionReturnType.Float):
            if (expression_evaluator.ReturnDimension() == 1):
                data = np.zeros(n_verts, dtype=np.float32)
                expression_evaluator.EvaluateFloat(data, time, n_verts)
                if ea.attribute in obj.data.attributes:
                    attr = obj.data.attributes[ea.attribute]
                    if (attr.domain != 'POINT' or attr.data_type != 'FLOAT'):
                        report_error("\"" + ea.attribute + "\" already exists on " + obj.name + ". But it is not a FLOAT attribute of POINT domain", operator)
                        succeed = False
                        continue
                    else:
                        attr = obj.data.attributes.new(ea.attribute, 'FLOAT', 'POINT')
                    attr.data.foreach_set('value', data);
            elif (expression_evaluator.ReturnDimension() == 3):
                data = np.zeros((n_verts, 3), dtype=np.float32)
                expression_evaluator.EvaluateVector3(data, time, n_verts)
                if ea.attribute in obj.data.attributes:
                    attr = obj.data.attributes[ea.attribute]
                    if (attr.domain != 'POINT' or attr.data_type != 'FLOAT_VECTOR'):
                        report_error("\"" + ea.attribute + "\" already exists on " + obj.name + ". But it is not a FLOAT_VECTOR attribute of POINT domain", operator)
                        succeed = False
                        continue
                else:
                    attr = obj.data.attributes.new(ea.attribute, 'FLOAT_VECTOR', 'POINT')
                attr.data.foreach_set('vector', np.ravel(data));
            else:
                report_error("\"" + ea.expression + "\" is not a valid float or float vector expression, on object " + obj.name, operator)
                succeed = False
                continue
        elif (expression_evaluator.ReturnType() == SimulationAPI.ExpressionReturnType.Int):
            if (expression_evaluator.ReturnDimension() == 1):
                data = np.zeros(n_verts, dtype=np.int32)
                expression_evaluator.EvaluateInt(data, time, n_verts)
                if ea.attribute in obj.data.attributes:
                    attr = obj.data.attributes[ea.attribute]
                    if (attr.domain != 'POINT' or attr.data_type != 'INT'):
                        report_error("\"" + ea.attribute + "\" already exists on " + obj.name + ". But it is not a INT attribute of POINT domain", operator)
                        succeed = False
                        continue
                else:
                    attr = obj.data.attributes.new(ea.attribute, 'INT', 'POINT')
                attr.data.foreach_set('value', data);
            else:
                report_error("\"" + ea.expression + "\" is not a valid int expression, on object " + obj.name, operator)
                succeed = False
                continue
        else:
            report_error("\"" + ea.expression + "\" does not have a supported return type, on object " + obj.name, operator)
            succeed = False
            continue
    return succeed
