// XDrawChem
// Copyright (C) 2004-2005  Bryan Herger <bherger@users.sourceforge.net>
// Copyright (C) 2020  Yaman Qalieh <ybq987@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#ifndef DPOINT_H
#define DPOINT_H

#include <cmath>

#include <QColor>
#include <QDebug>
#include <QFont>
#include <QList>
#include <QPoint>
#include <QString>
#include <QTextDocument>
#include <QTextStream>
#include <QVector>
#include <qglobal.h>

class Bond;

class DPoint {
  public:
    double x, y, z;
    double x3, y3, z3;
    double dx, dy, dz;   // where to go on this iteration of (3D/minimize)
    QString id;          // XML ID field
    QString element;     // OR text string if associated with Text
    QString elementmask; // (updated) actual display text
    QString symbol;      // only used when saving Molecules as XDC-XML
    QString hosecode;    // save HOSE codes made in molecule_tools.cpp
    QString tmphose;     // temp HOSE code (if in first sphere)
    int hosescore;       // HOSE score
    int ketos;           // number of keto groups attached (if any)
    bool aromatic;       // part of an aromatic system?
    bool inring;         // part of a ring?
    QString ringset;     // SMILES ring closure code(s) for this atom
    QString hybrid;      // hybridization (sp2, etc...) for 2D and 3D layout
    QColor color;
    QFont font;
    bool hit;
    int substituents; // how many bonds to this atom (sum of order)
    DPoint *other;    // other end of single bond.  See molecule.cpp
    Bond *otherBond;  // single Bond containing this DPoint
    // for SDG and SSSR
    DPoint *source;
    int serial;
    QList<DPoint *> neighbors;
    QVector<int> bondorder;
    QList<DPoint *> path;
    // for reconstructing rings...
    int new_order;
    // reactivity
    double partial_charge;   // + or -, should also set "color" above
    QString react_atom_type; // AMBER atom type?  Custom?
    // spectral data
    bool nmr_proton; // is this DPoint a proton added for 1H NMR prediction?
    double H1_shift;
    int H1_multiplicity;
    QString protonHOSECode;
    double C13_shift;
    QString C13_multiplicity;
    double partialCharge;
    double getC13_shift() { return C13_shift; }
    QString getC13_multiplicity() { return C13_multiplicity; }
    void setC13_shift(double in1) { C13_shift = in1; }
    void setC13_multiplicity(QString in1) { C13_multiplicity = in1; }
    bool highlighted;
    // functions
    DPoint() {
        x = 0.0;
        y = 0.0;
        z = 0.0;
        init();
    }

    DPoint(double a, double b) {
        x = a;
        y = b;
        init();
    }

    DPoint(QPoint q) {
        x = q.x();
        y = q.y();
        init();
    }

    DPoint(DPoint *op) {
        x = op->x;
        y = op->y;
        z = op->z;
        id = op->id;
        element = op->element;
        elementmask = op->elementmask;
        symbol = op->symbol;
        hosecode = op->hosecode;
        tmphose = op->tmphose;
        hosescore = op->hosescore;
        ketos = op->ketos;
        aromatic = op->aromatic;
        inring = op->inring;
        ringset = op->ringset;
        color = op->color;
        font = op->font;
        hit = op->hit;
        substituents = op->substituents;
        other = op->other;
        otherBond = op->otherBond;
        serial = op->serial;
        bondorder.resize(6);
    }

    void init() {
        highlighted = false;
        nmr_proton = false;
        element = QString("C");
        elementmask = QString(" ");
        symbol = QString("");
        color = QColor(0, 0, 0);
        font = QFont("Helvetica", 12);
        C13_shift = 0.0;
        C13_multiplicity = "NA";
        aromatic = false;
        serial = 0;
        hit = false;
        bondorder.resize(6);
        partialCharge = 0.0;
    }
    // these would be more relevant... if "substituents" were private...
    int degree() { return substituents; }
    void setDegree(int neworder) { substituents = neworder; }
    // same here -- highlighted should be private
    bool isHighlighted() { return highlighted; }
    void setHighlighted(bool neworder) { highlighted = neworder; }
    // same here -- partialCharge should be private
    double getCharge() { return partialCharge; }
    void setCharge(double nc1) { partialCharge = nc1; }

    void set(QPoint p) {
        x = (double)p.x();
        y = (double)p.y();
    }

    void set(double xn, double yn, double zn) {
        x3 = xn;
        y3 = yn;
        z3 = zn;
    }

