//
// surf.h
//
// Copyright (C) 1996 Limit Point Systems, Inc.
//
// Author: Curtis Janssen <cljanss@limitpt.com>
// Maintainer: LPS
//
// This file is part of the SC Toolkit.
//
// The SC Toolkit is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// The SC Toolkit 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 Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with the SC Toolkit; see the file COPYING.LIB.  If not, write to
// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
// The U.S. Government is granted a limited license as per AL 91-7.
//

#ifndef _math_isosurf_surf_h
#define _math_isosurf_surf_h

#ifdef __GNUC__
#pragma interface
#endif

#ifdef HAVE_CONFIG_H
#include <scconfig.h>
#endif

#include <map>
#include <set>
#include <vector>

#include <math/isosurf/triangle.h>
#include <math/isosurf/volume.h>
#include <util/render/render.h>

namespace sc {

template <class C, class I>
inline void
erase_elements_by_value(C &container, I begin, I end)
{
  for (I i=begin; i!=end; i++) {
      container.erase(*i);
    }
}

class TriangulatedSurface: public DescribedClass {
  protected:
    int _verbose;
    int _debug;

    int _completed_surface;

    // sets of objects that make up the surface
    std::set<Ref<Vertex> > _vertices;
    std::set<Ref<Edge> > _edges;
    std::set<Ref<Triangle> > _triangles;

    // map objects to an integer index
    std::map<Ref<Vertex>,int> _vertex_to_index;
    std::map<Ref<Edge>,int> _edge_to_index;
    std::map<Ref<Triangle>,int> _triangle_to_index;

    // map integer indices to an object
    std::vector<Ref<Vertex> > _index_to_vertex;
    std::vector<Ref<Edge> > _index_to_edge;
    std::vector<Ref<Triangle> > _index_to_triangle;

    // mappings between array element numbers
    int** _triangle_vertex;
    int** _triangle_edge;
    int** _edge_vertex;

    // values for each of the vertices
    int _have_values;
    std::vector<double> _values;

    // what to use to integrate over the surface, by default
    Ref<TriangleIntegrator> _integrator;
    // other integrators, in terms of time & accuracy:
    // _fast_integrator <= _integrator <= _accurate_interator
    Ref<TriangleIntegrator> _fast_integrator;
    Ref<TriangleIntegrator> _accurate_integrator;

    void clear_int_arrays();

    void complete_ref_arrays();
    void complete_int_arrays();

    void recompute_index_maps();

    void add_triangle(const Ref<Triangle>&);
    void add_vertex(const Ref<Vertex>&);
    void add_edge(const Ref<Edge>&);

    // these members must be used to allocate new triangles and edges
    // since specializations of TriangulatedSurface might need to
    // override these to produce triangles and edges with interpolation
    // data.
    virtual Triangle* newTriangle(const Ref<Edge>&,
                                  const Ref<Edge>&,
                                  const Ref<Edge>&,
                                  int orientation) const;
    virtual Edge* newEdge(const Ref<Vertex>&,const Ref<Vertex>&) const;

    // this map of edges to vertices is used to construct the surface
    std::map<Ref<Vertex>,std::set<Ref<Edge> > > _tmp_edges;
  public:
    TriangulatedSurface();
    TriangulatedSurface(const Ref<KeyVal>&);
    virtual ~TriangulatedSurface();

    // control printing
    int verbose() const { return _verbose; }
    void verbose(int v) { _verbose = v; }

    // set up an integrator
    void set_integrator(const Ref<TriangleIntegrator>&);
    void set_fast_integrator(const Ref<TriangleIntegrator>&);
    void set_accurate_integrator(const Ref<TriangleIntegrator>&);
    virtual Ref<TriangleIntegrator> integrator(int itri);
    virtual Ref<TriangleIntegrator> fast_integrator(int itri);
    virtual Ref<TriangleIntegrator> accurate_integrator(int itri);

    // construct the surface
    void add_triangle(const Ref<Vertex>&,
                      const Ref<Vertex>&,
                      const Ref<Vertex>&);
    Ref<Edge> find_edge(const Ref<Vertex>&, const Ref<Vertex>&);
    virtual void complete_surface();

    // clean up the surface
    virtual void remove_short_edges(double cutoff_length = 1.0e-6,
                                    const Ref<Volume> &vol=0, double isoval=0.0);
    virtual void remove_slender_triangles(
                                    int remove_slender, double height_cutoff,
                                    int remove_small, double area_cutoff,
                                    const Ref<Volume> &vol=0, double isoval=0.0);
    virtual void fix_orientation();
    virtual void clear();

