/**********************************************************************
  SurfaceExtension - Extension for generating cubes and meshes

  Copyright (C) 2009-2011 Marcus D. Hanwell

  This file is part of the Avogadro molecular editor project.
  For more information, see <http://avogadro.cc/>

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

  Avogadro 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, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  02110-1301, USA.
 **********************************************************************/

#ifndef SURFACEEXTENSION_H
#define SURFACEEXTENSION_H

#include "surfacedialog.h"

#include <avogadro/extension.h>

#include <QVector>
#include <QList>

class QProgressDialog;

namespace OpenQube
{
  class BasisSet;
  class Cube;
}

namespace Avogadro
{
  class Cube;
  class Mesh;
  class MeshGenerator;
  class VdWSurface;
  class SurfaceDialog;

  class SurfaceExtension : public Extension
  {
    Q_OBJECT
    AVOGADRO_EXTENSION("Surfaces", tr("Surfaces"),
                       tr("Calculate molecular orbitals and other surfaces"))

  public:
    SurfaceExtension(QObject* parent = 0);
    virtual ~SurfaceExtension();

    virtual QList<QAction *> actions() const;

    virtual QString menuPath(QAction* action) const;

    virtual QUndoCommand* performAction(QAction *action, GLWidget *widget);

    void setMolecule(Molecule *molecule);

  private:
    QList<unsigned long> m_cubes; // These are the standard cubes
    QVector<unsigned long> m_moCubes; // These are the MO cubes
    int m_calculationPhase;        // The calculation phase
    GLWidget* m_glwidget;
    SurfaceDialog *m_surfaceDialog;
    QList<QAction *> m_actions;
    Molecule *m_molecule;
    OpenQube::BasisSet *m_basis;   // The basis set
    QString m_loadedFileName;
    QProgressDialog *m_progress;

    Mesh *m_mesh1, *m_mesh2;
    MeshGenerator *m_meshGen1;
    MeshGenerator *m_meshGen2;

    VdWSurface *m_VdWsurface;

    Cube *m_cube;
    OpenQube::Cube *m_qube;
    Cube *m_cubeColor;

    //! Load the appropriate basis set (if possible)
    bool loadBasis();

    //! Calculate the ESP from the partial charges of the atoms on the supplied
    //! Mesh object.
    void calculateESP(Mesh *mesh);

    //! Convenience function - creates a new cube with the correct dimensions.
    Cube * newCube();
    OpenQube::Cube * newQube();

    //! Calculate the VdW cube
    void calculateVdW(Cube *cube);

    //! Calculate an MO cube
    void calculateMo(OpenQube::Cube *cube, int mo);

    //! Calculate electron density cube
    void calculateElectronDensity(OpenQube::Cube *cube);

    //! Calculate a mesh isosurface for the given cube
    void calculateMesh(Cube *cube, double isoValue);

    /**
     * Figure out which kind of calculation is required. The relevant m_qube
     * or m_cube will be set to the cube being calculated.
     * @param type the type of cube to be calculated.
     * @param mo the MO number, default of -1 is invalid/ignored.
     * @param calculateCube true if the cube needs to be calculated.
     */
    void startCubeCalculation(Cube::Type type, int mo,
                              bool &calculateCube);

  private slots:
    /**
     * New calculate function - queries the dialog and calculates the cubes and
     * the meshes. This is much more demand based.
     */
    void calculate();

    /**
     * This is called once the calculation is complete - check for more
     * calculations, clean up once complete.
     */
    void calculateDone();

    /**
     * This is called once the calculation is canceled - clean up.
     */
    void calculateCanceled();

  };

  class SurfaceExtensionFactory : public QObject, public PluginFactory
  {
      Q_OBJECT
      Q_INTERFACES(Avogadro::PluginFactory)
      AVOGADRO_EXTENSION_FACTORY(SurfaceExtension)
  };

} // End namespace Avogadro

#endif
