"""Möbius pencil of circles example for use in a tutorial in the docs.

Portions of this file are included in the docs. Comments of the form "# [...]" Mark
sections that should become code blocks in the docs. Keep this in mind when editing this
file!

These markers are not necessary when including a whole function/class/method etc.
"""

# [imports]
import numpy as np

import ddg
from ddg.visualization.blender.collection import collection, clear

# [imports]

#
# BLENDER COLLECTIONS
# -------------------


# [clear]
clear(remove_collections=True)
# [clear]


# [collection setup]
mob_geo_coll = collection(
    "Mobius geometry",
    children=[
        ["lines", "points_on_line", "points_on_polar_line"],
        "spherical circles",
        "polar spherical circles",
    ],
)
euc_geo_coll = collection(
    "Euclidean geometry", children=["Euclidean circles", "orthogonal Euclidean circles"]
)
projection_coll = collection("projection_with_light")
# [collection setup]

# ------------------------------------------------------------

#
# SPHERE S2
# ---------

# [create sphere as quadric]
s2 = ddg.geometry.quadrics.Quadric(np.diag([1, 1, 1, -1]))
# [create sphere as quadric]

# [display sphere]
bobj_s2 = ddg.to_blender_object_helper(
    s2, sampling=[0.1, 40, "c"], name="s2", material="s2", collection=mob_geo_coll
)
# [display sphere]


# ------------------------------------------------------------


#
# LINE L THROUGH S2
# -----------------

# [create line as subspace]
# p1, p2 = np.array([-4, .5, -0.5, 1]), np.array([4,.5, -0.5, 1])
p1, p2 = np.array([-1, 1, 0, 1]), np.array([1, 1, 0, 1])
l = ddg.geometry.subspaces.Subspace(p1, p2)
# [create line as subspace]

# [line to smooth net]
# orthonormalize and center l
l = l.orthonormalize_and_center((p1 + p2) / 2)

# create smooth net
l_net = ddg.to_smooth_net(l, domain=[[-4, 4]], affine=True, convex=True)
# [line to smooth net]

# [line to blender object]
# to blender
bobj_l = ddg.to_blender_object_helper(
    l,
    domain=[[-4, 4]],
    sampling=[0.1, 40, "c"],
    name="line",
    collection=mob_geo_coll.children[0],
    material="line",
    curve_properties={"bevel_depth": 0.01},
)
# [line to blender object]


# -------------------------------------------------------------


#
# SPHERICAL AND PLANAR CIRCLES
# ----------------------------


#
# Spherical Circles
#

# [get a point]
# get a point
pt = l_net(2)
# [get a point]

# [homogenize point]
# homogenize pt
pt_homogenized = ddg.math.projective.homogenize(pt)
# [homogenize point]

# [convert point to subspace]
# convert point to subspace
pt_projective = ddg.geometry.subspaces.Subspace(pt_homogenized)
# [convert point to subspace]

# [display point]
# display point in blender
bobj_pt = ddg.to_blender_object_helper(
    pt_projective,
    name="point",
    sphere_radius=0.05,
    material="point",
    collection=mob_geo_coll.children[0].children[0],
)
# [display point]


# [calculate polar plane]
# calculate its polar plane
plane = s2.polarize(pt_projective)
# [calculate polar plane]

# [calculate intersection circle]
# calculate intersection circle
circ_on_sphere = ddg.geometry.intersection.meet(plane, s2)
# [calculate intersection circle]

# [check for empty intersection]
# check if intersection is not empty
if circ_on_sphere.dimension != -1:
    bobj_circ_on_sphere = ddg.to_blender_object_helper(
        circ_on_sphere,
        name="spherical circle",
        sampling=[0.1, 100, "c"],
        collection=mob_geo_coll.children[1],
        material="spherical circle",
        curve_properties={"bevel_depth": 0.005},
    )
# [check for empty intersection]


#
# Planar Circles
#

# [stereographic project and display circle]
# stereographic project
euc_plane = ddg.geometry.subspaces.subspace_from_affine_points_and_directions(
    [[0, 0, 0]], [[1, 0, 0], [0, 1, 0]]
)
circ_on_plane = ddg.geometry.projections.stereographic_project(
    circ_on_sphere, euc_plane
)

# convert to blender
bobj_circ_on_plane = ddg.to_blender_object_helper(
    circ_on_plane,
    name="planar circle",
    sampling=[0.1, 100, "c"],
    collection=euc_geo_coll.children[0],
    material="planar circle",
    curve_properties={"bevel_depth": 0.005},
)

bobj_euc_plane = ddg.to_blender_object_helper(
    euc_plane,
    name="euc_plane",
    domain=[[-30, 30], [-30, 30]],
    sampling=[9, "t"],
    material="euc plane",
    collection=euc_geo_coll,
)
# [stereographic project and display circle]


# [stereographic project with a light]
# stereographic project
light = ddg.visualization.blender.light.light(
    type_="POINT", location=[0, 0, 1], energy=600, collection=projection_coll
)
light.data.shadow_soft_size = 0

bobj_euc_plane.is_shadow_catcher = True
# [stereographic project with a light]

# -------------------------------------------------------------

#
# Möbius Pencil of Circles
# ------------------------


#
# Spherical and Planar Circles Function
#


