/****************************************************************************
 * Copyright (C) from 2009 to Present EPAM Systems.
 *
 * This file is part of Indigo toolkit.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

#ifndef __molecule_arom_h__
#define __molecule_arom_h__

#include "base_cpp/tlscont.h"

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable : 4251)
#endif

namespace indigo
{

    class Graph;
    class Molecule;
    class QueryMolecule;
    class BaseMolecule;

    struct AromaticityOptions
    {
        enum Method
        {
            BASIC,
            GENERIC
        };

        Method method;
        bool dearomatize_check;

        bool unique_dearomatization;

        AromaticityOptions(Method method = BASIC) : method(method), dearomatize_check(true), unique_dearomatization(false)
        {
        }
    };

    // Aromatization classes
    class DLLEXPORT AromatizerBase
    {
    public:
        explicit AromatizerBase(BaseMolecule& molecule);
        virtual ~AromatizerBase();

        void aromatize();
        void reset(void);

        bool isBondAromatic(int e_idx);
        const byte* isBondAromaticArray(void);

        void addAromaticCycle(int id, const int* cycle, int cycle_len);
        void removeAromaticCycle(int id, const int* cycle, int cycle_len);
        bool handleUnsureCycles();

        void setBondAromaticCount(int e_idx, int count);

        DECL_ERROR;

    protected:
        // Functions for overloading
        virtual bool _checkVertex(int v_idx);
        virtual bool _isCycleAromatic(const int* cycle, int cycle_len) = 0;
        virtual void _handleAromaticCycle(const int* cycle, int cycle_len);
        virtual bool _acceptOutgoingDoubleBond(int atom, int bond)
        {
            return false;
        }

    protected:
        enum
        {
            MAX_CYCLE_LEN = 22
        };

        struct CycleDef
        {
            int id;
            bool is_empty;
            int length;
            int cycle[MAX_CYCLE_LEN];
        };

        BaseMolecule& _basemol;

        CP_DECL;
        TL_CP_DECL(Array<byte>, _bonds_arom);
        TL_CP_DECL(Array<int>, _bonds_arom_count);
        TL_CP_DECL(Array<CycleDef>, _unsure_cycles);
        TL_CP_DECL(Array<int>, _cycle_atoms);
        int _cycle_atoms_mark;

        bool _checkDoubleBonds(const int* cycle, int cycle_len);
        void _aromatizeCycle(const int* cycle, int cycle_len);
        void _handleCycle(const Array<int>& vertices);

        static bool _cb_check_vertex(Graph& graph, int v_idx, void* context);
        static bool _cb_handle_cycle(Graph& graph, const Array<int>& vertices, const Array<int>& edges, void* context);

        int _cyclesHandled;
        int _unsureCyclesCount;
    };

    class DLLEXPORT MoleculeAromatizer : public AromatizerBase
    {
    public:
        // Interface function for aromatization
        static bool aromatizeBonds(Molecule& mol, const AromaticityOptions& options);

        MoleculeAromatizer(Molecule& molecule, const AromaticityOptions& options);
        void precalculatePiLabels();

        static void findAromaticAtoms(BaseMolecule& mol, Array<int>* atoms, Array<int>* bonds, const AromaticityOptions& options);

    protected:
        virtual bool _checkVertex(int v_idx);
        virtual bool _isCycleAromatic(const int* cycle, int cycle_len);
        virtual bool _acceptOutgoingDoubleBond(int atom, int bond);

        int _getPiLabel(int v_idx);
        int _getPiLabelByConn(int v_idx, int conn);

        AromaticityOptions _options;

        CP_DECL;
        TL_CP_DECL(Array<int>, _pi_labels);
    };

    class QueryMoleculeAromatizer : public AromatizerBase
    {
    public:
        // Interface function for query molecule aromatization
        static bool aromatizeBonds(QueryMolecule& mol, const AromaticityOptions& options);

        enum
        {
            EXACT,
            FUZZY
        };

        QueryMoleculeAromatizer(QueryMolecule& molecule, const AromaticityOptions& options);

        void setMode(int mode);
        void precalculatePiLabels();

    protected:
        struct PiValue
        {
            PiValue()
            {
            }
            PiValue(int min, int max) : min(min), max(max)
            {
            }

            bool canBeAromatic()
            {
                return min != -1;
            }

            int min, max;
        };

        virtual bool _checkVertex(int v_idx);
        virtual bool _isCycleAromatic(const int* cycle, int cycle_len);
        virtual void _handleAromaticCycle(const int* cycle, int cycle_len);
        virtual bool _acceptOutgoingDoubleBond(int atom, int bond);

        static bool _aromatizeBondsExact(QueryMolecule& mol, const AromaticityOptions& options);
        static bool _aromatizeBondsFuzzy(QueryMolecule& mol, const AromaticityOptions& options);

        static bool _aromatizeBonds(QueryMolecule& mol, int additional_atom, const AromaticityOptions& options);

        static bool _aromatizeRGroupFragment(QueryMolecule& fragment, bool add_single_bonds, const AromaticityOptions& options);

        PiValue _getPiLabel(int v_idx);

        CP_DECL;
        TL_CP_DECL(Array<PiValue>, _pi_labels);
        TL_CP_DECL(Array<CycleDef>, _aromatic_cycles);

        int _mode;
        bool _collecting;
        AromaticityOptions _options;
    };

    // Structure that keeps query infromation abount bonds that
    // can be aromatic in the substructure search.
    class DLLEXPORT QueryMoleculeAromaticity
    {
    public:
        bool canBeAromatic(int edge_index) const;
        void setCanBeAromatic(int edge_index, bool state);
        void clear();

    private:
        Array<bool> can_bond_be_aromatic;
    };

} // namespace indigo

#ifdef _WIN32
#pragma warning(pop)
#endif

#endif
