////////////////////////////////////////////////////////////////////////////////
/** 
  \file
  \brief \ru Tриангуляция двумерной области.
         \en Triangulation of two-dimensional region. \~

*/
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// "PGrid.cpp"
// \ru PGrid.h - заголовочный файл триангуляции массива двумерных точек. \en PGrid.h - header file of two-dimensional point array triangulation. 
// \ru Содержит следующие разделы: \en Contains the following sections: 
// \ru - Триангуляция двумерной области \en - Triangulation of a two-dimensional region 
// \ru - Вершина полигона \en - Vertex of a polygon 
// \ru - Многоугольник \en - Polygon 
// \ru - Аппроксимация плоской области треугольными пластинами \en - Approximation of a planar region by triangular plates 
// \ru - Триангуляция двумерного региона \en - Triangulation of a two-dimensional region 
// \ru - Выпуклая триангуляция неупорядоченного массива двумерных точек \en - Convex triangulation of unordered array of two-dimensional points 
//
////////////////////////////////////////////////////////////////////////////////

#ifndef __MESH_PLANE_GRID_H
#define __MESH_PLANE_GRID_H

#include <reference_item.h>
#include <templ_s_array.h>
#include <templ_p_array.h>
#include <mesh_float_point.h>
#include <mb_rect.h>
#include <mb_cart_point.h>
#include <mesh_triangle.h>


class MATH_CLASS MbRegion;
class MATH_CLASS MbMatrix3D;
class MATH_CLASS ProgressBarWrapper;


//------------------------------------------------------------------------------
/** \brief \ru Tреугольник.
           \en Triangle. \~
  \details \ru Tреугольник определен, как тройка точек, заданных индексами 
           вершин триангуляции MbPlanarGrid. \n
           \en Triangle is defined as triple of points defined by indices 
           of vertices of MbPlanarGrid triangulation. \n \~
  \ingroup Algorithms_2D
*/
// ---
class MbTri {
protected :
  size_t pIndex[3];  // \ru Номера вершин треугольника в массиве точек \en Indices of triangle vertices in array of points 

public :
  MbTri() { pIndex[0] = pIndex[1] = pIndex[2] = SYS_MAX_T; }
  MbTri( size_t j0, size_t j1, size_t j2, bool orientation ) { Init( j0, j1, j2, orientation ); }
  ~MbTri() {}
private:
  MbTri( const MbTri & ); // \ru Не реализовано \en Not implemented 
  void operator = ( const MbTri & ); // \ru Не реализовано \en Not implemented 
public :

  void    Init( size_t j0, size_t j1, size_t j2, bool orientation )
  {
    if ( orientation ) { // \ru Совпадает направление обхода \en Traverse direction coincides 
      pIndex[0] = j0;
      pIndex[1] = j1;
      pIndex[2] = j2;
    }
    else {
      pIndex[1] = j1;
      pIndex[2] = j0;
      pIndex[0] = j2;
    }
  }

  bool    GetTriangle ( size_t & i0, size_t & i1, size_t & i2 ) const
  {
    i0 = pIndex[0];
    i1 = pIndex[1];
    i2 = pIndex[2];
    return true;
  }

  size_t  GetIndex( size_t n ) const { return pIndex[n % 3]; }

  bool    IsTriangleEdge( size_t k0, size_t k1, size_t & eInd ) const
  {
    eInd = SYS_MAX_T;

    if ( k0 == pIndex[0] && k1 == pIndex[1] )
      eInd = 0;
    else if ( k0 == pIndex[1] && k1 == pIndex[2] )
      eInd = 1;
    else if ( k0 == pIndex[2] && k1 == pIndex[0] )
      eInd = 2;

    return (eInd != SYS_MAX_T);
  }
}; // MbTri


