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

import java.util.ArrayList;
import java.util.HashMap;
import qsardes.molecule.Molecule;
import qsardes.util.UtilsCalc;
import java.util.List;
import java.util.Map;
import org.openscience.cdk.exception.CDKException;
import qsardes.atomconst.AtomConstFunctionCall;
import qsardes.molecule.AbstractMoleculeBuilder;

/**
 * Constitutional descriptors abstract class.
 *
 * @author Nicola Chirico
 */
public abstract class DescriptorConstitutional implements Descriptor
{
    protected DescriptorConstitutional()
    {
    }

    @Override
    public String toString()
    {
        return "Constitutional";
    }

    @Override
    public DescriptorType get_descriptor_type()
    {
        return DescriptorType.valueOf("DESCRIPTOR");
    }

    @Override
    public List<String> get_descriptor_names()
    {
        List<String> list = new ArrayList<>();

        for (var w_name_map : get_weights_list()) {
            var w_name = w_name_map.entrySet().iterator().next().getKey();

            list.add("S" + w_name);
            list.add("M" + w_name);
        }

        return list;
    }

    @Override
    public Map<String, String> get_descriptor_values(
            AbstractMoleculeBuilder mol_build)
    {
        prepare_descr_map();

        try {
            mol = new Molecule(mol_build);
            mol.convert_impl_to_expl_hydrogens();
            calculate_descr();
        } catch (Exception ex) {
            System.err.println(ex);
        }

        return descr_map;
    }

    protected abstract List<Map<String, AtomConstFunctionCall>>
        get_weights_list();

    private void prepare_descr_map()
    {
        final String NAN = String.valueOf(Double.NaN);

        get_descriptor_names().forEach(name -> descr_map.put(name, NAN));
    }

    private void calculate_descr() throws CDKException
    {
        var utils_calc = new UtilsCalc();

        for (var w_name_map : get_weights_list()) {
            var map_item = w_name_map.entrySet().iterator().next();

            var weight_res = map_item.getValue().call(mol);

            double[] weights = weight_res.const_vect();

            for (int i = 0; i < weights.length; i++) {
                weights[i] /= weight_res.carbon();
            }

            double sum = utils_calc.calc_sum(weights);

            String w_name = map_item.getKey();

            descr_map.put("S" + w_name,
                    String.valueOf(sum));

            descr_map.put("M" + w_name,
                    String.valueOf(sum / (double) weights.length));
        }
    }

    private final Map<String, String> descr_map = new HashMap<>();

    private Molecule mol;
}
