""""
Functions to define the reaction sites
""""

import numpy as np
from dolfin import *
from cutfem import *

parameters["form_compiler"]["no-evaluate_basis_derivatives"] = False


def Gauss_expression_3d(x0, y0, z0, sigma = 0.01):
    gauss = Expression("1./ ((sqrt(pi*2)*sigma)*(sqrt(pi*2)*sigma) *(sqrt(pi*2)*sigma)) * exp(-((x[0]-x0) * (x[0]-x0) + (x[1]-y0) * (x[1]-y0) + (x[2]-z0) * (x[2]-z0)) / (2*sigma*sigma))", x0=x0, y0=y0, z0=z0, sigma=sigma, degree=2)
    return(gauss)


def eta_gauss(gaussian, mesh, mesh_cutter, dxc):

    """ Find the parameter eta"""

    V0 = FunctionSpace(mesh, "Lagrange", 1)

    # Fictitious domain
    composite_mesh = CompositeMesh()
    composite_mesh.add(mesh)

    V = CompositeFunctionSpace(composite_mesh)
    V.add(V0);
    V.build();

    # Constrain dofs outside
    FidoTools_compute_constrained_dofs(V, mesh_cutter)

    u_e_V0 = project(gaussian, V0)

    psi0 = u_e_V0 * dxc

    form_psi0 = create_dolfin_form(psi0)

    composite_form_psi0 = CompositeForm(V)
    composite_form_psi0.add(form_psi0)

    cut_cells = mesh_cutter.cut_cells(0)

    quadrature = Quadrature(cut_cells.type().cell_type(), cut_cells.geometry().dim(),order=2)

    composite_form_psi0.cut_form(0).set_quadrature(0, quadrature);
    composite_form_psi0.cut_form(0).set_cut_mesh(0, cut_cells);
    composite_form_psi0.cut_form(0).add_single_parent_mesh_id(0, 0);

    gauss_norm = composite_assemble(composite_form_psi0)

    return(gauss_norm)


def uniform_coordinates_enzymes(level_set, num_enz, x_size, y_size, z_size, x_min = 0.0, y_min =0.0, z_min = 0.0):
    xyz_min = [x_min, y_min, z_min]
    xyz_max = [x_size, y_size, z_size]
    coordinates_list = []
    while len(coordinates_list)< num_enz:
        candidate = np.random.uniform(low=xyz_min, high=xyz_max, size=(1, 3))
        p = Point(candidate[0][0], candidate[0][1], candidate[0][2])
        if level_set(p.x(), p.y(), p.z()) < 0.:
            coordinates_list.append(candidate)

    new_coord = np.array(coordinates_list)
    new_coord = new_coord.squeeze()
    return (new_coord.T)


def enzymes_close2mito(level_set, mito_points, sigma_x, sigma_y, sigma_z, x_size, y_size, z_size, num_sites):
    xyz_sigma = [sigma_x, sigma_y, sigma_z]
    coordinates_list = []

    for i in range(num_sites):
        xyz_loc  = [mito_points[0,i], mito_points[1,i], mito_points[2,i]]
        while len(coordinates_list) < (i+1):
            candidate = np.random.normal(xyz_loc, xyz_sigma, size=(1, 3))
            p = Point(candidate[0][0], candidate[0][1], candidate[0][2])
            if level_set(p.x(), p.y(), p.z()) < 0.:
                if candidate[0][0] < x_size and candidate[0][0] > 0.0 and candidate[0][1] < y_size and candidate[0][1] > 0.0 and candidate[0][2] < z_size and candidate[0][2] > 0.0:
                    coordinates_list.append(candidate)

    new_coord = np.asarray(coordinates_list)
    new_coord = new_coord.squeeze()
    return (new_coord.T)
