/*
 * International Chemical Identifier (InChI)
 * Version 1
 * Software version 1.07
 * April 30, 2024
 *
 * MIT License
 *
 * Copyright (c) 2024 IUPAC and InChI Trust
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
*
* The InChI library and programs are free software developed under the
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
 * Originally developed at NIST.
 * Modifications and additions by IUPAC and the InChI Trust.
 * Some portions of code were developed/changed by external contributors
 * (either contractor or volunteer) which are listed in the file
 * 'External-contributors' included in this distribution.
 *
 * info@inchi-trust.org
 *
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <errno.h>
#include <limits.h>
#include <float.h>
#include <math.h>

#include "e_mode.h"

#include "../../../../INCHI_BASE/src/inchi_api.h"
#include "../../../../INCHI_BASE/src/bcf_s.h"

#include "e_ctl_data.h"
#include "e_comdef.h"
#include "e_util.h"
#include "e_ichicomp.h"

#include "e_readmol.h"
#include "e_inpdef.h"

#include "e_inchi_atom.h"


#define MIN_STDATA_X_COORD           0.0
#define MAX_STDATA_X_COORD         256.0
#define MIN_STDATA_Y_COORD           0.0
#define MAX_STDATA_Y_COORD         256.0
#define MIN_STDATA_Z_COORD           0.0
#define MAX_STDATA_Z_COORD         256.0
#define MAX_STDATA_AVE_BOND_LENGTH  20.0
#define MIN_STDATA_AVE_BOND_LENGTH  10.0


/* local prototypes */
inchi_Atom* mol_to_inchi_Atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inchi_Atom* at_inp,
                               int bDoNotAddH, int *err, char *pStrErr );
int mol_to_inchi_Atom_xyz( MOL_DATA* mol_data, int num_atoms, inchi_Atom* at, int *err, char *pStrErr );

int MolfileToInchi_Atom( FILE *inp_molfile, int bDoNotAddH, inchi_Atom **at, int max_num_at,
                      int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue,
                      long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr );
long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr );


 /* too long ave. bond length prevents structure from displaying  */
 /* According to Steve, a standard bond length is 10. 9-24-97 DCh */
 /* Ave. bond length in MainLib is 20. Also fixed Average         */
 /* bond length calculation by introducing num_avg_bonds          */
 /* in mol_to_stdata(). 12-9-99 DCh.                              */