    // get information from the object sets
    int nvertex() const { return _vertices.size(); };
    Ref<Vertex> vertex(int i) const { return _index_to_vertex[i]; };
    int vertex_index(const Ref<Vertex> &o) {
      std::map<Ref<Vertex>,int>::iterator i = _vertex_to_index.find(o);
      if (i != _vertex_to_index.end()) return i->second;
      return -1;
    }
    int nedge() const { return _edges.size(); };
    Ref<Edge> edge(int i) const { return _index_to_edge[i]; };
    int edge_index(const Ref<Edge> &o) {
      std::map<Ref<Edge>,int>::iterator i = _edge_to_index.find(o);
      if (i != _edge_to_index.end()) return i->second;
      return -1;
    }
    int ntriangle() const { return _triangles.size(); };
    Ref<Triangle> triangle(int i) const { return _index_to_triangle[i]; }
    int triangle_index(const Ref<Triangle> &o) {
      std::map<Ref<Triangle>,int>::iterator i = _triangle_to_index.find(o);
      if (i != _triangle_to_index.end()) return i->second;
      return -1;
    }

    // information from the index mappings
    int triangle_vertex(int i,int j) const { return _triangle_vertex[i][j]; };
    int triangle_edge(int i,int j) const { return _triangle_edge[i][j]; };
    int edge_vertex(int i,int j) const { return _edge_vertex[i][j]; };

    // associate values with vertices
    //void compute_colors(Volume&);
    void compute_values(Ref<Volume>&);

    // properties of the surface
    virtual double flat_area(); // use flat triangles
    virtual double flat_volume(); // use flat triangles
    virtual double area();
    virtual double volume();

    // output of the surface
    virtual void print(std::ostream&o=ExEnv::out0()) const;
    virtual void print_vertices_and_triangles(std::ostream&o=ExEnv::out0()) const;
    virtual void print_geomview_format(std::ostream&o=ExEnv::out0()) const;
    virtual void render(const Ref<Render> &render);

    // print information about the topology
    void topology_info(std::ostream&o=ExEnv::out0());
    void topology_info(int nvertex, int nedge, int ntri, std::ostream&o=ExEnv::out0());
};


class TriangulatedSurfaceIntegrator {
  private:
    Ref<TriangulatedSurface> _ts;
    int _itri;
    int _irs;
    double _r;
    double _s;
    double _weight;
    double _surface_element;
    Ref<Vertex> _current;
    SCVector3 _dA;
    Ref<TriangleIntegrator> (TriangulatedSurface::*_integrator)(int itri);
    Ref<MessageGrp> _grp;
  public:
    TriangulatedSurfaceIntegrator();
    // the surface cannot be changed until this is destroyed
    TriangulatedSurfaceIntegrator(const Ref<TriangulatedSurface>&);
    ~TriangulatedSurfaceIntegrator();
    // Objects initialized by these operators are not automatically
    // updated.  This must be done with the update member.
    // The _grp is not copied.
    void operator = (const TriangulatedSurfaceIntegrator&);
    TriangulatedSurfaceIntegrator(const TriangulatedSurfaceIntegrator&i) {
        operator = (i);
      }
    // Return the number of integration points.
    int n();
    // Assign the surface.  Don't do this while iterating.
    void set_surface(const Ref<TriangulatedSurface>&);
    // returns the number of the vertex in the current triangle
    int vertex_number(int i);
    inline double r() const { return _r; }
    inline double s() const { return _s; }
    inline double w() const { return _weight*_surface_element; }
    double surface_element() const { return _surface_element; }
    double weight() const { return _weight; }
    const SCVector3& dA() const { return _dA; }
    Ref<Vertex> current();
    // Tests to see if this point is valid, if it is then
    // _r, _s, etc are computed and 1 is returned.
    int update();
    // This can be used to loop through unique pairs of points.
    // The argument should be a TriangulatedSurfaceIntegrator for
    // the same surface as this.
    int operator < (TriangulatedSurfaceIntegrator&i) {
        update();
        return _itri<i._itri?1:(_itri>i._itri?0:(_irs<i._irs?1:0));
      }
    // Goes to the next point.  Does not update.
    void operator++();
    inline void operator++(int) { operator++(); }
    // setting TSI = i sets TSI to begin at the triangle i
    int operator = (int);
    int itri() const { return _itri; }
    int irs() const { return _irs; }
    // the number of points in the current triangle
    int n_in_tri() const { return (_ts.pointer()->*_integrator)(_itri)->n(); }
    void distribute(const Ref<MessageGrp> &);
    void use_fast_integrator();
    void use_accurate_integrator();
    void use_default_integrator();
};

class TriangulatedImplicitSurface: public TriangulatedSurface {
  private:
    // The surface is defined as an isosurface of the volume vol_.
    Ref<Volume> vol_;
    double isovalue_;

    int fix_orientation_;
    int remove_short_edges_;
    double short_edge_factor_;
    int remove_slender_triangles_;
    double slender_triangle_factor_;
    int remove_small_triangles_;
    double small_triangle_factor_;
    double resolution_;

    int order_;

    int inited_;
  public:
    TriangulatedImplicitSurface(const Ref<KeyVal>&);
    ~TriangulatedImplicitSurface();

    Ref<Volume> volume_object() const { return vol_; }
    double isovalue() const { return isovalue_; }

    void init();
    int inited() const { return inited_; }
};

}

#endif

// Local Variables:
// mode: c++
// c-file-style: "CLJ"
// End:
