// 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/>.

#include <QList>
#include <qxml.h>
#include <qxmlstream.h>

#include "defs.h"
#include "xml_cml.h"

// bool CMLParser::start Document()
// {
//     qDebug() << "New CML parser started.";
//     return true;
// }

bool CMLParser::startElement() {
    QXmlStreamAttributes attr = xml->attributes();
    qDebug() << "Start:" << xml->name();
    QString qName = xml->name().toString().toUpper();
    if (qName == "ATOM") {
        states = CML_ATOM;
        tmp_pt = new DPoint;
        tmp_pt->id = attr.value("id").toString();
        qDebug() << "Atom id=" << attr.value("id");

    } else if (qName == "BOND") {
        states = CML_BOND;
        tmp_bond = new Bond(r);
        tmp_bond->setID(attr.value("id").toString());
        ep1 = 0;
        ep2 = 0;

    } else if (qName == "FLOAT") {
        last_builtin = attr.value("builtin").toString().toUpper();
        if (last_builtin == "X3")
            last_builtin = "X2";
        else if (last_builtin == "X3")
            last_builtin = "Y2";

    } else if (qName == "STRING") {
        last_builtin = attr.value("builtin").toString().toUpper();
    }

    return true;
}

bool CMLParser::endElement() {
    qDebug() << "End:" << xml->name();
    QString qName = xml->name().toString().toUpper();
    if (qName == "ATOM") {
        localPoints.append(tmp_pt);
        tmp_pt = 0;
        states = CML_NONE;
        qDebug() << "finished atom";

    } else if (qName == "BOND") {
        tmp_bond->setPoints(ep1, ep2);
        localBonds.append(tmp_bond);
        tmp_bond = 0;
        states = CML_NONE;
        qDebug() << "finished bond";
    }
    return true;
}

bool CMLParser::characters() {
    QStringRef ch = xml->text();
    qDebug() << "char:" << ch << ":";
    if (states == CML_ATOM) {
        if (last_builtin == "ELEMENTTYPE")
            tmp_pt->element = ch.toString();
        if (last_builtin == "X2")
            tmp_pt->x = ch.toDouble();
        if (last_builtin == "Y2")
            tmp_pt->y = ch.toDouble();
    }
    if (states == CML_BOND) {
        if (last_builtin == "ATOMREF") {
            foreach (tmp_pt, localPoints) {
                if (tmp_pt->id == ch)
                    break;
            }
            if (ep1 == 0)
                ep1 = tmp_pt;
            else
                ep2 = tmp_pt;
        }
        if (last_builtin == "ORDER")
            tmp_bond->setOrder(ch.toInt());
        if (last_builtin == "STEREO") {
            if (ch == "H")
                tmp_bond->setOrder(7);
            if (ch == "W")
                tmp_bond->setOrder(5);
        }
    }
    return true;
}

// bool CMLParser::ignored( const QString & ch )
// {
//     qDebug() << "ignored:" << ch << ":";
//     return true;
// }

QList<DPoint *> CMLParser::getPoints() { return localPoints; }

QList<Bond *> CMLParser::getBonds() { return localBonds; }

bool CMLParser::parse() {
    while (!xml->atEnd()) {
        xml->readNext();

        switch (xml->tokenType()) {
        case QXmlStreamReader::StartElement:
            startElement();
            break;
        case QXmlStreamReader::EndElement:
            endElement();
            break;
        case QXmlStreamReader::Characters:
            if (!xml->isWhitespace())
                characters();
            break;
        default:
            qDebug() << "ignored: '" << xml->text() << "'"
                     << "of type" << xml->tokenString();
            break;
        }
    }

    return !xml->hasError();
}
