/***************************************************************************
    copyright            : (C) 2008, 2009 by Carsten Niehaus
    email                : cniehaus@kde.org
    Copyright 2008 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kalzium_engine.h"

#include <element.h>
#include <elementparser.h>
#include <psetables.h>

#include <QFile>
#include <QLatin1String>
#include <QStringList>

#include <KLocale>
#include <KUrl>
#include <KStandardDirs>
#include <KConfigGroup>

#include "plasma/datacontainer.h"


KalziumEngine::KalziumEngine(QObject* parent, const QVariantList& args)
        : Plasma::DataEngine(parent)
{
    KGlobal::locale()->insertCatalog("libkdeedu");
    Q_UNUSED(args)
    // we don't need the data to fly by any faster - even at a second it's hard to read.
    setMinimumPollingInterval(1000);

    // init random sequence
    m_random = new KRandomSequence(QDateTime::currentDateTime().toTime_t());

    // reading elements
    ElementSaxParser * parser = new ElementSaxParser();

    QFile xmlFile(KStandardDirs::locate("data", "libkdeedu/data/elements.xml"));
    QXmlInputSource source(&xmlFile);
    QXmlSimpleReader reader;

    reader.setContentHandler(parser);
    reader.parse(source);

    m_elements = parser->getElements();
    delete parser;

    //Initialising the MoleculeParser
    m_moleculeParser = new MoleculeParser(m_elements);

    // Initialising the Table typ.
    m_currentTableTyp = "Classic";
}

KalziumEngine::~KalziumEngine()
{
    delete m_random;
    delete m_moleculeParser;
    qDeleteAll(m_elements);
}

QStringList KalziumEngine::sources() const
{
    // BlueObelisk:Element:# is created dynamically, so we don't advertise it here.
    QStringList list;
    list << QLatin1String("BlueObelisk:RandomElement");
    list << QLatin1String("BlueObelisk:Element:<n>:");
    list << QLatin1String("Fact");
    return list;
}

bool KalziumEngine::sourceRequestEvent(const QString &source)
{
    currentSource = &source;

    // return a randomly chosen element
    if (currentSource->startsWith(QLatin1String("BlueObelisk:"))) {
        setElementData();
        return true;
    }

    if (currentSource->startsWith(QLatin1String("Molecule:Parser:"))) {
        setMoleculeData();
        return true;
    }

    if (currentSource->startsWith(QLatin1String("Table:"))) {
        setPeriodicTableData();
    }

    return false;
}



bool KalziumEngine::setElementData()
{
//     qDebug() << "updateSourceElement";
    Element *currentElement;

    if (currentSource->operator==("BlueObelisk:RandomElement")) {
        // decide for a randomly chosen element
        currentElement = getRandomElement();
    } else if (currentSource->operator==("Fact")) {
        setData(*currentSource, "fact", generateFact());
    } else {
        currentElement = getElement(getKeyWord().toInt());
    }

    if (!currentElement) {
        return false;
    }

    // fill the engine with data
    setData(*currentSource, "number", currentElement->dataAsString(ChemicalDataObject::atomicNumber));
    setData(*currentSource, "bp", currentElement->dataAsString(ChemicalDataObject::boilingpoint));
    setData(*currentSource, "mp", currentElement->dataAsString(ChemicalDataObject::meltingpoint));
    setData(*currentSource, "name", currentElement->dataAsString(ChemicalDataObject::name));
    setData(*currentSource, "mass", currentElement->dataAsString(ChemicalDataObject::mass));
    setData(*currentSource, "symbol", currentElement->dataAsString(ChemicalDataObject::symbol));
    setData(*currentSource, "electronconf", currentElement->dataAsString(ChemicalDataObject::electronicConfiguration));
    setData(*currentSource, "period", currentElement->dataAsString(ChemicalDataObject::period));
    setData(*currentSource, "group", currentElement->dataAsString(ChemicalDataObject::group));
    setData(*currentSource, "en", currentElement->dataAsString(ChemicalDataObject::electronegativityPauling));
    setData(*currentSource, "family", currentElement->dataAsString(ChemicalDataObject::family));

    // position Information
    int elementNumber = currentElement->dataAsVariant(ChemicalDataObject::atomicNumber).toInt();
    pseTable *currentTable = pseTables::instance()->getTabletype(m_currentTableTyp);
    setData(*currentSource, "x", currentTable->elementCoords(elementNumber).x());
    setData(*currentSource, "y", currentTable->elementCoords(elementNumber).y());
    setData(*currentSource, "previousof", currentTable->previousOf(elementNumber));
    setData(*currentSource, "nextof", currentTable->nextOf(elementNumber));

    // TODO add more/all data.

    return true;
}

QString KalziumEngine::getKeyWord(int id)
{
    QStringList idList;
    idList = currentSource->split(':');

    if (id == -1) {
        return idList.last();
    }

    if (idList.length() < id) {
        return QString();
    }

    return idList.at(id);
}


Element* KalziumEngine::getElement(int number)
{
    if (number > 0 && number <= m_elements.count()) {
        //Element N is N-1 in the list
        return m_elements.at(number - 1);
    }

    return 0;
}

Element* KalziumEngine::getRandomElement()
{
    qDebug() << "setting a random element";
    return m_elements.at(m_random->getLong(m_elements.count()));
}

QString KalziumEngine::generateFact()
{
    int rand = m_random->getLong(3);
    int eNumber = m_random->getLong(95);

    Element *currentElement;

    currentElement = getElement(eNumber);

    if (!currentElement) {
        return i18n("An error occurred. No element found.");
    }

    QString bp =        currentElement->dataAsString(ChemicalDataObject::boilingpoint);
    QString mp =        currentElement->dataAsString(ChemicalDataObject::meltingpoint)       ;
    QString name =      currentElement->dataAsString(ChemicalDataObject::name)      ;
    QString mass =      currentElement->dataAsString(ChemicalDataObject::mass)      ;
    QString symbol =    currentElement->dataAsString(ChemicalDataObject::symbol)       ;

    switch (rand) {
    case 0:
        return i18n("Did you know that\n the element %1 has the symbol %2?", name, symbol);
    case 1:
        return i18n("Did you know that\n %1 (%2) weighs %3 u?", name, symbol, mass);
    case 2:
        return i18n("Did you know that\n %1 (%2) weighs %3 u?", name, symbol, mass);
    default:
        return i18n("Did you know that\n the element %1 has the symbol %2?", name, symbol);
    }

    return i18n("An error occurred.");
}


bool KalziumEngine::setPeriodicTableData()
{
    // "Table:typ"

    if (getKeyWord() == "list") {
        setData(*currentSource, "tablelist", pseTables::instance()->tables());
        return true;
    }

    QString tableType;
    tableType = getKeyWord();

    if (tableType.isEmpty()) {
        return false;
    }

    pseTable *currentTable = pseTables::instance()->getTabletype(tableType);

    if (currentTable == 0) {
        return false;
    }

    m_currentTableTyp = tableType;

    setData(*currentSource, "description", currentTable->description());
    setData(*currentSource, "tablesize", currentTable->tableSize());
    setData(*currentSource, "firstelement", currentTable->firstElement());
    setData(*currentSource, "lastelement", currentTable->lastElement());
//     setData(*currentSource, "elementlist", currentTable->elements());

    return true;
}


bool KalziumEngine::setMoleculeData()
{
    QString          molecule;
    double           mass;
    ElementCountMap  elementMap;

    // Molecule:Parser:CH4
    molecule = getKeyWord();

    if (!m_moleculeParser->weight(molecule, &mass, &elementMap)) {
        return false;
    }

    setData(*currentSource, "molMass", mass);
    setData(*currentSource, "niceMolecule", sumUpMolecue(elementMap));
    return true;
}

QString KalziumEngine::sumUpMolecue(ElementCountMap &elementMap)
{
    QString niceMolecule;
    foreach (ElementCount * Ecount , elementMap.map()) {
        niceMolecule.append(Ecount->element()->dataAsString(ChemicalDataObject::symbol));
        if (Ecount->count() > 1) {
            niceMolecule.append(QString::number(Ecount->count()));
        }
    }
    return niceMolecule;
}


#include "kalzium_engine.moc"

