"""Example: Pascal lines and Brianchon lines.

Create and display Pascal lines,
their intersections, and Brianchon lines
and diagonals on a quadric surface.
"""

#############################################
# Setup
#############################################

# [setup-1]
import bpy
import numpy as np

import ddg
import ddg.datastructures.nets.utils
import ddg.geometry.subspaces as subspaces
import ddg.visualization.blender.curve as cutils
from ddg.geometry.intersection import join, meet
from ddg.visualization.blender import curve, mesh

# [setup-1]

# [setup-2]
# Clear existing mesh and curve objects in Blender
mesh.clear()
curve.clear()
# [setup-2]


#############################################
# Functions
#############################################

# [functions-1]
# Create a small square to be used as a profile curve for lines
profile_curve = cutils.create_curve(
    0.01 * np.array([[0.5, 0, 0], [0.5, 1, 0], [-0.5, 1, 0], [-0.5, 0, 0]])
)
cutils.add_curve(
    profile_curve, name="ProfileCurve", collection=bpy.data.collections.get("aux")
)
# [functions-1]


# [functions-2]
# Helper function to draw projective lines
def draw_line(
    line,
    material=None,
    sampling=1,
    collection=None,
    attributes={},
):
    line_net = ddg.to_smooth_net(line, convex=True, affine=True)
    line_net = ddg.sample_smooth_net(line_net, sampling)
    line_net = ddg.datastructures.nets.utils.embed(line_net)
    return ddg.to_blender_object(
        line_net,
        material=material,
        collection=bpy.data.collections.get(collection),
        attributes=attributes,
    )


# [functions-2]


# [functions-3]
# Helper function to draw points in projective space
def draw_point(
    point,
    material=None,
    collection=None,
    sphere_radius=0.03,
):
    point_net = ddg.to_smooth_net(point, convex=True, affine=True)
    point_net = ddg.datastructures.nets.utils.embed(point_net)
    return ddg.to_blender_object(
        point_net,
        material=material,
        collection=bpy.data.collections.get(collection),
        sphere_radius=sphere_radius,
    )


# [functions-3]


#############################################
# Math
#############################################

# [math-1]
# Create a quadric surface with specific coefficients
quadric = ddg.geometry.quadrics.Quadric(np.diag([1, 3, -1]))
quadric_net = ddg.to_smooth_net(quadric, affine=True)

# Generate parameters for points on the quadric surface
# (order matters)
pt_parameters = [(k + 0.2) * 2 * np.pi for k in [0, 0.125, 0.25, 0.375, 0.5, 0.75]]
pt_coords = [quadric_net(t) for t in pt_parameters]
points = [subspaces.subspace_from_affine_points(co) for co in pt_coords]
# [math-1]

# [math-2]
# Create Pascal lines by joining pairs of points
pascal_lines = [join(points[i], points[(i + 1) % 6]) for i in range(6)]

# Find intersections of Pascal lines
pascal_intersections = [meet(pascal_lines[i], pascal_lines[i + 3]) for i in range(3)]

# Create a Pascal line that goes through two of the intersections
pascal_line = join(pascal_intersections[0], pascal_intersections[1])

# Create Brianchon lines by dualizing the points
brianchon_lines = [p.dualize() for p in points]

# Dualize Pascal lines to create Brianchon intersections
brianchon_intersections = [l.dualize() for l in pascal_lines]

# Dualize Pascal intersection points to create Brianchon diagonals
brianchon_diagonals = [p.dualize() for p in pascal_intersections]

# Dualize Pascal line to create a Brianchon triple intersection point
brianchon_triple_intersection = pascal_line.dualize()
# [math-2]

#############################################
# Visualization
#############################################

# [visualization-1]
# Sample and visualize the quadric surface
quadric_dnet = ddg.sample_smooth_net(quadric_net, [100, "t"])
quadric_dnet = ddg.datastructures.nets.utils.embed(quadric_dnet)
ddg.to_blender_object(
    quadric_dnet,
    curve_properties={"bevel_depth": 0.005},
    material="black",
    collection=bpy.data.collections.get("Pascal"),
)
# [visualization-1]

# [visualization-2]
# Dualize the quadric surface and visualize it
dual_quadric_net = ddg.to_smooth_net(quadric.dualize(), affine=True)
dual_quadric_dnet = ddg.sample_smooth_net(dual_quadric_net, [100, "t"])
dual_quadric_dnet = ddg.datastructures.nets.utils.embed(dual_quadric_dnet)
ddg.to_blender_object(
    dual_quadric_dnet,
    curve_properties={"bevel_depth": 0.005},
    material="black",
    collection=bpy.data.collections.get("Brianchon"),
)
# [visualization-2]

# [visualization-3]
# Visualize Pascal lines, intersections, and points
for line in pascal_lines:
    draw_line(line, collection="Pascal", material="blue")
draw_line(
    pascal_line, material="yellow", collection="Pascal", attributes={"name": "THE line"}
)
for point in pascal_intersections:
    draw_point(point, collection="Pascal", material="red")
for point in points:
    draw_point(point, collection="Pascal", material="green")

# Visualize Brianchon lines, diagonals, intersections, and the triple intersection point
for line in brianchon_lines:
    draw_line(line, collection="Brianchon", material="green")
for line in brianchon_diagonals:
    draw_line(
        line, collection="Brianchon", material="red", attributes={"name": "diagonal"}
    )
for point in brianchon_intersections:
    draw_point(point, collection="Brianchon", material="blue")
draw_point(brianchon_triple_intersection, collection="Brianchon", material="yellow")
# [visualization-3]

from testing.tests.examples.blender.snapshot import opt_in  # noqa: E402

opt_in()
