""" Module contains helper functions to build microtubules, centrosomes
and interMT connectors"""
from abaqus import *
from abaqusConstants import *
import section
import regionToolset
import part
import material
import sketch


def create_MT_part( l, type, i, **kwargs ):
    """
    Create an Abaqus object representing a single microtubule part

    :param l: Length of the microtuble

    :type l: float

    :param type: Type of the MT: aMT or ipMT

    :type type: str

    :param i: sequential number of the MT

    :type i: str

    :param kwargs: model parameters

    :type kwargs: dict

    :return: part object, MTname

    :rtype: object, str
    """
    modelname = kwargs['modelname']
    s = mdb.models[modelname].ConstrainedSketch(
        name='__profile__',
        sheetSize=1.0)
    g, v, d, c = s.geometry, s.vertices, s.dimensions, s.constraints
    s.sketchOptions.setValues(decimalPlaces=3)
    s.setPrimaryObject(option=STANDALONE)
    # create line
    s.Line(point1=(0.0, 0.0), point2=(l, 0.0))
    # create a name for MT
    MTname = create_mt_name(l, type, i)
    # Create part
    p = mdb.models[modelname].Part(
        name=MTname,
        dimensionality=THREE_D,
        type=DEFORMABLE_BODY)
    p = mdb.models[modelname].parts[MTname]
    p.BaseWire(sketch=s)
    s.unsetPrimaryObject()
    del mdb.models[modelname].sketches['__profile__']
    return p, MTname


def create_connector_part( connectorname, length, **kwargs ):
    """
    Create an Abaqus object that represents a single connector part

    :param connectorname: Name of the connector

    :type connectorname: str

    :param length: Length of the connector

    :type length: float

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Part object

    :rtype: object
    """
    modelname = kwargs['modelname']
    s = mdb.models[modelname].ConstrainedSketch(
        name='__profile__',
        sheetSize=1)
    g, v, d, c = s.geometry, s.vertices, s.dimensions, s.constraints
    s.sketchOptions.setValues(decimalPlaces=4)
    s.setPrimaryObject(option=STANDALONE)
    # create line
    s.Line(point1=(0.0, 0.0), point2=(0.0, length))
    p = mdb.models[modelname].Part(
        name=connectorname,
        dimensionality=THREE_D,
        type=DEFORMABLE_BODY)
    p = mdb.models[modelname].parts[connectorname]
    p.BaseWire(sketch=s)
    s.unsetPrimaryObject()
    del mdb.models[modelname].sketches['__profile__']
    return p


def create_centrosome_sketch( l, r, **kwargs ):
    """
    Create a 2D sketch of the centrosome middle cross-section

    :param l: Length of the centrosome, e.g., dimension along z axis

    :type l: float

    :param r: Radius of the centrosome, e.g., radius of the centrosome cross-section
            in x-y plane

    :type r: float

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Sketch object

    :rtype: object
    """
    modelname = kwargs['modelname']
    s = mdb.models[modelname].ConstrainedSketch(
        name='__profile__', sheetSize=2.0)
    g, v, d, c = s.geometry, s.vertices, s.dimensions, s.constraints
    # Create cross-sectoion
    s.setPrimaryObject(option=STANDALONE)
    # Define an axis of revolution
    s.ConstructionLine(point1=(0.0, -1.0), point2=(0.0, 1.0))
    # Create a revolution sketch
    s.FixedConstraint(entity=g[2])
    s.Arc3Points(point1=(0.0, r), point2=(0.0, -r), point3=(l / 2, 0.0))
    s.Line(point1=(0.0, -r), point2=(0.0, r))
    s.VerticalConstraint(entity=g[4], addUndoState=False)
    return s


def create_mt_name( l, type, i ):
    """
    Specify a unique name to each created microtubule

    :param l: Length of the MT

    :type l: float

    :param type: Type of the MT: aMT or ipMT

    :type type: str

    :param i: sequential number of the MT

    :type i: int

    :return: MTname

    :rtype: str
    """
    if type == 'ipMT':
        MTname = 'ipMT_' + str(l * 100)[:3] + '_' + str(i)
        print(MTname)
    elif type == 'aMT':
        MTname = 'aMT_' + str(i)
    else:
        raise ValueError('Only interpolar MTs (ipMT) and astral MTs (aMT) are currently supported')

    return MTname


def create_section( sectionName, sectionProfile, sectionMaterial, **kwargs ):
    """
    Create a beam section for the microtubule

    :param sectionName: Name of the section. 'MT-section'

    :type sectionName: str

    :param sectionProfile: Type of the section profile. 'MT-profile'

    :type sectionProfile: str

    :param sectionMaterial: Name of the material assigned to the section. 'MT_material'

    :type sectionMaterial: str

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Null

    :rtype: Null
    """
    modelname = kwargs['modelname']
    mdb.models[modelname].BeamSection(
        name=sectionName,
        integration=DURING_ANALYSIS,
        poissonRatio=0.0,
        profile=sectionProfile,
        material=sectionMaterial,
        temperatureVar=LINEAR,
        consistentMassMatrix=False)


