/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2016                                           \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program 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.                                       *
*                                                                           *
* This program 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/
/****************************************************************************
  History



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

#ifndef __VCG_TETRA_POS
#define __VCG_TETRA_POS

#include <vector>
#include <algorithm>

#include "component.h"

namespace vcg {
namespace tetra {

/** \addtogroup tetra */
/*@{*/

/**  Class VTIterator.
 This is a vertex - tetrahedron iterator
                @param MTTYPE (Template Parameter) Specifies the type of the tetrahedron.
 */
template < class MTTYPE> 
class VTIterator
{
public:
    /// The tetrahedron type
    typedef  MTTYPE TetraType;
    typedef typename TetraType::VertexType VertexType;
private:
    /// Pointer to a tetrahedron
    TetraType *_vt;
    /// Index of one vertex
    int _vi;
    /// Default Constructor
public:
    VTIterator() : _vt(0), _vi(-1){}
    /// Constructor
    VTIterator(TetraType  * const tp, int const zp)
    {
        _vt=tp->V(zp)->VTp();
        _vi=tp->V(zp)->VTi();
    }
    VTIterator(VertexType * const vp)
    {
        _vt = vp->VTp();
        _vi = vp->VTi();
    }

    ~VTIterator(){};

    /// Return the tetrahedron stored in the half edge
    inline TetraType* & Vt()
    {
        return _vt;
    }

    /// Return the index of vertex as seen from the tetrahedron
    inline int & Vi()
    {
        return _vi;
    }

    /// Return the index of vertex as seen from the tetrahedron
    inline const int & Vi() const
    {
        return _vi;
    }

	inline bool End(){return (Vt()==nullptr);}

    /// move on the next tetrahedron that share the vertex
    void operator++()
    {
        int vi=Vi();
        TetraType * tw = Vt();
        Vt() = tw->VTp(vi);
        Vi() = tw->VTi(vi);

		assert((Vt()==nullptr)||((tw->V(vi))==(Vt()->V(Vi()))));
    }

};

template <class TetraType>
void VTStarVT( typename TetraType::VertexType* vp,
               std::vector<TetraType *> &tetraVec,
               std::vector<int> &indexes)
{
    tetraVec.clear();
    indexes.clear();
    tetraVec.reserve(16);
    indexes.reserve(16);
    tetra::VTIterator<TetraType> vti(vp);
    while(!vti.End())
    {
        tetraVec.push_back(vti.Vt());
        indexes.push_back(vti.Vi());
        ++vti;
    }
}
template <class TetraType>
void VVStarVT( typename TetraType::VertexPointer vp, std::vector<typename TetraType::VertexPointer> & starVec)
{
    typedef typename TetraType::VertexPointer VertexPointer;

    starVec.clear();
    starVec.reserve(16);
	VTIterator<TetraType> vti(vp);

    while (!vti.End())
    {
        starVec.push_back(vti.Vt()->V1(vti.Vi()));
        starVec.push_back(vti.Vt()->V2(vti.Vi()));
        starVec.push_back(vti.Vt()->V3(vti.Vi()));
        ++vti;
    }

    std::sort(starVec.begin(), starVec.end());
    typename std::vector<VertexPointer>::iterator new_end = std::unique(starVec.begin(),starVec.end());
    starVec.resize(new_end - starVec.begin());
}


/**  Templated over the class tetrahedron, it stores a \em position over a tetrahedron in a mesh.
        It contain a pointer to the current tetrahedron,
        the index of one face,edge and a edge's incident vertex.
 */
template < class MTTYPE> 
class Pos
{
public:

    /// The tetrahedron type
    typedef MTTYPE TetraType;
    /// The vertex type
    typedef	typename TetraType::VertexType VertexType;
    /// The coordinate type
    typedef	typename TetraType::VertexType::CoordType CoordType;
    ///The HEdgePos type
    typedef Pos<TetraType> BasePosType;

private:
    /// Pointer to the tetrahedron of the half-edge
    TetraType *_t;
    /// Index of the face
    char _f;
    /// Index of the edge
    char _e;
    /// Pointer to the vertex
    char _v;

public:
    /// Default constructor
    Pos(){SetNull();};
    /// Constructor which associates the half-edge elementet with a face, its edge and its vertex
    Pos(TetraType * const tp, char const fap,char const ep,
        char const vp){_t=tp;_f=fap;_e=ep;_v=vp;}