/******************************************************************************************************/
inchi_Atom* mol_to_inchi_Atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inchi_Atom* at_inp,
                       int bDoNotAddH, int *err, char *pStrErr )
{
    inchi_Atom *at = NULL;
    /* char      *bond_stereo = NULL; */
    AT_NUM   *p1, *p2;
    int       i, a1, a2, n1, n2, bonds;
    S_CHAR    cBondStereo, cBondType;
    S_CHAR    cStereo1, cStereo2;

    *err = 0;
    *num_atoms = *num_bonds = 0;
    /* check if MOLfile contains atoms */
    if (!mol_data || !mol_data->ctab.MolAtom ||
         (0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond) ||
         0 >= ( *num_atoms = mol_data->ctab.nNumberOfAtoms )) /* djb-rwth: addressing LLVM warning */
    {
/* MOLFILE_ERR_SET (*err, 0, "Empty structure"); */
        goto exit_function; /*  no structure */
    }
    /* allocate memory if necessary */
    if (at_inp)
    {
        at = at_inp;
    }
    else
        if (!( at = e_CreateInchi_Atom( *num_atoms ) ))
        {
            *err = -1;
            MOLFILE_ERR_FIN( *err, -1, exit_function, "Out of RAM" );
        }

        /* copy atom info */
    for (i = 0; i < *num_atoms; i++)
    {
        e_mystrncpy( at[i].elname, mol_data->ctab.MolAtom[i].szAtomSymbol, sizeof( at->elname ) );
        /* at[i].chem_bonds_valence = mol_data->ctab.MolAtom[i].cValence; */ /*  MOLfile valence; will change */
        at[i].isotopic_mass = mol_data->ctab.MolAtom[i].cMassDifference;
        at[i].charge = mol_data->ctab.MolAtom[i].cCharge;
        at[i].radical = mol_data->ctab.MolAtom[i].cRadical;
        /* coordinates are copied in mol_to_inchi_Atom_xyz() */
#if( SINGLET_IS_TRIPLET == 1 )
        if (at[i].radical == RADICAL_SINGLET)
        {
            at[i].radical = RADICAL_TRIPLET;
        }
#endif
        /* removed parsing at[i].elname to extract H, charge, radical from the
           Molfile alias record now this is done in the INChI dll */
    }

    /* copy bond info */
    for (i = 0, bonds = 0; i < mol_data->ctab.nNumberOfBonds; i++)
    {
        cBondStereo = mol_data->ctab.MolBond[i].cBondStereo;
        cBondType = mol_data->ctab.MolBond[i].cBondType;
        a1 = mol_data->ctab.MolBond[i].nAtomNo1 - 1;
        a2 = mol_data->ctab.MolBond[i].nAtomNo2 - 1;

        if (a1 < 0 || a1 >= *num_atoms ||
             a2 < 0 || a2 >= *num_atoms ||
             a1 == a2)
        {
            *err |= 1; /*  bond for impossible atom number(s); ignored */
            MOLFILE_ERR_SET( *err, 0, "Bond to nonexistent atom" );
            continue;
        }
        /*  check for multiple bonds between same atoms */
        p1 = e_is_in_the_slist( at[a1].neighbor, (AT_NUM) a2, at[a1].num_bonds );
        p2 = e_is_in_the_slist( at[a2].neighbor, (AT_NUM) a1, at[a2].num_bonds );
        if (( p1 || p2 ) && ( p1 || at[a1].num_bonds < MAXVAL ) && ( p2 || at[a2].num_bonds < MAXVAL ))
        {
            n1 = p1 ? ( p1 - at[a1].neighbor ) : at[a1].num_bonds++;
            n2 = p2 ? ( p2 - at[a2].neighbor ) : at[a2].num_bonds++;
            MOLFILE_ERR_SET( *err, 0, "Multiple bonds between two atoms" );
            *err |= 2; /*  multiple bonds between atoms */
        }
        else
            if (!p1 && !p2 && at[a1].num_bonds < MAXVAL && at[a2].num_bonds < MAXVAL)
            {
                n1 = at[a1].num_bonds++;
                n2 = at[a2].num_bonds++;
                bonds++;
            }
            else
            {
                char szMsg[64];
                *err |= 4; /*  too large number of bonds. Some bonds ignored. */
                sprintf( szMsg, "Atom '%s' has more than %d bonds", at[a1].num_bonds >= MAXVAL ? at[a1].elname : at[a2].elname, MAXVAL );
                MOLFILE_ERR_SET( *err, 0, szMsg );
                continue;
            }
        if (cBondType < MIN_INPUT_BOND_TYPE || cBondType > MAX_INPUT_BOND_TYPE)
        {
            char szBondType[16];
            sprintf( szBondType, "%d", cBondType );
            cBondType = 1;
            MOLFILE_ERR_SET( *err, 0, "Unrecognized bond type:" );
            MOLFILE_ERR_SET( *err, 0, szBondType );
            *err |= 8; /*  Unrecognized Bond type replaced with single bond */
        }
        /* bond type */
        at[a1].bond_type[n1] =
            at[a2].bond_type[n2] = cBondType;
            /* connection */
        at[a1].neighbor[n1] = (AT_NUM) a2;
        at[a2].neighbor[n2] = (AT_NUM) a1;
        /* stereo */
        switch (cBondStereo)
        {
            case INPUT_STEREO_DBLE_EITHER:  /* 3 */
                cStereo1 = INCHI_BOND_STEREO_DOUBLE_EITHER;
                cStereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER;
                break;
            case INPUT_STEREO_SNGL_UP:      /* 1 */
                cStereo1 = INCHI_BOND_STEREO_SINGLE_1UP;
                cStereo2 = INCHI_BOND_STEREO_SINGLE_2UP;
                break;
            case INPUT_STEREO_SNGL_EITHER:  /* 4 */
                cStereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER;
                cStereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER;
                break;
            case INPUT_STEREO_SNGL_DOWN:    /* 6 */
                cStereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN;
                cStereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN;
                break;
            case 0:
                cStereo1 = INCHI_BOND_STEREO_NONE;
                cStereo2 = INCHI_BOND_STEREO_NONE;
                break;
            default:
                *err |= 16; /*  Ignored unrecognized Bond stereo */
                MOLFILE_ERR_SET( *err, 0, "Unrecognized bond stereo" );
                continue;
        }
        at[a1].bond_stereo[n1] = cStereo1; /*  >0: the wedge (pointed) end is at this atom */
        at[a2].bond_stereo[n2] = cStereo2; /*  <0: the wedge (pointed) end is at the opposite atom */
    }
    *num_bonds = bonds;

    /* special Molfile valences */
    for (a1 = 0; a1 < *num_atoms; a1++)
    {
        int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1], bond_type;
        int chem_bonds_valence, valence;
        if (mol_data->ctab.MolAtom[a1].cValence &&
            ( mol_data->ctab.MolAtom[a1].cValence != 15 || at[a1].num_bonds ))
        {
/* Molfile contains special valence => calculate number of H */
            memset( num_bond_type, 0, sizeof( num_bond_type ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            valence = mol_data->ctab.MolAtom[a1].cValence; /*  save atom valence if available */
            for (n1 = 0; n1 < at[a1].num_bonds; n1++)
            {
                bond_type = at[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE;
                if (bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE)
                {
                    bond_type = 0;
                    MOLFILE_ERR_SET( *err, 0, "Unknown bond type in MOLfile assigned as a single bond" );
                }
                num_bond_type[bond_type] ++;
            }
            chem_bonds_valence = 0;
            for (n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1++)
            {
                chem_bonds_valence += ( MIN_INPUT_BOND_TYPE + n1 ) * num_bond_type[n1];
            }
            if (MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE &&
                ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN - MIN_INPUT_BOND_TYPE] ))
            {
/* accept input aromatic bonds for now */
                switch (n2)
                {
                    case 2:
                        chem_bonds_valence += 3;  /* =A- */
                        break;
                    case 3:
                        chem_bonds_valence += 4;  /* =A< */
                        break;
                    default:
                        /*  if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */
                        for (n1 = 0; n1 < at[a1].num_bonds; n1++)
                        {
                            if (at[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN)
                            {
                                a2 = at[a1].neighbor[n1];
                                p1 = e_is_in_the_slist( at[a2].neighbor, (AT_NUM) a1, at[a2].num_bonds );
                                if (p1)
                                {
                                    at[a1].bond_type[n1] =
                                        at[a2].bond_type[p1 - at[a2].neighbor] = INCHI_BOND_TYPE_SINGLE;
                                }
                                else
                                {
                                    *err = -2;  /*  Program error */
                                    MOLFILE_ERR_SET( *err, 0, "Program error interpreting MOLfile" );
                                    goto exit_function; /*  no structure */
                                }
                            }
                        }
                        chem_bonds_valence += n2;
                        *err |= 32;
                        MOLFILE_ERR_SET( *err, 0, "Atom has more than 3 aromatic bonds" );
                        break;
                }
            }
            /*************************************************************************************
             *
             *  Set number of hydrogen atoms
             */
            if (valence >= chem_bonds_valence)
            {
                at[a1].num_iso_H[0] = valence - chem_bonds_valence;
            }
        }
        else
            if (mol_data->ctab.MolAtom[a1].cAtomAliasedFlag)
            {
                at[a1].num_iso_H[0] = 0;
            }
            else
                if (mol_data->ctab.MolAtom[a1].cValence == 15 && !at[a1].num_bonds)
                {
                    at[a1].num_iso_H[0] = 0;
                }
                else
                    if (!bDoNotAddH)
                    {
                        at[a1].num_iso_H[0] = -1;
                    }
    }

exit_function:;
    return at;
}
/******************************************************************************************************/
int mol_to_inchi_Atom_xyz( MOL_DATA* mol_data, int num_atoms, inchi_Atom* at, int *err, char *pStrErr )
{
    int      i, num_dimensions = 0;
    int      num_bonds;
    double max_x = -1.0e32, max_y = -1.0e32, max_z = -1.0e32;
    double min_x = 1.0e32, min_y = 1.0e32, min_z = 1.0e32;
    double macheps = 1.0e-10, small_coeff = 0.00001;
    double x_coeff, y_coeff, z_coeff, coeff, average_bond_length;

    /*  *err = 0; */
    /* check if MOLfile contains atoms */
    if (!mol_data || !mol_data->ctab.MolAtom ||
         (0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond) ||
         0 >= ( num_atoms = mol_data->ctab.nNumberOfAtoms )) /* djb-rwth: addressing LLVM warning */
    {
        goto exit_function; /*  no structure */
    }
    /* copy atom info */
    for (i = 0; i < num_atoms; i++)
    {
        max_x = inchi_max( mol_data->ctab.MolAtom[i].fX, max_x );
        min_x = inchi_min( mol_data->ctab.MolAtom[i].fX, min_x );
        max_y = inchi_max( mol_data->ctab.MolAtom[i].fY, max_y );
        min_y = inchi_min( mol_data->ctab.MolAtom[i].fY, min_y );
        max_z = inchi_max( mol_data->ctab.MolAtom[i].fZ, max_z );
        min_z = inchi_min( mol_data->ctab.MolAtom[i].fZ, min_z );
    }

    /* copy bond info */
    num_bonds = 0;
    average_bond_length = 0.0;
    for (i = 0; i < mol_data->ctab.nNumberOfBonds; i++)
    {
        int  a1 = mol_data->ctab.MolBond[i].nAtomNo1 - 1;
        int  a2 = mol_data->ctab.MolBond[i].nAtomNo2 - 1;
        double dx = mol_data->ctab.MolAtom[a1].fX - mol_data->ctab.MolAtom[a2].fX;
        double dy = mol_data->ctab.MolAtom[a1].fY - mol_data->ctab.MolAtom[a2].fY;
        double dz = mol_data->ctab.MolAtom[a1].fZ - mol_data->ctab.MolAtom[a2].fZ;

        if (a1 < 0 || a1 >= num_atoms ||
             a2 < 0 || a2 >= num_atoms ||
             a1 == a2)
        {
            *err |= 1; /*  bond for impossible atom number(s); ignored */
            MOLFILE_ERR_SET( *err, 0, "Bond to nonexistent atom" );
            continue;
        }
        average_bond_length += sqrt( dx*dx + dy*dy + dz*dz );
        num_bonds++;
    }

    /* convert to integral coordinates */

    if (max_x - min_x <= small_coeff*( fabs( max_x ) + fabs( min_x ) ))
        x_coeff = 0.0;
    else
        x_coeff = ( MAX_STDATA_X_COORD - MIN_STDATA_X_COORD ) / ( max_x - min_x );

    if (max_y - min_y <= small_coeff*( fabs( max_y ) + fabs( min_y ) ))
        y_coeff = 0.0;
    else
        y_coeff = ( MAX_STDATA_Y_COORD - MIN_STDATA_Y_COORD ) / ( max_y - min_y );
    if (max_z - min_z <= small_coeff*( fabs( max_z ) + fabs( min_z ) ))
        z_coeff = 0.0;
    else
        z_coeff = ( MAX_STDATA_Z_COORD - MIN_STDATA_Z_COORD ) / ( max_z - min_z );

    num_dimensions = ( ( x_coeff > macheps || y_coeff > macheps ) && fabs( z_coeff ) < macheps ) ? 2 :
        ( fabs( z_coeff ) > macheps ) ? 3 : 0;

    switch (num_dimensions)
    {
        case 0:
            coeff = 0.0;
            break;
        case 2:
            /* choose the smallest stretching coefficient */
            if (x_coeff > macheps && y_coeff > macheps)
            {
                coeff = inchi_min( x_coeff, y_coeff );
            }
            else
                if (x_coeff > macheps)
                {
                    coeff = x_coeff;
                }
                else
                    if (y_coeff > macheps)
                    {
                        coeff = y_coeff;
                    }
                    else
                    {
                        coeff = 1.0;
                    }
            break;
        case 3:
            /* choose the smallest stretching coefficient */
            if (x_coeff > macheps && y_coeff > macheps)
            {
                coeff = inchi_min( x_coeff, y_coeff );
                coeff = inchi_min( coeff, z_coeff );
            }
            else
                if (x_coeff > macheps)
                {
                    coeff = inchi_min( x_coeff, z_coeff );
                }
                else
                    if (y_coeff > macheps)
                    {
                        coeff = inchi_min( y_coeff, z_coeff );
                    }
                    else
                    {
                        coeff = z_coeff;
                    }
            break;
        default:
            coeff = 0.0;
    }

    if (num_bonds > 0)
    {
        average_bond_length /= (double) num_bonds;
        if (average_bond_length * coeff > MAX_STDATA_AVE_BOND_LENGTH)
        {
            coeff = MAX_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too long bonds */ /* djb-rwth: ignoring LLVM warning: value used */
        }
        else
            if (average_bond_length * coeff < macheps)
            {
                coeff = 1.0; /* all lengths are of zero length */ /* djb-rwth: ignoring LLVM warning: value used */
            }
            else
                if (average_bond_length * coeff < MIN_STDATA_AVE_BOND_LENGTH)
                {
                    coeff = MIN_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too short bonds */ /* djb-rwth: ignoring LLVM warning: value used */
                }
    }
#if( NORMALIZE_INP_COORD == 1 )
    /* set integral coordinates */
    for (i = 0; i < num_atoms; i++)
    {
        double x = mol_data->ctab.MolAtom[i].fX;
        double y = mol_data->ctab.MolAtom[i].fY;
        double z = mol_data->ctab.MolAtom[i].fZ;
        x = ( x - min_x )*coeff + MIN_STDATA_X_COORD;
        y = ( y - min_y )*coeff + MIN_STDATA_Y_COORD;
        z = ( z - min_z )*coeff + MIN_STDATA_Z_COORD;
        /* floor() behavior is not well defined for negative arguments.
         * Use positive arguments only to get nearest integer.
         */
        at[i].x = ( x >= 0.0 ) ? (int) floor( x + 0.5 ) : -(int) floor( -x + 0.5 );
        at[i].y = ( y >= 0.0 ) ? (int) floor( y + 0.5 ) : -(int) floor( -y + 0.5 );
        at[i].z = ( z >= 0.0 ) ? (int) floor( z + 0.5 ) : -(int) floor( -z + 0.5 );
    }
#else
    /* set input coordinates */
    for (i = 0; i < num_atoms; i++)
    {
        double x = mol_data->ctab.MolAtom[i].fX;
        double y = mol_data->ctab.MolAtom[i].fY;
        double z = mol_data->ctab.MolAtom[i].fZ;
        at[i].x = x;
        at[i].y = y;
        at[i].z = z;
    }
#endif

exit_function:;
    return num_dimensions;
}
/****************************************************************************/
long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr )
{
    static char sStruct[] = "Structure #";
    static char sINCHI[] = INCHI_NAME;
    long   lMolfileNumber = 0;
    char   *p, *q = NULL;
    if (pHdr)
    {
        if (!e_inchi_memicmp( pHdr->szMoleculeName, sStruct, sizeof( sStruct ) - 1 ))
        {
            p = pHdr->szMoleculeName + sizeof( sStruct ) - 1;
            lMolfileNumber = strtol( p, &q, 10 );
            p = pHdr->szMoleculeLine2;
            if (!q || *q ||
                 e_inchi_memicmp( p, sINCHI, sizeof( sINCHI ) - 1 ) ||
                 !strstr( p + sizeof( sINCHI ) - 1, "SDfile Output" ))
            {
                lMolfileNumber = 0;
            }
        }
    }
    return lMolfileNumber;
}