def define_material( name, E, nu, **kwargs ):
    """
    Define material parameters of microtubules, connectors and centrosomes specifying
    its module of elasticity and Poisson's ratio

    :param name: Name of the material

    :type name: str

    :param E: Module of elasticity

    :type E: float

    :param nu: Poisson's ratio

    :type nu: float

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Null

    :rtype: Null
    """
    modelname = kwargs['modelname']
    mdb.models[modelname].Material(name=name)
    mdb.models[modelname].materials[name].Elastic(table=((E, nu),))


def assign_MT_section( part, MTname, **kwargs ):
    """
    Assign a section to microtubules

    :param part: Microtubule part to which section to be assigned

    :type part: object

    :param MTname: Name of the microtubule

    :type MTname: str

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Null

    :rtype: Null
    """
    modelname = kwargs['modelname']
    e = part.edges
    edges = e.getByBoundingSphere((0.0, 0.0, 0.0), (kwargs['spindleLength']), )
    region = part.Set(edges=edges, name='MT-set')
    p = mdb.models[modelname].parts[MTname]
    p.SectionAssignment(
        region=region, sectionName='MT-section', offset=0.0,
        offsetType=MIDDLE_SURFACE, offsetField='',
        thicknessAssignment=FROM_SECTION)


def assign_MT_section_orientation( MTname, **kwargs ):
    """
    Assign MT section orientation with respect to the local coordinates of the MT

    :param MTname: Name of the microtubule

    :type MTname: str

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Null

    :rtype: Null
    """
    modelname = kwargs['modelname']
    p = mdb.models[modelname].parts[MTname]
    e = p.edges
    edges = e.getSequenceFromMask(mask=('[#1 ]',), )
    region = p.Set(edges=edges, name='MT-set')
    p = mdb.models[modelname].parts[MTname]
    p.assignBeamSectionOrientation(
        region=region,
        method=N1_COSINES,
        n1=(0.0, 0.0, -1.0))


def assign_connector_section( part, name, **kwargs ):
    """
    Assign a section to each connector

    :param part: Connector part to which section to be assigned

    :type part: object

    :param name: Name of the connector

    :type name: str

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Region containing connector

    :rtype: object
    """
    e = part.edges
    edges = e.getByBoundingSphere((0.0, 0.0, 0.0), (kwargs['spindleLength']), )
    region = part.Set(edges=edges, name='connector-set')
    part.SectionAssignment(
        region=region, sectionName=name, offset=0.0,
        offsetType=MIDDLE_SURFACE, offsetField='',
        thicknessAssignment=FROM_SECTION)
    return region


def create_centrosome_part( Centrosomesketch, name, **kwargs ):
    """
    Create an Abaqus object containing centrosome geometry part

    :param Centrosomesketch: Name of the centrosome sketch object

    :type Centrosomesketch: str

    :param name: Name of the centrosome part

    :type name: object

    :param kwargs: model parameters

    :type kwargs: str

    :return: Centrosome part

    :rtype: object
    """
    modelname = kwargs['modelname']
    p = mdb.models[modelname].Part(
        name=name,
        dimensionality=THREE_D,
        type=DEFORMABLE_BODY)
    p = mdb.models[modelname].parts[name]
    p.BaseSolidRevolve(sketch=Centrosomesketch, angle=360.0, flipRevolveDirection=OFF)
    Centrosomesketch.unsetPrimaryObject()
    p = mdb.models[modelname].parts[name]
    del mdb.models[modelname].sketches['__profile__']
    return p


def assign_centrosome_section( part, name, sectionName, **kwargs ):
    """
    Assign section to the Centrosome part

    :param part: Centrosome part to which the section is to be assigned

    :type part: object

    :param name: Name of the centrosome

    :type name: str

    :param sectionName: Name of the section to be assigned

    :type sectionName: str

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Null

    :rtype: Null
    """
    modelname = kwargs['modelname']
    c = part.cells
    cells = c.getByBoundingSphere((0.0, 0.0, 0.0), (kwargs['spindleLength']), )
    region = part.Set(cells=cells, name='centrosome-set')
    p = mdb.models[modelname].parts[name]
    p.SectionAssignment(
        region=region,
        sectionName=sectionName, offset=0.0,
        offsetType=MIDDLE_SURFACE, offsetField='',
        thicknessAssignment=FROM_SECTION)


def model( **kwargs ):
    """
    Create Abaqus model for the mitotic spindle

    :param kwargs: model parameters

    :type kwargs: dict

    :return: Null

    :rtype: Null
    """
    modelname = kwargs['modelname']
    # Create and save model database
    mdb.Model(name=modelname, modelType=STANDARD_EXPLICIT)
