/*
 * Copyright (c) 2026 University of Insubria <https://www.uninsubria.it>
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version. All we ask is that proper credit is given
 * for our work, which includes - but is not limited to - adding the above
 * copyright notice to the beginning of your source code files, and to any
 * copyright notice that you may distribute with programs based on this work.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.
 * <https://fsf.org/>
 */

package qsardes.descriptor.qsarme;

import org.openscience.cdk.interfaces.IAtomContainer;
import qsardes.descriptor.DescriptorDetourMatrix;
import qsardes.util.UtilsCalc;

/**
 * Detour matrix descriptors (QSAR-ME Profiler 2025).
 * <BR><BR>
 * Calculates detour matrix descriptors.
 * <BR><BR>
 * Returns
 * <BR><BR>
 * SpMax_Dz - Leading eigenvalue.
 * <BR>
 * SpDiam_Dz - Spectral diameter.
 * <BR>
 * SpAD_Dz - Spectral absolute deviation.
 * <BR>
 * SpMAD_Dz - Spectral mean absolute deviation.
 * <BR>
 * EE_Dz - Estrada-like index.
 * <BR>
 * VE1_Dz - Coefficient sum of the last eigenvector.
 * <BR>
 * VE2_Dz - Average coefficient sum of the last eigenvector.
 * <BR>
 * VE3_Dz - Logarithmic coefficient sum of the last eigenvector.
 * <BR>
 * VR1_Dz - Randic-like eigenvector-based index.
 * <BR>
 * VR2_Dz - Normalized Randic-like eigenvector-based index
 * <BR>
 * VR3_Dz - Logarithmic Randic-like eigenvector-based index.
 *
 * @author Nicola Chirico
 */
public final class QsarMEDetourMatrix extends DescriptorDetourMatrix
{
    public QsarMEDetourMatrix()
    {
    }

    @Override
    protected double calculate_sp_ve1_sum(UtilsCalc utils_calc,
            double[][] eig_vec_array)
    {
        return utils_calc.calc_col_sum(eig_vec_array, 0);
    }

    @Override
    protected double calculate_sp_vr1_sum(IAtomContainer iac,
            UtilsCalc utils_calc, double[][] eig_vec_array)
    {
        double sum = 0.0;

        for (var bond : iac.bonds()) {
            for (int i = 0; i < bond.getAtomCount() - 1; i++) {
                double eigv_1
                        = eig_vec_array[iac.indexOf(bond.getAtom(i))][0];

                double eigv_2
                        = eig_vec_array[iac.indexOf(bond.getAtom(i + 1))][0];

                var eig_abs_prod = StrictMath.abs(eigv_1 * eigv_2);

                /*
                   Guard against very small eigenvalues as in PaDEL-Descriptor
                 */
                sum += eig_abs_prod < 1e-12
                        ? StrictMath.pow(1e-12, -0.5)
                        : StrictMath.pow(eig_abs_prod, -0.5);
            }
        }

        return sum;
    }

    @Override
    protected double get_sp_ve1_sum(double sp_ve1_sum)
    {
        /*
          Implemented as in PaDEL-Descriptor which gets the absolute
          values of the sum of the eigenvector elements instead of the sum
          of the absolute values.

          From: PaDEL-Descriptor version 2.21
          Author: Chun Wei Yap
          Class: BaryszMatrixDescriptor
          Method: calculate
          Availability: http://yapcwsoft.com/dd/padeldescriptor
        */
        return StrictMath.abs(sp_ve1_sum);
    }

    @Override
    protected double get_ve3(double sp_ve1_sum, int atom_count)
    {
        /*
          Implemented as in PaDEL-Descriptor: if the argument of
          logarithm is 0, the descriptor value is set to 0.

          From: PaDEL-Descriptor version 2.21
          Author: Chun Wei Yap
          Class: BaryszMatrixDescriptor
          Method: calculate
          Availability: http://yapcwsoft.com/dd/padeldescriptor
        */
        return sp_ve1_sum != 0.0
                ? ((double) atom_count / 10.0)
                        * StrictMath.log(StrictMath.abs(sp_ve1_sum)) : 0.0;
    }

    @Override
    protected double get_vr3(double vr1_sum, int atom_count)
    {
        /*
          Followed the get_ve3 scheme
        */
        return vr1_sum != 0.0 ? ((double) atom_count / 10.0)
                * StrictMath.log(vr1_sum) : 0.0;
    }
}