//------------------------------------------------------------------------------
/** \brief \ru Tриангуляция двумерной области.
           \en Triangulation of a two-dimensional region. \~
  \details \ru Tриангуляция двумерной области. \n
           \en Triangulation of a two-dimensional region. \n \~ 
  \ingroup Algorithms_2D
*/
// ---
class MATH_CLASS MbPlanarGrid : public MbRefItem {
private:
  SArray<MbCartPoint> points;    ///< \ru Вершины триангуляции. \en Vertices of triangulation. 
  SArray<MbTri>       triangles; ///< \ru Множество треугольников. \en Array of triangles. 

public:
  MbPlanarGrid() : points( 0, 1 ), triangles( 0, 1 ) {}
 ~MbPlanarGrid() {};

public:
  void    PointsReserve       ( size_t size ) { points.Reserve( size );        }
  void    TrianglesReserve    ( size_t size ) { triangles.Reserve( size );     }
  void    PointsSetMaxDelta   ( uint16 size ) { points.SetMaxDelta( size );    }
  void    TrianglesSetMaxDelta( uint16 size ) { triangles.SetMaxDelta( size ); }
  void    PointsAdjust()                      { points.Adjust();               }
  void    TrianglesAdjust()                   { triangles.Adjust();            }

  size_t  GetPointsCount   () const { return points.Count();    }
  size_t  GetTrianglesCount() const { return triangles.Count(); }

  void    AddTriangle( size_t j0, size_t j1, size_t j2, bool o ) { triangles.Add()->Init( j0, j1, j2,o ); }
  void    AddPoint   ( const MbFloatPoint & p ) { points.Add( p ); }
  void    AddPoint   ( const MbCartPoint  & p ) { points.Add( p ); }
  /// \ru Выдать декартову точку. \en Get Cartesian point. 
  bool    GetPoint   ( size_t k, MbCartPoint  & p ) const; 
  /// \ru Выдать точку. \en Get point. 
  bool    GetPoint   ( size_t k, MbFloatPoint & p ) const;
  /// \ru Получить индексы точек треугольника. \en Get indices of points of the triangle. 
  bool    GetTriangle( size_t k, size_t & i0, size_t & i1, size_t & i2 ) const; 
  /// \ru Получить точки треугольника. \en Get points of the triangle. 
  bool    GetTrianglePoints( size_t k, MbCartPoint & p0, MbCartPoint & p1, MbCartPoint & p2 ) const; 

OBVIOUS_PRIVATE_COPY( MbPlanarGrid )
};


//------------------------------------------------------------------------------
/// \ru Выдать декартову точку \en Get Cartesian point 
// ---
inline bool MbPlanarGrid::GetPoint( size_t k, MbCartPoint & p ) const
{
  if ( k < points.Count() ) {
    p = points[k];
    return true;
  }
  return false;
}


//------------------------------------------------------------------------------
/// \ru Выдать точку \en Get point 
// ---
inline bool MbPlanarGrid::GetPoint( size_t k, MbFloatPoint & p ) const
{
  if ( k < points.Count() ) {
    p = points[k];
    return true;
  }
  return false;
}