    QPoint toQPoint() { return QPoint(qRound(x), qRound(y)); }

    QPointF toQPointF() { return QPointF(x, y); }

    void zero() {
        x = 0.0;
        y = 0.0;
        z = 0.0;
    }

    bool isZero() {
        if ((x < 0.01) && (x > -0.01) && (y < 0.01) && (y > -0.01))
            return true;
        else
            return false;
    }

    double distanceTo(DPoint *t) {
        double dx = x - t->x;
        double dy = y - t->y;
        return (double)sqrt(dx * dx + dy * dy);
    }

    double Magnitude() { return sqrt((x3 * x3) + (y3 * y3) + (z3 * z3)); }

    void Normalize() {
        double dst = Magnitude();
        x3 /= dst;
        y3 /= dst;
        z3 /= dst;
    }

    void Scale(double sf) {
        double dst = Magnitude();
        Normalize();
        dst = dst * sf;
        x3 *= dst;
        y3 *= dst;
        z3 *= dst;
    }

    void SetMagnitude(double sf) {
        double dst = Magnitude();
        sf = sf / dst;
        Normalize();
        dst = dst * sf;
        x3 *= dst;
        y3 *= dst;
        z3 *= dst;
    }

    void SetFontFromXML(QString xml_tag) {
        // qDebug() << "DP::SetFontFromXML:" << xml_tag;
        int i1, i2;

        i1 = xml_tag.indexOf("<font>");
        i2 = xml_tag.indexOf("</font>");
        xml_tag.remove(i2, 999);
        xml_tag.remove(i1, 6);
        i1 = xml_tag.indexOf("#");
        // qDebug() << xml_tag.mid( 0, i1 ) << "*" << xml_tag.mid( i1 + 1 );
        font = QFont(xml_tag.mid(0, i1), xml_tag.mid(i1 + 1).toInt());
    }

    void SetColorFromXML(QString xml_tag) {
        // qDebug() << "DP::SetColorFromXML:" << xml_tag;
        int i1, i2;
        int d1, d2, d3;

        i1 = xml_tag.indexOf("<color>");
        i2 = xml_tag.indexOf("</color>");
        xml_tag.remove(i2, 999);
        xml_tag.remove(i1, 7);

        QTextStream ts(&xml_tag, QIODevice::ReadOnly);

        ts >> d1 >> d2 >> d3;

        color = QColor(d1, d2, d3);
    }

    // set DPoint coordinates from XML
    void SetCoordFromXML(QString xml_tag) {
        // qDebug() << "DP::SetCoordFromXML:" << xml_tag;
        int i1, i2;

        i1 = xml_tag.indexOf("<coordinate2>");
        i2 = xml_tag.indexOf("</coordinate2>");
        xml_tag.remove(i2, 999);
        xml_tag.remove(i1, 13);

        QTextStream ts(&xml_tag, QIODevice::ReadOnly);

        ts >> x >> y;
    }

    void SetElementFromXML(QString xml_tag) {
        // qDebug() << "DP::SetElementFromXML:" << xml_tag;
        int i1, i2;

        i1 = xml_tag.indexOf("<element>");
        i2 = xml_tag.indexOf("</element>");
        xml_tag.remove(i2, 999);
        xml_tag.remove(i1, 9);

        element = xml_tag;
    }

    void SetElementMaskFromXML(QString xml_tag) {
        // qDebug() << "DP::SetElementMaskFromXML:" << xml_tag;
        int i1, i2;

        i1 = xml_tag.indexOf("<elemask>");
        i2 = xml_tag.indexOf("</elemask>");
        xml_tag.remove(i2, 999);
        xml_tag.remove(i1, 9);

        elementmask = xml_tag;
    }

    void SetSymbolFromXML(QString xml_tag) {
        // qDebug() << "DP::SetSymbolFromXML:" << xml_tag;
        int i1, i2;

        i1 = xml_tag.indexOf("<symtype>");
        i2 = xml_tag.indexOf("</symtype>");
        xml_tag.remove(i2, 999);
        xml_tag.remove(i1, 9);

        symbol = xml_tag;
    }

