/**********************************************************************
  CartoonMeshGenerator - Class to generate meshes for biomolecules

  Code taken from Zodiac: www.zeden.org
   
   Nicola Zonta
   nicola.zonta@zeden.org

   Ian J. Grimstead
   I.J.Grimstead@cs.cardiff.ac.uk

   Andrea Brancale
   brancalea@cf.ac.uk
 
  Copyright (C) 2009 by Tim Vandermeersch

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

  Avogadro is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 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 CARTOONMESHGENERATOR_H
#define CARTOONMESHGENERATOR_H

#include <avogadro/global.h>
#include <avogadro/color3f.h>

#include <Eigen/Core>

#include <QThread>


#include <vector>

namespace Avogadro {

  class Protein;
  class Molecule;
  class Residue;
  class Atom;
  class Mesh;

  class CartoonMeshGenerator : public QThread
  {
  public:
    /**
     * Constructor.
     */
    explicit CartoonMeshGenerator(QObject *parent = 0);

    /**
     * Constructor. Can be used to initialize the MeshGenerator.
     * @param cube The source Cube with the volumetric data.
     * @param mesh The Mesh that will hold the isosurface.
     * @param iso The iso value of the surface.
     * @return True if the MeshGenerator was successfully initialized.
     */
    CartoonMeshGenerator(const Molecule *molecule, Mesh *mesh, QObject *parent = 0);

    /**
     * Destructor.
     */
    ~CartoonMeshGenerator();

    /**
     * Initialization function, set up the MeshGenerator ready to find an
     * isosurface of the supplied Cube.
     * @param cube The source Cube with the volumetric data.
     * @param mesh The Mesh that will hold the isosurface.
     * @param iso The iso value of the surface.
     */
    bool initialize(const Molecule *molecule, Mesh *mesh);
    
    
    void setQuality( int quality)
    {
      m_quality = quality;
    }
    void setHelixColor(const Color3f &color)
    {
      m_helixColor = color;
    }
    void setSheetColor(const Color3f &color)
    {
      m_sheetColor = color;
    }
    void setLoopColor(const Color3f &color)
    {
      m_loopColor = color;
    }
    void setHelixABC(double a, double b, double c)
    {
      m_aHelix = a;
      m_bHelix = b;
      m_cHelix = c;
    }
    void setSheetABC(double a, double b, double c)
    {
      m_aSheet = a;
      m_bSheet = b;
      m_cSheet = c;
    }
    void setLoopABC(double a, double b, double c)
    {
      m_aLoop = a;
      m_bLoop = b;
      m_cLoop = c;
    }

    /**
     * Use this function to begin Mesh generation. Uses an asynchronous thread,
     * and so avoids locking the user interface while the isosurface is found.
     */
    void run();

    /**
     * @return The Mesh being generated by the class.
     */
    Mesh * mesh() const { return m_mesh; }

    /**
     * Clears the contents of the MeshGenerator.
     */
    void clear();

  protected:
    void setBackbonePoints(Residue *residue, const std::vector<Eigen::Vector3f> &points);
    const std::vector<Eigen::Vector3f>& backbonePoints(Residue *residue) const;
    
    void setBackboneDirection(Residue *residue, const Eigen::Vector3f &direction);
    const Eigen::Vector3f& backboneDirection(Residue *residue) const;
    
    Residue* previousResidue(Residue *residue, const QVector<Residue*> &chain) const;
    Residue* nextResidue(Residue *residue, const QVector<Residue*> &chain) const;
    Atom* atomFromResidue(Residue *residue, const QString &atomID);
    
    const Color3f& color(Residue *residue) const;
    
    void findBackboneData();
    void findBackbonePoints(Residue *residue, const QVector<Residue*> &chain);
    void findBackboneDirection(Residue *residue);
    Eigen::Vector3f startReference(Residue *residue);
    Eigen::Vector3f endReference(Residue *residue);
    void addGuidePointsToBackbone(Residue *residue, const QVector<Residue*> &chain,
        std::vector<Eigen::Vector3f> &lis);
    std::vector<Eigen::Vector3f> smoothList(const std::vector<Eigen::Vector3f> &lis);
    Eigen::Vector3f circumcenter(const Eigen::Vector3f &v1,
        const Eigen::Vector3f &v2, const Eigen::Vector3f v3);
    void interpolate(const Eigen::Vector3f &v1, const Eigen::Vector3f &v2, const Eigen::Vector3f &v3,
        Eigen::Vector3f &i1, Eigen::Vector3f &i2);
    Color3f mixColors(const Color3f &c1, const Color3f &c2);
    void drawBackboneStick(Residue *residue, const QVector<Residue*> &chain);
    void components(const Eigen::Vector3f &vec, const Eigen::Vector3f &ref,
        Eigen::Vector3f &parallel, Eigen::Vector3f &normal);
    void backboneRibbon(const Eigen::Vector3f &v1, const Eigen::Vector3f &v2,
        const Eigen::Vector3f &v3, const Eigen::Vector3f &v4, const Eigen::Vector3f &dir,
        const Eigen::Vector3f &dir2, const Color3f &c1, const Color3f &c2,
        const std::vector<Eigen::Vector3f> &shape1, const std::vector<Eigen::Vector3f> &shape2);
    
    
    Molecule *m_molecule;
    Mesh *m_mesh;
    Protein *m_protein;
    std::vector<std::vector<Eigen::Vector3f> > m_backbonePoints;
    std::vector<Eigen::Vector3f> m_backboneDirections;

    Color3f m_helixColor;
    Color3f m_sheetColor;
    Color3f m_loopColor;

    // mesh
    std::vector<Eigen::Vector3f> m_vertices;
    std::vector<Eigen::Vector3f> m_normals;
    std::vector<Color3f> m_colors;

    int m_quality;
    double m_aHelix, m_bHelix, m_cHelix;
    double m_aSheet, m_bSheet, m_cSheet;
    double m_aLoop, m_bLoop, m_cLoop;


  };

} // End namespace Avogadro

#endif