//------------------------------------------------------------------------------
/// \ru Вершина полигона \en Vertex of a polygon 
// ---
class TriVertex {
private:
  TriVertex * next;  ///< \ru Следующая вершина \en Next vertex 
  TriVertex * prev;  ///< \ru Предыдущая вершина \en Previous vertex 
  size_t      index; ///< \ru Номер точки в массиве точек триангуляции \en Index of point in array of points of triangulation 

public:
  TriVertex( size_t i ) : index(i) { next = this; prev = this; }

public:
  ~TriVertex() {}

public:
  /// \ru Смежная вершина (следующая или предыдущая). \en Adjacent vertex (next or previous). 
  TriVertex *         Neighbor  ( MbeMoveType ) const; 
  /// \ru Следующая вершина. \en Next vertex. 
  TriVertex *         Next      () const { return next;  } // SKIP_SA
  /// \ru Предыдущая вершина. \en Previous vertex. 
  TriVertex *         Prev      () const { return prev;  }  // SKIP_SA
  /// \ru Номер точки текущей вершины. \en Index of point of current vertex. 
  size_t              Index     () const { return index; } 
  /// \ru Вставить вершину после текущей. \en Insert vertex after the current one. 
  TriVertex *         Insert    ( TriVertex &  ); 
  /// \ru Исключить текущую вершину (возвращает исключенную, текущей становится предыдущая) \en Exclude the current vertex (returns excluded vertex, previous vertex becomes the current one) 
  TriVertex *         Detach    (); 
  /// \ru Соединить две цепочки вершин. \en Connect two vertex chains. 
  void                Splice    ( TriVertex &  );
  /// \ru Разделить цепочку вершин вершиной. \en Disconnect vertex chain by a vertex. 
  TriVertex *         Split     ( TriVertex &  );

OBVIOUS_PRIVATE_COPY( TriVertex )
};


//------------------------------------------------------------------------------
/// \ru Многоугольник \en Polygon 
// ---
class TriPoly {
private:
  TriVertex * vertex; ///< \ru Текущая вершина. \en Current vertex. 
  intptr_t    size;   ///< \ru Количество вершин. \en Count of vertices. 

public:
  TriPoly()                   : vertex( nullptr ), size( 0 ) {}
  TriPoly( TriVertex * vert ) : vertex( vert ), size( 0 ) {  Resize(); }

public:
  ~TriPoly();

public:
  intptr_t    Size() const { return size; } ///< \ru Размер цепочки вершин \en Size of vertex chain 
  size_t      Index() const { return (vertex != nullptr) ?  vertex->Index() : SYS_MAX_T; } ///< \ru Индекс вершины \en Index of vertex

  TriVertex * This() const { return vertex; } ///< \ru Текущая вершина \en Current vertex 
  TriVertex * Next() const; ///< \ru Следующая вершина \en Next vertex 
  TriVertex * Prev() const; ///< \ru Предыдущая вершина \en Previous vertex 
  TriVertex * Neighbor( MbeMoveType rotation ) const; ///< \ru Соседняя вершина \en Neighboring vertex 

  TriVertex * Advance ( MbeMoveType rotation ); ///< \ru Перемещение указателя текущей вершины на соседа \en Move current vertex pointer to the neighbor 
  TriVertex * SetVertex( TriVertex * );         ///< \ru Перемещение указателя текущей вершины на указанную \en Move current vertex pointer to the given one 

  TriVertex * Insert( TriVertex & ); ///< \ru Вставка новой вершины \en Insert new vertex 
  void        Remove(); ///< \ru Удаление текущей вершины \en Remove the current vertex 

  TriPoly  *  Split( TriVertex &, bool createNew ); ///< \ru Резка многоугольника вдоль хорды \en Cutting of polygon along a chord 
  bool        GetTriangle( size_t & k0, size_t & k1, size_t & k2 ) const; ///< \ru Получить индексы вершин треугольника (когда вершин больше 2) \en Get indices of triangle vertices (when there are more than 2 vertices) 

  template <class Point, class Gab>
  bool        CalculateGab( const SArray<Point> & pnts, Gab & rect ) const;

  TriPoly *   DeleteDegeneratedBridge( MbPlanarGrid & grid ); ///< \ru Удалить вырожденный мостик от текущей вершины. \en Remove the degenerate bridge from the current vertex.

private:
  void        Resize(); ///< \ru Обновление размера \en Set size to zero 

OBVIOUS_PRIVATE_COPY( TriPoly )
};