def circles_from_point(
    pt,
    pt_coll=None,
    spherical_coll=None,
    planar_coll=None,
    material_spherical=None,
    material_planar=None,
    link=True,
):
    """
    Create the point, the corresp. spherical circle,
    and the corresp. planar circle.

    Parameters
    ----------
    pt : array of length 3

    pt_coll,
    spherical_coll,
    planar_ coll : Blender Collections (default=None)

    material_spherical: string or bpy.tupes.Material
    material_planar: string or bpy.types.Material

    link : bool (default=True)

    Returns
    -------
    None or list
        depending on link
    """

    # display point in blender
    bobj_pt = ddg.to_blender_object_helper(
        ddg.datastructures.nets.PointNet(pt),
        name="point",
        sphere_radius=0.05,
        collection=pt_coll,
        material="point",
        link=link,
    )

    # homogenize pt
    pt_homogenized = ddg.math.projective.homogenize(pt)

    # convert point to subspace
    pt_homogenized = ddg.geometry.subspaces.Subspace(pt_homogenized)

    # calculate its polar plane
    plane = s2.polarize(pt_homogenized)

    # calculate intersection circle
    circ_on_sphere = ddg.geometry.intersection.meet(plane, s2)

    # check if intersection is not empty
    if circ_on_sphere.dimension != -1:

        # convert spherical circle to blender
        bobj_circ_on_sphere = ddg.to_blender_object_helper(
            circ_on_sphere,
            name="spherical circle",
            sampling=[0.1, 100, "c"],
            curve_properties={"bevel_depth": 0.006},
            collection=spherical_coll,
            material=material_spherical,
            link=link,
        )

        # stereographic project
        circ_on_plane = ddg.geometry.projections.stereographic_project_quadric(
            circ_on_sphere, euc_plane
        )

        # convert planar circle to blender
        bobj_circ_on_plane = ddg.to_blender_object_helper(
            circ_on_plane,
            name="planar circle",
            sampling=[0.1, 100, "c"],
            curve_properties={"bevel_depth": 0.0060},
            collection=planar_coll,
            material=material_planar,
            link=link,
        )

        return [bobj_pt, bobj_circ_on_sphere, bobj_circ_on_plane]

    else:
        return [bobj_pt]


#
# Wrapper Function for Points on a Line
#


def circles_from_line_and_value(
    line,
    t,
    pt_coll=None,
    spherical_coll=None,
    planar_coll=None,
    material_spherical=None,
    material_planar=None,
    link=True,
):
    """
    Create the point line(t), the corresp. spherical circle,
    and the corresp. planar circle.

    Parameters
    ----------
    line : SmoothNet

    t : float
        point in domain of line

    pt_coll,
    spheric_coll,
    planar_ coll : Blender Collections (default=None)

    link : bool (default=True)

    Returns
    -------
    None or list
        depending on link
    """
    return circles_from_point(
        line(t),
        pt_coll=pt_coll,
        spherical_coll=spherical_coll,
        planar_coll=planar_coll,
        material_spherical=material_spherical,
        material_planar=material_planar,
        link=link,
    )


# [get circles for a point]
circles_from_line_and_value(
    l_net,
    1.3,
    pt_coll=mob_geo_coll.children[0].children[0],
    spherical_coll=mob_geo_coll.children[1],
    planar_coll=euc_geo_coll.children[0],
    material_spherical="spherical cirlce",
    material_planar="planar circle",
)
# [get circles for a point]


# Adding Slider


# [add slider]


def slider_fct_for_l(t):
    return circles_from_line_and_value(l_net, t, link=False)


callback = ddg.visualization.blender.props.clear_callback(
    "slider circles", slider_fct_for_l
)

ddg.visualization.blender.props.add_props_with_callback(callback, ("t",), 1.0)
# [add slider]

#
# Visualize some Circles in Pencil
#


# [sample domain of line]
# LINEAR SAMPLING OF DOMAIN
# this function takes arg3 samples equidistantly of the intervall [arg1,ag2]

sample = np.linspace(-3, 3, 30)
# [sample domain of line]


# [visualize pencil]
# visualize pencil
for t in sample:
    circles_from_line_and_value(
        l_net,
        t,
        pt_coll=mob_geo_coll.children[0].children[0],
        spherical_coll=mob_geo_coll.children[1],
        planar_coll=euc_geo_coll.children[0],
        material_spherical="spherical cirlce",
        material_planar="planar circle",
    )
# [visualize pencil]


#
# POLAR LINE TO L
# ----------------

# [orthogonal pencil]
# get polar subspace of l
polar_l = s2.polarize(l)

# orthonormalize
polar_l = polar_l.orthonormalize()

# convert to net
polar_l_net = ddg.to_smooth_net(polar_l, domain=[[-4, 4]], affine=True, convex=True)

# convert to blender
bobj_polar = ddg.to_blender_object_helper(
    polar_l,
    sampling=[0.1, 40, "c"],
    name="polar line",
    curve_properties={"bevel_depth": 0.01},
    collection=mob_geo_coll.children[0],
)

# visualize orthogonal pencil
for t in sample:
    circles_from_line_and_value(
        polar_l_net,
        t,
        pt_coll=mob_geo_coll.children[0].children[1],
        spherical_coll=mob_geo_coll.children[2],
        planar_coll=euc_geo_coll.children[1],
        material_spherical="polar spherical cirlce",
        material_planar="polar planar circle",
    )
# [orthogonal pencil]