    ~Pos(){};

    /// Return the tetrahedron stored in the half edge
    inline TetraType* & T()
    {
        return _t;
    }

    /// Return the tetrahedron stored in the half edge
    inline  TetraType* const & T() const
    {
        return _t;
    }

    /// Return the index of face as seen from the tetrahedron
    inline char & F()
    {
        return _f;
    }

    /// Return the index of face as seen from the tetrahedron
    inline const char & F() const
    {
        return _f;
    }

    /// Return the index of face as seen from the tetrahedron
    inline char & E()
    {
        return _e;
    }

    /// Return the index of edge as seen from the tetrahedron
    inline const char & E() const
    {
        return _e;
    }

    /// Return the index of vertex as seen from the tetrahedron
    inline char & V()
    {
        return _v;
    }

    /// Return the index of vertex as seen from the tetrahedron
    inline const char & V() const
    {
        return _v;
    }

    /// Operator to compare two half-edge
    inline bool operator == ( BasePosType const & p ) const {
        return (T()==p.T() && F()==p.F() && E()==p.E() && V()==p.V());
    }

    /// Operator to compare two half-edge
    inline bool operator != ( BasePosType const & p ) const {
        return (!((*this)==p));
    }

    /// Set to null the half-edge
    void SetNull(){
        T()=0;
        F()=-1;
        E()=-1;
        V()=-1;
    }

    /// Check if the half-edge is null
    bool IsNull() const {
        return ((T()==0) || (F()<0) || (E()<0) || (V()<0));
    }


    /// Changes edge maintaining the same face and the same vertex
    void FlipE()
    {

        //take the absolute index of the tree edges of the faces
        char e0=vcg::Tetra::EofF(_f ,0);
        char e1=vcg::Tetra::EofF(_f ,1);
        char e2=vcg::Tetra::EofF(_f ,2);
        //eliminate the same as himself
        if (e0==E())
        {
            e0=e1;
            e1=e2;
        }
        else
            if (e1==E())
            {
                e1=e2;
            }

        //now choose the one that preserve the same vertex
        if ((vcg::Tetra::VofE(e1,0)==V())||(vcg::Tetra::VofE(e1,1)==V()))
            E()=e1;
        else
            E()=e0;
    }


    /// Changes vertex maintaining the same face and the same edge
    void FlipV()
    {
        // in the same edge choose the one that change
        char v0=vcg::Tetra::VofE(E(),0);
        char v1=vcg::Tetra::VofE(E(),1);
        if (v0!=V())
            V()=v0;
        else
            V()=v1;
    }

    /// Changes face maintaining the same vertex and the same edge
    void FlipF()
    {
        char f0=vcg::Tetra::FofE(E(),0);
        char f1=vcg::Tetra::FofE(E(),1);
        if (f0!=F())
            F()=f0;
        else
            F()=f1;
    }

    /// Changes tetrahedron maintaining the same face edge and vertex'... to finish
    void FlipT()
    {

        //save the two vertices of the old edge
        VertexType *v0=T()->V(vcg::Tetra::VofE(E(),0));
        VertexType *v1=T()->V(vcg::Tetra::VofE(E(),1));

        //get the current vertex
        VertexType *vcurr=T()->V(V());

        //get new tetrahedron according to tetra to tetra topology
        TetraType *nt=T()->TTp(F());
        char nfa=T()->TTi(F());
        if (nfa!=-1)
        {
            //find the right edge
            char ne0=vcg::Tetra::EofF(nfa,0);
            char ne1=vcg::Tetra::EofF(nfa,1);
            char ne2=vcg::Tetra::EofF(nfa,2);

            //the vertices of new edges
            VertexType *vn0=nt->V(vcg::Tetra::VofE(ne0,0));
            VertexType *vn1=nt->V(vcg::Tetra::VofE(ne0,1));
            //verify that the two vertices of tetrahedron are identical
            if (((vn0==v0)&&(vn1==v1))||((vn1==v0)&&(vn0==v1)))
                E()=ne0;
            else
            {
                vn0=nt->V(vcg::Tetra::VofE(ne1,0));
                vn1=nt->V(vcg::Tetra::VofE(ne1,1));
                if (((vn0==v0)&&(vn1==v1))||((vn1==v0)&&(vn0==v1)))
                    E()=ne1;
                else
                {
#ifdef _DEBUG
                    vn0=nt->V(vcg::Tetra::VofE(ne2,0));
                    vn1=nt->V(vcg::Tetra::VofE(ne2,1));
                    assert(((vn0==v0)&&(vn1==v1))||((vn1==v0)&&(vn0==v1)));
#endif
                    E()=ne2;
                }
            }

            //find the right vertex
            vn0=nt->V(vcg::Tetra::VofE(E(),0));
#ifdef _DEBUG
            vn1=nt->V(vcg::Tetra::VofE(E(),1));
            assert((vn0==vcurr)||(vn1==vcurr));
#endif
            if (vn0==vcurr)
                V()=vcg::Tetra::VofE(E(),0);
            else
                V()=vcg::Tetra::VofE(E(),1);

            T()=nt;
            assert(T()->V(V())==vcurr);
            F()=nfa;
        }
    }

