/**
 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
 * storing and accessing finite element mesh data.
 * 
 * Copyright 2004 Sandia Corporation.  Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
 * retains certain rights in this software.
 * 
 * This library 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.
 * 
 */

#ifndef SCD_VERTEX_DATA_HPP
#define SCD_VERTEX_DATA_HPP

//
// Class: ScdVertexData
//
// Purpose: represent a rectangular vertex block of mesh
//
// A ScdVertex represents a rectangular vertex block of mesh, including both vertices and
// the parametric space used to address those vertices.

#include "SequenceData.hpp"
#include "moab/HomXform.hpp"

namespace moab {

class ScdVertexData : public SequenceData
{

private:

    //! parameter min/max, in homogeneous coords ijkh (extra row for stride eventually)
  HomCoord vertexParams[3];

    //! difference between max and min params plus one (i.e. # VERTICES in
    //! each parametric direction)
  int dIJK[3];
  
    //! difference between max and min params (i.e. # VERTEXS in
    //! each parametric direction)
  int dIJKm1[3];

public:

    //! constructor
  ScdVertexData(const EntityHandle start_vertex, 
                const int imin, const int jmin, const int kmin,
                const int imax, const int jmax, const int kmax) ;
  
  virtual ~ScdVertexData() {}

    //! get handle of vertex at i, j, k
  EntityHandle get_vertex(const int i, const int j, const int k) const;

    //! get handle of vertex at homogeneous coordinates
  EntityHandle get_vertex(const HomCoord &coords) const;

    //! get the parameters of a given handle; return MB_FAILURE if vhandle not in this
    //! sequence
  ErrorCode get_params(const EntityHandle vhandle,
                          int &i, int &j, int &k) const;
  
    //! get min params for this vertex
  void min_params(int &i, int &j, int &k) const;
  
    //! get max params for this vertex
  void max_params(int &i, int &j, int &k) const;

    //! get the min params
  const HomCoord &min_params() const;

    //! get the max params
  const HomCoord &max_params() const;
  
    //! get the number of vertices in each direction, inclusive
  void param_extents(int &di, int &dj, int &dk) const;

    //! convenience functions for parameter extents
  int i_min() const {return vertexParams[0].hom_coord()[0];}
  int j_min() const {return vertexParams[0].hom_coord()[1];}
  int k_min() const {return vertexParams[0].hom_coord()[2];}
  int i_max() const {return vertexParams[1].hom_coord()[0];}
  int j_max() const {return vertexParams[1].hom_coord()[1];}
  int k_max() const {return vertexParams[1].hom_coord()[2];}

    //! return whether this vseq's parameter space contains these parameters
  bool contains(const HomCoord &coords) const;
  bool contains(const int i, const int j, const int k) const;

  SequenceData* subset( EntityHandle start, 
                        EntityHandle end,
                        const int* sequence_data_sizes,
                        const int* tag_data_sizes ) const;
};

inline EntityHandle ScdVertexData::get_vertex(const int i, const int j, 
                                                const int k) const
{
  return start_handle() + (i-i_min()) + (j-j_min())*dIJK[0] + 
    (k-k_min())*dIJK[0]*dIJK[1];
}

inline EntityHandle ScdVertexData::get_vertex(const HomCoord &coords) const
{
  return get_vertex(coords.hom_coord()[0], coords.hom_coord()[1], coords.hom_coord()[2]);
}

inline ErrorCode ScdVertexData::get_params(const EntityHandle vhandle,
                                             int &i, int &j, int &k) const
{
  if (TYPE_FROM_HANDLE(vhandle) != MBVERTEX) return MB_FAILURE;

  int hdiff = vhandle - start_handle();

  k = hdiff / (dIJK[0]*dIJK[1]);
  j = (hdiff - (k*dIJK[0]*dIJK[1])) / dIJK[0];
  i = hdiff % dIJK[0];

  k += vertexParams[0].k();
  j += vertexParams[0].j();
  i += vertexParams[0].i();

  return (vhandle >= start_handle() &&
          i >= i_min() && i <= i_max() &&
          j >= j_min() && j <= j_max() &&
          k >= k_min() && k <= k_max()) ? MB_SUCCESS : MB_FAILURE;
}
  
  //! get min params for this vertex
inline void ScdVertexData::min_params(int &i, int &j, int &k) const
{
  i = i_min();
  j = j_min();
  k = k_min();
}

//! get max params for this vertex
inline void ScdVertexData::max_params(int &i, int &j, int &k) const
{
  i = i_max();
  j = j_max();
  k = k_max();
}

inline const HomCoord &ScdVertexData::min_params() const 
{
  return vertexParams[0];
}

inline const HomCoord &ScdVertexData::max_params() const 
{
  return vertexParams[1];
}

  //! get the number of vertices in each direction, inclusive
inline void ScdVertexData::param_extents(int &di, int &dj, int &dk) const
{
  di = dIJK[0];
  dj = dIJK[1];
  dk = dIJK[2];
}

inline bool ScdVertexData::contains(const HomCoord &coords) const
{
  return (coords >= vertexParams[0] && coords <= vertexParams[1]) ? true : false;
}

inline bool ScdVertexData::contains(const int i, const int j, const int k) const
{
  return contains(HomCoord(i, j, k));
}
  
} // namespace moab

#endif
