"""Torque-driven rotating flexible crank verification journal."""

import sys

import numpy as np


sys.path.insert(0, "./server/")

from mesh_generators.structured import build_structured_mesh
from pytorsocae import TorsoCAESession


LENGTH = 0.5
WIDTH = 0.08
THICKNESS = 0.04
YOUNG_MODULUS = 210.0e9
POISSON_RATIO = 0.3
DENSITY = 7800.0

TARGET_REVOLUTION_TIME = 1.0
ANGULAR_ACCELERATION = 4.0 * np.pi / TARGET_REVOLUTION_TIME**2
MASS = DENSITY * LENGTH * WIDTH * THICKNESS
PIVOT_INERTIA = MASS * (LENGTH**2 / 3.0 + WIDTH**2 / 12.0)
APPLIED_TORQUE = PIVOT_INERTIA * ANGULAR_ACCELERATION
TIP_FORCE = APPLIED_TORQUE / LENGTH
TIP_TRACTION = TIP_FORCE / (WIDTH * THICKNESS)

TIME_STEP = 0.005
NUM_STEPS = int(round(TARGET_REVOLUTION_TIME / TIME_STEP))


def build_session() -> TorsoCAESession:
    """Build the crank model without executing it."""
    mesh = build_structured_mesh(
        bounds=(
            (0.0, LENGTH),
            (-WIDTH / 2.0, WIDTH / 2.0),
            (-THICKNESS / 2.0, THICKNESS / 2.0),
        ),
        divisions=(12, 2, 1),
        surface_tags={
            "xmin": 1,
            "xmax": 2,
            "ymin": 3,
            "ymax": 4,
            "zmin": 5,
            "zmax": 6,
        },
        volume_tag=1,
    )

    session = TorsoCAESession()
    session.inline_mesh(
        mesh,
        mesh_id="rotating_crank_mesh",
        name="Torque-Driven Flexible Crank",
        volume_names={1: "Flexible Crank"},
    )
    session.solid("Flexible Crank").material(
        E=YOUNG_MODULUS,
        nu=POISSON_RATIO,
        rho=DENSITY,
    )
    session.set_flexible_multibody(
        frames=[
            {
                "id": "ground",
                "origin": [0.0, 0.0, 0.0],
                "grounded": True,
            },
            {
                "id": "crank_root",
                "origin": [0.0, 0.0, 0.0],
            },
        ],
        bodies=[
            {
                "id": "crank_body",
                "kind": "flexible",
                "reference_frame_id": "crank_root",
            },
        ],
        attachments=[
            {
                "frame_id": "crank_root",
                "surface_tag": 1,
                "body_id": "crank_body",
                "dependent_axes": ["x", "y", "z"],
            },
        ],
        joints=[
            {
                "id": "crank_bearing",
                "frame_a": "ground",
                "frame_b": "crank_root",
                "kind": "revolute",
                "axis": "z",
            },
        ],
        kkt_augmentation=0.0,
        projection={
            "tolerance": 1.0e-9,
            "max_iterations": 15,
        },
    )
    session.surface(2).bc(
        "traction",
        values=[0.0, TIP_TRACTION, 0.0],
        follower=True,
    )
    session.set_physics(
        "structural",
        submodel="flexible_multibody_dynamics",
        backend="dolfinx",
    )
    session.set_solver_options(
        algo="auto",
        tol=1.0e-8,
        max_iter=800,
        num_steps=NUM_STEPS,
        dt=TIME_STEP,
        time_scheme="newmark",
        newmark_beta=0.25,
        newmark_gamma=0.5,
        max_inner_iter=100,
        inner_tol=1.0e-7,
        live_viz_interval=1,
        n_cores=1,
        device="cpu",
    )
    return session


def main() -> None:
    session = build_session()
    session.compute(mesh_ids=["rotating_crank_mesh"])


if __name__ == "__main__":
    main()
