/*
 * 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 qsardes.descriptor.DescriptorTopologicalCharge;
import qsardes.util.UtilsCalc;

/**
 * Topological charge descriptors (QSAR-ME Profiler 2025).
 * <BR><BR>
 * Calculates topological charge descriptors.
 * <BR><BR>
 * GGI# - Order # topological charge index.
 * <BR>
 * JGI# - Order # mean topological charge index.
 * <BR><BR>
 * # is the order spanning from 1 to 10.
 * <BR><BR>
 * JGT - Global topological charge index.
 *
 * @author Nicola Chirico
 */
public final class QsarMETopologicalCharge extends DescriptorTopologicalCharge
{
    public QsarMETopologicalCharge()
    {
    }

    @Override
    protected double[] get_topol_charge_index(double[][] charge_term_matr,
            int[][] topo_matr)
    {
        /*
           All orders starting from 1 are included in JGT computation as
           in PaDEL-Descriptor.

           From: PaDEL-Descriptor version 2.21
           Author: Chun Wei Yap
           Class: TopologicalChargeDescriptor
           Method: calculate
           Availability: http://yapcwsoft.com/dd/padeldescriptor
         */
        double[] index = new double[topo_matr.length > 11 ?
                topo_matr.length : 11];

        var ggi_map = get_ggi_map();

        for (int order = 0; order < index.length; order++) {
            double sum = 0.0;

            for (int i = 0; i < charge_term_matr.length; i++) {
                for (int j = 0; j < charge_term_matr[0].length; j++) {
                    sum += StrictMath.abs(charge_term_matr[i][j])
                            * (topo_matr[i][j] == order ? 1.0 : 0.0);
                }
            }

            index[order] = sum / 2.0;

            if ((order >= 1) && (order <= 10)) {
                ggi_map.put(build_name("GGI", String.valueOf(order)),
                        String.valueOf(index[order]));
            }
        }

        return index;
    }

    @Override
    protected void calc_mean_topol_charge_index(double[] topol_charge_index,
            int[][] topo_matr)
    {
        double[] vert_num = get_vert_num(topo_matr);

        /*
           All orders starting from 1 are included int JGT computation as
           in PaDEL-Descriptor.

           From: PaDEL-Descriptor version 2.21
           Author: Chun Wei Yap
           Class: TopologicalChargeDescriptor
           Method: calculate
           Availability: http://yapcwsoft.com/dd/padeldescriptor
         */
        double[] index = new double[topo_matr.length > 11 ?
                topo_matr.length : 11];

        var jgi_map = get_jgi_map();

        for (int order = 0; order < index.length; order++) {
            index[order] = vert_num[order] > 0
                    ? topol_charge_index[order] / vert_num[order] : 0.0;

            if ((order >= 1) && (order <= 10)) {
                jgi_map.put(build_name("JGI", String.valueOf(order)),
                        String.valueOf(index[order]));
            }
        }

        get_jgt_map().put("JGT", String.valueOf(
                new UtilsCalc().calc_sum(index)));
    }

    @Override
    protected double[] get_vert_num(int[][] topo_matr)
            throws NumberFormatException
    {
        /*
           All orders starting from 1 are considered for JGT computation as
           in PaDEL-Descriptor.

           See PaDEL-Descriptor version 2.21
           Author: Chun Wei Yap
           Class: TopologicalChargeDescriptor
           Method: calculate
           Availability: http://yapcwsoft.com/dd/padeldescriptor
         */
        double[] num = new double[topo_matr.length > 11 ?
                topo_matr.length : 11];

        for (int order = 1; order < num.length; order++) {
            int sum = 0;

            for (int i = 0; i < topo_matr.length; i++) {
                for (int j = i + 1; j < topo_matr[0].length; j++) {
                    if (topo_matr[i][j] == order) {
                        sum++;
                    }
                }
            }

            num[order] = (double) sum;
        }

        num[0] = 0.0; // num[0] should be not used. Set to 0 to ensure
                      // correct JGT computation

        return num;
    }
}