    void FromXML(QString xml_tag) {
        int i1, i2, ib, ie;

        i1 = xml_tag.indexOf("<atom ");
        ib = xml_tag.indexOf("\"", i1);
        ie = xml_tag.indexOf("\"", ib + 1);
        id = xml_tag.mid(ib + 1, ie - ib - 1);
        // qDebug() << "DP::AtomID:" << id;
        i1 = xml_tag.indexOf("<coordinate2>");
        i2 = xml_tag.indexOf("</coordinate2>") + 14;
        SetCoordFromXML(xml_tag.mid(i1, i2 - i1));
        i1 = xml_tag.indexOf("<color>");
        i2 = xml_tag.indexOf("</color>") + 8;
        SetColorFromXML(xml_tag.mid(i1, i2 - i1));
        i1 = xml_tag.indexOf("<font>");
        i2 = xml_tag.indexOf("</font>") + 7;
        SetFontFromXML(xml_tag.mid(i1, i2 - i1));
        i1 = xml_tag.indexOf("<element>");
        i2 = xml_tag.indexOf("</element>") + 10;
        SetElementFromXML(xml_tag.mid(i1, i2 - i1));
        i1 = xml_tag.indexOf("<elemask>");
        i2 = xml_tag.indexOf("</elemask>") + 10;
        SetElementMaskFromXML(xml_tag.mid(i1, i2 - i1));
        i1 = xml_tag.indexOf("<symtype>");
        i2 = xml_tag.indexOf("</symtype>") + 10;
        if (i1 >= 0)
            SetSymbolFromXML(xml_tag.mid(i1, i2 - i1));
        i1 = xml_tag.indexOf("<textobject>yes</textobject>");
        if (i1 >= 0)
            hit = true; // Create Text object associated with this DPoint
    }
    // get plain text element
    QString plainElement() {
        QTextDocument localText;
        localText.setHtml(element);
        return localText.toPlainText();
    }
    // return atomic number of atom connected in this group
    int getAtomicNumber() {
        QString localElement;
        QTextDocument localText;
        localText.setHtml(element);
        // localElement = localText.toPlainText();
        localElement = element;
        // qDebug() << "localElement:" << localElement;
        int atom1 = 999;
        if (localElement == "H")
            atom1 = 1;
        if (localElement == "C")
            atom1 = 6;
        if (localElement == "CN")
            atom1 = 6;
        if (localElement == "NC")
            atom1 = 6;
        if (localElement == "CO")
            atom1 = 6;
        if (localElement == "OC")
            atom1 = 6;
        if (localElement == "HC")
            atom1 = 6;
        if (localElement == "CH")
            atom1 = 6;
        if (localElement == "H2C")
            atom1 = 6;
        if (localElement == "CH2")
            atom1 = 6;
        if (localElement == "H3C")
            atom1 = 6;
        if (localElement == "CH3")
            atom1 = 6;
        if (localElement == "N")
            atom1 = 7;
        if (localElement == "NH")
            atom1 = 7;
        if (localElement == "HN")
            atom1 = 7;
        if (localElement == "NO")
            atom1 = 7;
        if (localElement == "ON")
            atom1 = 7;
        if (localElement == "NH2")
            atom1 = 7;
        if (localElement == "H2N")
            atom1 = 7;
        if (localElement == "NO2")
            atom1 = 7;
        if (localElement == "O2N")
            atom1 = 7;
        if (localElement == "O")
            atom1 = 8;
        if (localElement == "OH")
            atom1 = 8;
        if (localElement == "HO")
            atom1 = 8;
        if (localElement == "F")
            atom1 = 9;
        if (localElement == "P")
            atom1 = 15;
        if (localElement == "S")
            atom1 = 16;
        if (localElement == "SH")
            atom1 = 16;
        if (localElement == "HS")
            atom1 = 16;
        if (localElement == "Cl")
            atom1 = 17;
        if (localElement == "Br")
            atom1 = 35;
        if (localElement == "I")
            atom1 = 53;
        return atom1;
    }
    QString baseElement() {
        int tc1 = getAtomicNumber();
        if (tc1 == 1)
            return "H";
        if (tc1 == 6)
            return "C";
        if (tc1 == 7)
            return "N";
        if (tc1 == 8)
            return "O";
        if (tc1 == 9)
            return "F";
        if (tc1 == 15)
            return "P";
        if (tc1 == 16)
            return "S";
        if (tc1 == 17)
            return "Cl";
        if (tc1 == 35)
            return "Br";
        if (tc1 == 53)
            return "I";
        if (tc1 == 999) {
            // let's try this...
            QString bele = element;
            bele.remove("+");
            bele.remove("-");
            bele.remove("H");
            bele.remove(QRegExp("\\d+"));
            return bele;
        }
        return "X";
    }
};

#endif
