/*
 * 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.qsarwb;

import qsardes.descriptor.DescriptorTopologicalCharge;
import qsardes.util.UtilsCalc;

/**
 * Topological charge descriptors (QSAR-WB Descriptors).
 * <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 QsarWBTopologicalCharge extends DescriptorTopologicalCharge
{
    public QsarWBTopologicalCharge()
    {
    }

    @Override
    protected double[] get_topol_charge_index(double[][] charge_term_matr,
            int[][] topo_matr)
    {
        var order_list = get_order_list();

        double[] index = new double[order_list.size()];

        final int ORDER_OFFSET = Integer.parseInt(order_list.get(0));

        var ggi_map = get_ggi_map();

        for (var order : order_list) {
            int order_num = Integer.parseInt(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_num ? 1.0 : 0.0);
                }
            }

            index[order_num - ORDER_OFFSET] = sum / 2.0;

            ggi_map.put(build_name("GGI", order),
                    String.valueOf(index[order_num - ORDER_OFFSET]));
        }

        return index;
    }

    @Override
    protected void calc_mean_topol_charge_index(double[] topol_charge_index,
            int[][] topo_matr)
    {
        var order_list = get_order_list();

        double[] vert_num = get_vert_num(topo_matr);

        double[] index = new double[order_list.size()];

        final int ORDER_OFFSET = Integer.parseInt(order_list.get(0));

        var jgi_map = get_jgi_map();

        for (var order : order_list) {
            int order_num = Integer.parseInt(order) - ORDER_OFFSET;

            index[order_num] = vert_num[order_num] > 0
                    ? topol_charge_index[order_num] / vert_num[order_num] : 0;

            jgi_map.put(build_name("JGI", order),
                    String.valueOf(index[order_num]));
        }

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

    @Override
    protected double[] get_vert_num(int[][] topo_matr)
            throws NumberFormatException
    {
        var order_list = get_order_list();

        double[] num = new double[order_list.size()];

        final int ORDER_OFFSET = Integer.parseInt(order_list.get(0));

        for (var order : order_list) {
            int order_num = Integer.parseInt(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_num) {
                        sum++;
                    }
                }
            }

            num[order_num - ORDER_OFFSET] = (double) sum;
        }

        return num;
    }
}
