"""All Sfepy imports go in this module
"""
import numpy as np
from sfepy.base.goptions import goptions
from sfepy.discrete.fem import Field
from sfepy.discrete.fem import FEDomain as Domain
from sfepy.discrete import (
    FieldVariable,
    Material,
    Integral,
    Function,
    Equation,
    Equations,
    Problem,
)
from sfepy.terms import Term, Terms
from sfepy.discrete.conditions import Conditions, EssentialBC
from sfepy.solvers.ls import ScipyDirect
from sfepy.solvers.nls import Newton
from sfepy.discrete import Functions
from sfepy.mesh.mesh_generators import gen_block_mesh
from sfepy.base.base import output
from toolz.curried import pipe, curry, do


goptions["verbose"] = False
output.set_output(quiet=True)


def check(ids):
    """Check that the fixed displacement nodes have been isolated

    Args:
      ids: the isolated IDs

    Returns the unchanged IDs

    >>> check([1, 2, 3, 4])
    Traceback (most recent call last):
    ...
    RuntimeError: length of ids is incorrect
    """
    if len(ids) != 3:
        raise RuntimeError("length of ids is incorrect")
    return ids


@curry
def subdomain(i_x, domain_, eps, coords, **_):
    """Find the node IDs that will be fixed

    Args:
      i_x: the index (either 0 or 1) depends on direction of axes
      domain_: the Sfepy domain
      eps: a small value
      coords: the coordinates of the nodes

    Returns:
      the isolated node IDs
    """

    def i_y():
        """Switch the index from 0 -> 1 or from 1 -> 0
        """
        return (i_x + 1) % 2

    return pipe(
        (coords[:, i_x] > -eps) & (coords[:, i_x] < eps),
        lambda x: (coords[:, i_x] < domain_.get_mesh_bounding_box()[0][i_x] + eps) | x,
        lambda x: (coords[:, i_x] > domain_.get_mesh_bounding_box()[1][i_x] - eps) | x,
        lambda x: (coords[:, i_y()] < eps) & (coords[:, i_y()] > -eps) & x,
        lambda x: np.where(x)[0],
        check,
    )


def get_bc(domain, delta_x, index, cond):
    """Make a displacement boundary condition

    Args:
      domain: the Sfepy domain
      delta_x: the mesh spacing
      index: the index (either 0 or 1) depends on direction of axes
      cond: the BC dictionary

    Returns:
      the Sfepy boundary condition
    """
    return pipe(
        Function("fix_points", subdomain(index, domain, delta_x * 1e-3)),
        lambda x: domain.create_region(
            "region_fix_points",
            "vertices by fix_points",
            "vertex",
            functions=Functions([x]),
        ),
        lambda x: EssentialBC("fix_points_BC", x, cond),
    )


def get_bcs(domain, delta_x):
    """Get the boundary conditions

    Args:
      domain: the Sfepy domain
      delta_x: the mesh spacing

    Returns:
      the boundary conditions
    """
    return Conditions(
        [
            get_bc(domain, delta_x, 1, {"u.0": 0.0}),
            get_bc(domain, delta_x, 0, {"u.1": 0.0}),
        ]
    )


def get_material(calc_stiffness, calc_prestress):
    """Get the material

    Args:
      calc_stiffness: the function for calculating the stiffness tensor
      calc_prestress: the function for calculating the prestress

    Returns:
      the material
    """

    def _material_func_(_, coors, mode=None, **__):
        if mode == "qp":
            return dict(D=calc_stiffness(coors), stress=calc_prestress(coors))
        return None

    return Material("m", function=Function("material_func", _material_func_))


def get_uv(shape, delta_x):
    """Get the fields for the displacement and test function

    Args:
      shape: the shape of the domain
      delta_x: the mesh spacing

    Returns:
      tuple of field variables
    """
    return pipe(
        np.array(shape),
        lambda x: gen_block_mesh(
            x * delta_x, x + 1, np.zeros_like(shape), verbose=False
        ),
        lambda x: Domain("domain", x),
        lambda x: x.create_region("region_all", "all"),
        lambda x: Field.from_args("fu", np.float64, "vector", x, approx_order=2),
        lambda x: (
            
FieldVariable("u", "unknown", x)