import math


def _get_rupture_dimensions(src, mag, nodal_plane):
    """
    Calculate and return the rupture length and width
    for given magnitude ``mag`` and nodal plane.

    :param src:
        a PointSource, AreaSource or MultiPointSource
    :param mag:
        a magnitude
    :param nodal_plane:
        Instance of :class:`openquake.hazardlib.geo.nodalplane.NodalPlane`.
    :returns:
        Tuple of two items: rupture length in width in km.

    The rupture area is calculated using method
    :meth:`~openquake.hazardlib.scalerel.base.BaseMSR.get_median_area`
    of source's
    magnitude-scaling relationship. In any case the returned
    dimensions multiplication is equal to that value. Than
    the area is decomposed to length and width with respect
    to source's rupture aspect ratio.

    If calculated rupture width being inclined by nodal plane's
    dip angle would not fit in between upper and lower seismogenic
    depth, the rupture width is shrunken to a maximum possible
    and rupture length is extended to preserve the same area.
    """
    area = src.magnitude_scaling_relationship.get_median_area(
        mag, nodal_plane.rake)
    rup_length = math.sqrt(area * src.rupture_aspect_ratio)
    rup_width = area / rup_length
    seismogenic_layer_width = (src.lower_seismogenic_depth
                               - src.upper_seismogenic_depth)
    max_width = (seismogenic_layer_width
                 / math.sin(math.radians(nodal_plane.dip)))
    if rup_width > max_width:
        rup_width = max_width
        rup_length = area / rup_width
    return rup_length, rup_width