int MolfileToInchi_Atom( FILE *inp_molfile, int bDoNotAddH, inchi_Atom **at, int max_num_at,
                      int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue,
                      long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr )
{
    int      num_atoms = 0;
    MOL_DATA *mol_data = NULL;
    MOL_HEADER_BLOCK OnlyHeaderBlock, *pOnlyHeaderBlock = NULL, *pHdr;
    MOL_CTAB         OnlyCtab, *pOnlyCtab = NULL;
    char             cSdfValueFirstChar = '\0';

    if (at)
    {
        pOnlyHeaderBlock = NULL;
        if (*at && max_num_at)
        {
            memset( *at, 0, max_num_at * sizeof( **at ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        }
    }
    else
    {
        pOnlyHeaderBlock = &OnlyHeaderBlock;
        pOnlyCtab = &OnlyCtab;
    }
    if (pSdfValue)
    {
        cSdfValueFirstChar = pSdfValue[0];
        pSdfValue[0] = '\0';
    }

    mol_data = e_read_sdfile_segment( inp_molfile, pOnlyHeaderBlock, pOnlyCtab, 0, NULL, 0, Id, pSdfLabel, pSdfValue, err, pStrErr );

    if (mol_data && !strcmp( mol_data->ctab.csCurrentCtabVersion, "V3000" ))
    {
        MOLFILE_ERR_SET( *err, 0, "Molfile V3000 input is not supported" );
        *err = 71 + 100;
        num_atoms = -1;
    }

    pHdr = ( mol_data && !pOnlyHeaderBlock ) ? &mol_data->hdr :
        ( !mol_data && pOnlyHeaderBlock ) ? pOnlyHeaderBlock : NULL;
    if (lMolfileNumber && pHdr)
    {
        *lMolfileNumber = GetMolfileNumber( pHdr );
    }
    if (pSdfValue &&
         pSdfLabel && pSdfLabel[0] && pHdr)
    {
        if (!e_inchi_stricmp( pSdfLabel, "MOLFILENAME" ))
        {
            e_mystrncpy( pSdfValue, pHdr->szMoleculeName, MAX_SDF_VALUE + 1 );
            e_LtrimRtrim( pSdfValue, NULL );
        }
        else
            if (!e_inchi_stricmp( pSdfLabel, "MOLFILELINE2" ))
            {
                e_mystrncpy( pSdfValue, pHdr->szMoleculeLine2, MAX_SDF_VALUE + 1 );
                e_LtrimRtrim( pSdfValue, NULL );
            }
            else
                if (!e_inchi_stricmp( pSdfLabel, "MOLFILECOMMENT" ))
                {
                    e_mystrncpy( pSdfValue, pHdr->szComment, MAX_SDF_VALUE + 1 );
                    e_LtrimRtrim( pSdfValue, NULL );
                }
        if (!pSdfValue[0])
        {
            pSdfValue[0] = cSdfValueFirstChar;
        }
    }

    if (mol_data && at && !*err)
    {
/* *at points to an allocated memory */
        if (*at && mol_data->ctab.nNumberOfAtoms <= max_num_at)
        {
            *at = mol_to_inchi_Atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr );
            if (*err >= 0)
            {
                *num_dimensions = mol_to_inchi_Atom_xyz( mol_data, num_atoms, *at, err, pStrErr );
            }
        }
        else
     /* *at points to NULL */
            if (!*at && mol_data->ctab.nNumberOfAtoms <= max_num_at)
            {
                *at = mol_to_inchi_Atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr );
                if (*err >= 0)
                {
                    *num_dimensions = mol_to_inchi_Atom_xyz( mol_data, num_atoms, *at, err, pStrErr );
                }
            }
            else
            {
                MOLFILE_ERR_SET( *err, 0, "Too many atoms" );
                *err = 70;
                num_atoms = -1;
            }
        if (*err > 0)
        {
            *err += 100;
        }
        /* 11-16-2004: use Chiral flag */
        if (num_atoms > 0 && at && *at && mol_data && pInpAtomFlags)
        {
            if (mol_data->ctab.cChiralFlag)
            {
                *pInpAtomFlags |= FLAG_INP_AT_CHIRAL;
            }
            else
            {
                *pInpAtomFlags |= FLAG_INP_AT_NONCHIRAL;
            }
        }
    }
    else
        if (!at)
        {
            num_atoms = pOnlyCtab->nNumberOfAtoms;
        }

    if (!pOnlyHeaderBlock)
    {
        e_delete_mol_data( mol_data );
    }

    return num_atoms;
}


