#!/usr/bin/env python3.10

"""Build and plot local and global cubic Lagrange basis functions."""

from pathlib import Path
import shutil

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np


def local_basis_0(sample_points):
    """Evaluate the first local basis function."""
    return -0.25 * (sample_points - 1.0) * (3.0 * sample_points**2 - 1.0)


def local_basis_1(sample_points):
    """Evaluate the second local basis function."""
    return 0.75 * (np.sqrt(3.0) * sample_points - 1.0) * (sample_points**2 - 1.0)


def local_basis_2(sample_points):
    """Evaluate the third local basis function."""
    return -0.75 * (np.sqrt(3.0) * sample_points + 1.0) * (sample_points**2 - 1.0)


def local_basis_3(sample_points):
    """Evaluate the fourth local basis function."""
    return 0.25 * (sample_points + 1.0) * (3.0 * sample_points**2 - 1.0)


def affine_map(local_points, left_endpoint, right_endpoint):
    """Map the reference interval `[-1, 1]` into `[left_endpoint, right_endpoint]`."""
    return 0.5 * ((1.0 - local_points) * left_endpoint + (1.0 + local_points) * right_endpoint)


def configure_plot_style():
    """Configure a consistent LaTeX-like plotting style."""
    mpl.rcParams.update(
        {
            "text.usetex": shutil.which("latex") is not None,
            "font.family": "serif",
            "font.serif": ["Computer Modern Roman", "DejaVu Serif"],
            "mathtext.fontset": "cm",
            "axes.titlesize": 15,
            "axes.labelsize": 13,
            "xtick.labelsize": 11,
            "ytick.labelsize": 11,
            "legend.fontsize": 11,
        }
    )


def save_plot(x_values, y_values, output_path, x_label="x", y_label="value"):
    """Save one curve with the common plotting style."""
    plt.figure(figsize=(6.0, 4.0))
    plt.plot(x_values, y_values, linewidth=2.0)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.grid(True, linewidth=0.4, alpha=0.5)
    plt.tight_layout()
    plt.savefig(output_path, bbox_inches="tight")
    plt.close()


def main():
    """Generate all basis-function plots referenced in the project text."""
    configure_plot_style()
    output_directory = Path(__file__).resolve().parent.parent / "Figuras"
    output_directory.mkdir(exist_ok=True)

    reference_points = np.linspace(-1.0, 1.0, 400)
    save_plot(reference_points, local_basis_0(reference_points), output_directory / "gamma0.pdf", "t")
    save_plot(reference_points, local_basis_1(reference_points), output_directory / "gamma1.pdf", "t")
    save_plot(reference_points, local_basis_2(reference_points), output_directory / "gamma2.pdf", "t")
    save_plot(reference_points, local_basis_3(reference_points), output_directory / "gamma3.pdf", "t")

    partition = np.array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
    local_grid = np.linspace(-1.0, 1.0, 200)

    gamma_a_x = affine_map(local_grid, partition[0], partition[1])
    save_plot(gamma_a_x, local_basis_0(local_grid), output_directory / "gamma_a.pdf")

    gamma_b_x = affine_map(local_grid, partition[-2], partition[-1])
    save_plot(gamma_b_x, local_basis_3(local_grid), output_directory / "gamma_b.pdf")

    gamma_xkm2_x = affine_map(local_grid, partition[1], partition[2])
    save_plot(gamma_xkm2_x, local_basis_1(local_grid), output_directory / "gamma_xkm2.pdf")

    gamma_xkm1_x = affine_map(local_grid, partition[1], partition[2])
    save_plot(gamma_xkm1_x, local_basis_2(local_grid), output_directory / "gamma_xkm1.pdf")

    left_piece_x = affine_map(local_grid, partition[1], partition[2])
    right_piece_x = affine_map(local_grid, partition[2], partition[3])
    plt.figure(figsize=(6.0, 4.0))
    plt.plot(left_piece_x, local_basis_3(local_grid), linewidth=2.0)
    plt.plot(right_piece_x, local_basis_0(local_grid), linewidth=2.0)
    plt.xlabel("x")
    plt.ylabel("value")
    plt.grid(True, linewidth=0.4, alpha=0.5)
    plt.tight_layout()
    plt.savefig(output_directory / "gamma_xk.pdf", bbox_inches="tight")
    plt.close()


if __name__ == "__main__":
    main()