    ///returns the next half edge on the same edge
    void NextT( )
    {
#ifdef _DEBUG
        VertexType *vold=T()->V(V());
#endif
        FlipT();
        FlipF();
#ifdef _DEBUG
        VertexType *vnew=T()->V(V());
        assert(vold==vnew);
#endif
    }

    void Assert()
#ifdef _DEBUG
    {
        auto ht=*this;
        ht.FlipT();
        ht.FlipT();
        assert(ht==*this);

        ht=*this;
        ht.FlipF();
        ht.FlipF();
        assert(ht==*this);

        ht=*this;
        ht.FlipE();
        ht.FlipE();
        assert(ht==*this);

        ht=*this;
        ht.FlipV();
        ht.FlipV();
        assert(ht==*this);
    }
#else
    {}
#endif
};

///this pos structure jump on  next tetrahedron if find an external face
template < class MTTYPE> 
class PosJump:public Pos<MTTYPE>
{
private:
    MTTYPE *_t_initial;
    short int _back;
public :
    typedef  MTTYPE  TetraType;
    PosJump(const TetraType*  tp,const int  fap,const int  ep,
            int  vp){this->T()=tp;this->F()=fap;this->E()=ep;this->V()=vp;_t_initial=tp;_back=0;}

    void NextT()
    {
#ifdef _DEBUG
        int cont=0;
#endif
        MTTYPE *tpred=this->T();
        Pos<MTTYPE>::NextT();
        //external face
        if (tpred==this->T())
        {
            while (this->T()!=_t_initial)
            {
                Pos<MTTYPE>::NextT();
#ifdef _DEBUG
                cont++;
                assert (cont<500);
#endif
            }
            _back++;
            if (_back==1)
            {
                Pos<MTTYPE>::NextT();
            }
        }
    }
};

///this pos structure jump on  next tetrahedron in rotational sense if find an external face
template < class MTTYPE> 
class PosLoop:public Pos<MTTYPE>
{
private:
    MTTYPE *_t_initial;
    bool _jump;
    bool _loop;
public :
    typedef  MTTYPE  TetraType;
    PosLoop(TetraType*  tp,const int  fap,const int  ep,
            int vp){this->T()=tp;this->F()=fap;this->E()=ep;this->V()=vp;_t_initial=tp;_jump=false;_loop=false;}

    bool LoopEnd()
    {
        return (_loop);
    }

    bool Jump()
    {
        return(_jump);
    }

    void Reset()
    {
        _loop=false;
        _jump=false;
    }

    void NextT()
    {
#ifdef _DEBUG
        TetraType *t_old=this->T();
#endif
        TetraType *tpred=this->T();
        Pos<TetraType>::NextT();
        _loop=false;
        _jump=false;

        //external face
        if (tpred==this->T())
        {
            tpred=this->T();
            //jump next one
            Pos<TetraType>::NextT();
            //find the next external face
            while (tpred!=this->T())
            {
                tpred=this->T();
                Pos<TetraType>::NextT();
            }
            ////reset right rotation sense
            //  Pos<TetraType>::NextT();
            _jump=true;
        }
        if (this->T()==_t_initial)
            _loop=true;
#ifdef _DEBUG
        if (_loop==false)
            assert(t_old!=this->T());
#endif
    }

};
//@}
}//end namespace tetra
}//end namespace vcg

#endif