int e_MolfileToInchi_Input( FILE *inp_molfile, inchi_InputEx *orig_at_data, int bMergeAllInputStructures,
                       int bDoNotAddH, int bAllowEmptyStructure,
                       const char *pSdfLabel, char *pSdfValue, long *lSdfId, long *lMolfileNumber,
                       INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr )
{
    /* inp_ATOM       *at = NULL; */
    int            num_dimensions_new;
    int            num_inp_bonds_new;
    int            num_inp_atoms_new;
    inchi_Atom    *at_new = NULL;
    inchi_Atom    *at_old = NULL;
    int            nNumAtoms = 0;
    int            i, j;

    if (pStrErr)
    {
        pStrErr[0] = '\0';
    }

    /*FreeOrigAtData( orig_at_data );*/

    do
    {

        at_old = orig_at_data ? orig_at_data->atom : NULL; /*  save pointer to the previous allocation */
        num_inp_atoms_new =
            MolfileToInchi_Atom( inp_molfile,
                                 bDoNotAddH,
                                 orig_at_data ? &at_new : NULL,
                                 NORMALLY_ALLOWED_INP_MAX_ATOMS, /*MAX_ATOMS,*/
                                 &num_dimensions_new, &num_inp_bonds_new,
                                 pSdfLabel, pSdfValue,
                                 lSdfId, lMolfileNumber,
                                 pInpAtomFlags,
                                 err, pStrErr );


        if (num_inp_atoms_new <= 0 && !*err)
        {
            if (!bAllowEmptyStructure)
            {
                MOLFILE_ERR_SET( *err, 0, "Empty structure" );  /* the message will be issued by the InChI library */
            }
            *err = 98;
        }
        else
            if (orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures)
            {
                *err = 0; /* end of file */
                break;
            }
            else
                if (num_inp_atoms_new > 0 && orig_at_data)
                {
/*  merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */
                    nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms;
                    if (nNumAtoms >= MAX_ATOMS)
                    {
                        MOLFILE_ERR_SET( *err, 0, "Too many atoms" );
                        *err = 70;
                        orig_at_data->num_atoms = -1;
                    }
                    else
                        if (!at_old)
                        {
            /* the first structure */
                            orig_at_data->atom = at_new;
                            at_new = NULL;
                            orig_at_data->num_atoms = num_inp_atoms_new;
                        }
                        else
                            if ((orig_at_data->atom = e_CreateInchi_Atom( nNumAtoms ))) /* djb-rwth: addressing LLVM warning */
                            {
/*  switch at_new <--> orig_at_data->at; */
                                if (orig_at_data->num_atoms)
                                {
                                    memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof( orig_at_data->atom[0] ) );
                                    /*  adjust numbering in the newly read structure */
                                    for (i = 0; i < num_inp_atoms_new; i++)
                                    {
                                        if (at_new) /* djb-rwth: fixing a NULL pointer dereference */
                                        {
                                            for (j = 0; j < at_new[i].num_bonds; j++)
                                            {
                                                at_new[i].neighbor[j] += orig_at_data->num_atoms;
                                            }
                                        }
                                    }
                                }
                                e_FreeInchi_Atom( &at_old );
                                /*  copy newly read structure */
                                if (at_new)
                                    memcpy( orig_at_data->atom + orig_at_data->num_atoms, at_new, num_inp_atoms_new * sizeof( orig_at_data->atom[0] ) ); /* djb-rwth: fixing potentially zero value of at_new */
                                /*  add other things */
                                orig_at_data->num_atoms += num_inp_atoms_new;
                            }
                            else
                            {
                                MOLFILE_ERR_SET( *err, 0, "Out of RAM" );
                                *err = -1;
                            }
                }
                else
                    if (num_inp_atoms_new > 0)
                    {
                        nNumAtoms += num_inp_atoms_new;
                    }
        e_FreeInchi_Atom( &at_new );
    }
    while (!*err && bMergeAllInputStructures);
 /*
 if ( !*err ) {
     orig_at_data->num_components =
         MarkDisconnectedComponents( orig_at_data );
     if ( orig_at_data->num_components == 0 ) {
         MOLFILE_ERR_SET (*err, 0, "No components found");
         *err = 99;
     }
     if ( orig_at_data->num_components < 0 ) {
         MOLFILE_ERR_SET (*err, 0, "Too many components");
         *err = 99;
     }
 }
 */
    e_FreeInchi_Atom( &at_new );
    if (*err)
    {
        e_FreeInchi_Input( orig_at_data );
    }
    if (*err && !( 10 < *err && *err < 20 ) && pStrErr && !pStrErr[0])
    {
        MOLFILE_ERR_SET( *err, 0, "Unknown error" );  /*   <BRKPT> */
    }
    return orig_at_data ? orig_at_data->num_atoms : nNumAtoms;
}