//------------------------------------------------------------------------------
// \ru Посчитать габарит цепочки вершин \en Calculate bounding box of vertex chain 
// ---
template <class Point, class Gab>
bool TriPoly::CalculateGab( const SArray<Point> & pnts, Gab & rect ) const
{
  bool isDone = false;

  rect.SetEmpty();
  size_t pntsCnt = pnts.Count();

  if ( Size() > 0 && pntsCnt > 0 ) {
    const TriVertex * v0 = This();
    size_t ind0 = v0->Index();

    if ( ind0 < pntsCnt ) {
      isDone = true;
      const Point & pnt0 = pnts[ind0];
      rect |= pnt0;

      TriVertex * v = v0->Next();
      while( v0 != v && isDone ) {
        isDone = false;
        size_t ind = v->Index();
        if ( ind < pntsCnt ) {
          const Point & pnt = pnts[ind];
          rect |= pnt;
          v = v->Next();
          isDone = true;
        }
      }
    }
  }

  C3D_ASSERT( isDone );
  return isDone;
}


/*
//------------------------------------------------------------------------------
// \ru Треугольник со ссылками на смежные(соседние) с ним треугольники \en Triangle with references to triangles adjacent (neighboring) to it 
// ---
class MbLinkedTri : public MbTri {
protected:
  MbTri * neighbors[3]; // \ru Соседние треугольники \en Neighboring triangles 
  // \ru Нумерация соседних треугольников: \en Numeration of neighboring triangles: 
  // \ru 0 - смежный через ребро на вершинах 0,1 \en 0 - adjacent at edge with vertices 0,1 
  // \ru 1 - смежный через ребро на вершинах 1,2 \en 1 - adjacent at edge with vertices 1,2 
  // \ru 2 - смежный через ребра на вершинах 2,0 \en 2 - adjacent at edge with vertices 2,0 

public:
  MbLinkedTri() : MbTri() { neighbors[0] = neighbors[1] = neighbors[2] = nullptr; };
  ~MbLinkedTri() {};
public:
  MbTri * GetNeighbor( size_t n ) const { return neighbors[n % 3]; }
  void    SetNeighbor( size_t n, MbTri * neighbor ) { neighbors[n % 3] = neighbor; }
  bool    IsBoundary() const; // \ru Является ли треугольник граничным \en Whether the triangle is boundary 

OBVIOUS_PRIVATE_COPY( MbLinkedTri )
};


//------------------------------------------------------------------------------
// \ru Является ли треугольник граничным \en Whether the triangle is boundary 
// ---
inline bool MbLinkedTri::IsBoundary() const
{
  bool isBoundary = (neighbors[0] == nullptr) ||
                    (neighbors[1] == nullptr) ||
                    (neighbors[2] == nullptr);
  return isBoundary;
}
// */


//------------------------------------------------------------------------------
// \ru Аппроксимация плоской области треугольными пластинами \en Approximation of planar region by triangular plates 
// \ru Функция удаляет полигоны точек из массива \en The function removes polygons of points from the array 
// ---
MATH_FUNC (void) CalculatePlanarGrid( PArray< SArray<MbCartPoint> > & poly, MbPlanarGrid & grid );


//------------------------------------------------------------------------------
/** \brief \ru Триангуляция двумерного региона
           \en Triangulation of a two-dimensional region \~
  \details \ru Триангуляция двумерного региона.
    Регион region должен быть корректным (на некорректном работает неправильно)
           \en Triangulation of a two-dimensional region.
    'region' region has to be correct (improper handling of incorrect ones) \~
  \ingroup Polygonal_Objects
*/
// ---
MATH_FUNC (void) TriangulateRegion( const MbRegion & region, double sag, MbPlanarGrid & grid );


//------------------------------------------------------------------------------
/// \ru Выпуклая триангуляция неупорядоченного массива двумерных точек \en Convex triangulation of unordered array of two-dimensional points 
// ---
MATH_FUNC (bool) TriangulateConvexCloud( const SArray<MbCartPoint> & uvPnts, double xEps, double yEps, const MbMatrix3D & from,
                                         SArray<MbTri> & triangles,
                                         ProgressBarWrapper * progBar );


#endif // __PGRID_H
