//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#ifndef BOOST_SELF_AVOIDING_WALK_HPP
#define BOOST_SELF_AVOIDING_WALK_HPP

/*
  This file defines necessary components for SAW. 

  mesh language: (defined by myself to clearify what is what)
  A triangle in mesh is called an triangle.
  An edge in mesh is called an line. 
  A vertex in mesh is called a point.

  A triangular mesh corresponds to a graph in which a vertex is a
  triangle and an edge(u, v) stands for triangle u and triangle v 
  share an line.

  After this point, a vertex always refers to vertex in graph,
  therefore it is a traingle in mesh. 

 */

#include <utility>
#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/property_map.hpp>

#define SAW_SENTINAL -1

namespace boost {

  template <class T1, class T2, class T3>
  struct triple {
    T1 first;
    T2 second;
    T3 third;
    triple(const T1& a, const T2& b, const T3& c) : first(a), second(b), third(c) {}
    triple() : first(SAW_SENTINAL), second(SAW_SENTINAL), third(SAW_SENTINAL) {}
  };
 
  typedef triple<int, int, int> Triple;

  /* Define a vertex property which has a triangle inside. Triangle is
    represented by a triple.  */
  struct triangle_tag { enum { num = 100 }; };
  typedef property<triangle_tag,Triple> triangle_property;

  /* Define an edge property with a line. A line is represented by a
    pair.  This is not required for SAW though.
  */
  struct line_tag { enum { num = 101 }; };
  template <class T> struct line_property
    : public property<line_tag, std::pair<T,T> > { };

  /*Precondition: Points in a Triangle are in order */
  template <class Triangle, class Line>
  inline void get_sharing(const Triangle& a, const Triangle& b, Line& l)
  {
    l.first = SAW_SENTINAL;
    l.second = SAW_SENTINAL;

    if ( a.first == b.first ) {
      l.first = a.first;
      if ( a.second == b.second || a.second == b.third )
        l.second = a.second;
      else if ( a.third == b.second || a.third == b.third )
        l.second = a.third; 

    }  else if ( a.first == b.second ) {
      l.first = a.first;
      if ( a.second == b.third )
        l.second = a.second;
      else if ( a.third == b.third )
        l.second = a.third; 

    } else if ( a.first == b.third ) {
      l.first = a.first;


    } else if ( a.second == b.first ) {
      l.first = a.second;
      if ( a.third == b.second || a.third == b.third )
        l.second = a.third;

    } else if ( a.second == b.second ) {
      l.first = a.second;
      if ( a.third == b.third ) 
        l.second = a.third;

    } else if ( a.second == b.third ) {
      l.first = a.second;


    } else if ( a.third == b.first 
                || a.third == b.second  
                || a.third == b.third )
      l.first = a.third; 

    /*Make it in order*/
    if ( l.first > l.second ) {
      typename Line::first_type i = l.first;
      l.first = l.second;
      l.second = i;
    }

  }

  template <class TriangleDecorator, class Vertex, class Line>
  struct get_vertex_sharing {
    typedef std::pair<Vertex, Line> Pair;
    get_vertex_sharing(const TriangleDecorator& _td) : td(_td) {}
    inline Line operator()(const Vertex& u, const Vertex& v) const {
      Line l;
      get_sharing(td[u], td[v], l);
      return l;
    }
    inline Line operator()(const Pair& u, const Vertex& v) const {
      Line l;
      get_sharing(td[u.first], td[v], l);
      return l;
    }
    inline Line operator()(const Pair& u, const Pair& v) const {
      Line l;
      get_sharing(td[u.first], td[v.first], l);
      return l;
    }
    TriangleDecorator td;
  };
  
  /* HList has to be a handle of data holder so that pass-by-value is
   * in right logic.
   *
   * The element of HList is a pair of vertex and line. (remember a
   * line is a pair of two ints.). That indicates the walk w from
   * current vertex is across line. (If the first of line is -1, it is
   * a point though.
   */
  template < class TriangleDecorator, class HList, class IteratorD>
  class SAW_visitor
    : public bfs_visitor<>, public dfs_visitor<>
  {
    typedef typename boost::property_traits<IteratorD>::value_type iter;
    /*use boost shared_ptr*/
    typedef typename HList::element_type::value_type::second_type     Line;
  public:

    typedef tree_edge_tag category;

    inline SAW_visitor(TriangleDecorator _td, HList _hlist, IteratorD ia)
      : td(_td), hlist(_hlist), iter_d(ia) {}

    template <class Vertex, class Graph>
    inline void start_vertex(Vertex v, Graph&) {
      Line l1;
      l1.first = SAW_SENTINAL;
      l1.second = SAW_SENTINAL;
      hlist->push_front(std::make_pair(v, l1));
      iter_d[v] = hlist->begin();
    }

    /*Several symbols:
      w(i): i-th triangle in walk w
      w(i) |- w(i+1): w enter w(i+1) from w(i) over a line
      w(i) ~> w(i+1): w enter w(i+1) from w(i) over a point
      w(i) -> w(i+1): w enter w(i+1) from w(i)
      w(i) ^ w(i+1): the line or point w go over from w(i) to w(i+1)
    */
    template <class Edge, class Graph>
    bool tree_edge(Edge e, Graph& G) {
          using std::make_pair;
      typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
      Vertex tau = target(e, G);
      Vertex i   = source(e, G); 

      get_vertex_sharing<TriangleDecorator, Vertex, Line> get_sharing_line(td);
      
      Line tau_i = get_sharing_line(tau, i);
      
      iter w_end = hlist->end();

      iter w_i = iter_d[i];

      iter w_i_m_1 = w_i;
      iter w_i_p_1 = w_i;

      /*----------------------------------------------------------
       *             true             false
       *==========================================================
       *a       w(i-1) |- w(i)    w(i-1) ~> w(i) or w(i-1) is null
       *----------------------------------------------------------
       *b       w(i) |- w(i+1)    w(i) ~> w(i+1) or no w(i+1) yet
       *----------------------------------------------------------
       */
      
      bool a = false, b = false;

      --w_i_m_1;
      ++w_i_p_1;
      b = ( w_i->second.first != SAW_SENTINAL );

      if ( w_i_m_1 != w_end ) {
        a = ( w_i_m_1->second.first != SAW_SENTINAL );
      }      
      
      if ( a ) {
        
        if ( b ) {
          /*Case 1: 
            
            w(i-1) |- w(i) |- w(i+1)
          */
          Line l1 = get_sharing_line(*w_i_m_1, tau);

          iter w_i_m_2 = w_i_m_1;
          --w_i_m_2;

          bool c = true;
          
          if ( w_i_m_2 != w_end ) {
            c = w_i_m_2->second != l1;
          }

          if ( c ) {  /* w(i-1) ^ tau != w(i-2) ^ w(i-1)  */
            /*extension: w(i-1) -> tau |- w(i) */
            w_i_m_1->second = l1;
            /*insert(pos, const T&) is to insert before pos*/
            iter_d[tau] = hlist->insert(w_i, make_pair(tau, tau_i));  
            
          } else {  /* w(i-1) ^ tau == w(i-2) ^ w(i-1)  */
            /*must be w(i-2) ~> w(i-1) */
            
            bool d = true;
            //need to handle the case when w_i_p_1 is null
            Line l3 = get_sharing_line(*w_i_p_1, tau);
            if ( w_i_p_1 != w_end )
              d = w_i_p_1->second != l3;
            if ( d ) { /* w(i+1) ^ tau != w(i+1) ^ w(i+2) */
              /*extension: w(i) |- tau -> w(i+1) */
              w_i->second = tau_i;
              iter_d[tau] = hlist->insert(w_i_p_1, make_pair(tau, l3));
            } else { /* w(i+1) ^ tau == w(i+1) ^ w(i+2) */
              /*must be w(1+1) ~> w(i+2) */
              Line l5 = get_sharing_line(*w_i_m_1, *w_i_p_1);
              if ( l5 != w_i_p_1->second ) { /* w(i-1) ^ w(i+1) != w(i+1) ^ w(i+2) */
                /*extension: w(i-2) -> tau |- w(i) |- w(i-1) -> w(i+1) */
                w_i_m_2->second = get_sharing_line(*w_i_m_2, tau);
                iter_d[tau] = hlist->insert(w_i, make_pair(tau, tau_i));
                w_i->second = w_i_m_1->second;
                w_i_m_1->second = l5;
                iter_d[w_i_m_1->first] = hlist->insert(w_i_p_1, *w_i_m_1);
                hlist->erase(w_i_m_1);
              } else {
                /*mesh is tetrahedral*/
                // dont know what that means.
                ;
              }
            }
            
          }
        } else { 
          /*Case 2:
            
            w(i-1) |- w(i) ~> w(1+1)
          */
          
          if ( w_i->second.second == tau_i.first 
               || w_i->second.second == tau_i.second ) {  /*w(i) ^ w(i+1) < w(i) ^ tau*/
            /*extension: w(i) |- tau -> w(i+1) */
            w_i->second = tau_i;
            Line l1 = get_sharing_line(*w_i_p_1, tau);
            iter_d[tau] = hlist->insert(w_i_p_1, make_pair(tau, l1));
          } else { /*w(i) ^ w(i+1) !< w(i) ^ tau*/
            Line l1 = get_sharing_line(*w_i_m_1, tau);
            bool c = true;
            iter w_i_m_2 = w_i_m_1;
            --w_i_m_2;
            if ( w_i_m_2 != w_end )
              c =  l1 != w_i_m_2->second;
            if (c) { /*w(i-1) ^ tau != w(i-2) ^ w(i-1)*/
              /*extension: w(i-1) -> tau |- w(i)*/
              w_i_m_1->second = l1;
              iter_d[tau] = hlist->insert(w_i, make_pair(tau, tau_i));
            } else { /*w(i-1) ^ tau == w(i-2) ^ w(i-1)*/
              /*must be w(i-2)~>w(i-1)*/
              /*extension: w(i-2) -> tau |- w(i) |- w(i-1) -> w(i+1)*/
              w_i_m_2->second = get_sharing_line(*w_i_m_2, tau);
              iter_d[tau] = hlist->insert(w_i, make_pair(tau, tau_i));
              w_i->second = w_i_m_1->second;
              w_i_m_1->second = get_sharing_line(*w_i_m_1, *w_i_p_1);
              iter_d[w_i_m_1->first] = hlist->insert(w_i_p_1, *w_i_m_1);
              hlist->erase(w_i_m_1);
            }
            
          }    
          
        }
        
      } else {
        
        if ( b ) {
          /*Case 3:
            
            w(i-1) ~> w(i) |- w(i+1)
          */
          bool c = false;
          if ( w_i_m_1 != w_end )
            c = ( w_i_m_1->second.second == tau_i.first)
              || ( w_i_m_1->second.second == tau_i.second);
          
          if ( c ) {  /*w(i-1) ^ w(i) < w(i) ^ tau*/
            /* extension: w(i-1) -> tau |- w(i) */
            if ( w_i_m_1 != w_end )
              w_i_m_1->second = get_sharing_line(*w_i_m_1, tau);
            iter_d[tau] = hlist->insert(w_i, make_pair(tau, tau_i));
          } else {
            bool d = true;
            Line l1;
            l1.first = SAW_SENTINAL;
            l1.second = SAW_SENTINAL;
            if ( w_i_p_1 != w_end ) {
              l1 = get_sharing_line(*w_i_p_1, tau);
              d = l1 != w_i_p_1->second;
            }
            if (d) { /*w(i+1) ^ tau != w(i+1) ^ w(i+2)*/
              /*extension: w(i) |- tau -> w(i+1) */
              w_i->second = tau_i;
              iter_d[tau] = hlist->insert(w_i_p_1, make_pair(tau, l1));
            } else {
              /*must be w(i+1) ~> w(i+2)*/
              /*extension: w(i-1) -> w(i+1) |- w(i) |- tau -> w(i+2) */
              iter w_i_p_2 = w_i_p_1;
              ++w_i_p_2;
              
              w_i_p_1->second = w_i->second;
              iter_d[i] = hlist->insert(w_i_p_2, make_pair(i, tau_i));
              hlist->erase(w_i);
              Line l2 = get_sharing_line(*w_i_p_2, tau);
              iter_d[tau] = hlist->insert(w_i_p_2, make_pair(tau, l2));
            }
          }
          
        } else {
          /*Case 4:
            
            w(i-1) ~> w(i) ~> w(i+1)
            
          */
          bool c = false;
          if ( w_i_m_1 != w_end ) {
            c = (w_i_m_1->second.second == tau_i.first) 
              || (w_i_m_1->second.second == tau_i.second);
          }
          if ( c ) {   /*w(i-1) ^ w(i) < w(i) ^ tau */
            /*extension: w(i-1) -> tau |- w(i) */
            if ( w_i_m_1 != w_end )
              w_i_m_1->second = get_sharing_line(*w_i_m_1, tau);
            iter_d[tau] = hlist->insert(w_i, make_pair(tau, tau_i));
          } else { 
            /*extension: w(i) |- tau -> w(i+1) */
            w_i->second = tau_i;
            Line l1;
            l1.first = SAW_SENTINAL;
            l1.second = SAW_SENTINAL;
            if ( w_i_p_1 != w_end ) 
              l1 = get_sharing_line(*w_i_p_1, tau);
            iter_d[tau] = hlist->insert(w_i_p_1, make_pair(tau, l1));
          }
        }
        
      }

      return true;
    }
    
  protected:
    TriangleDecorator td; /*a decorator for vertex*/
    HList             hlist;
    /*This must be a handle of list to record the SAW
      The element type of the list is pair<Vertex, Line>
     */
   
    IteratorD         iter_d; 
    /*Problem statement: Need a fast access to w for triangle i.
     *Possible solution: mantain an array to record. 
     iter_d[i] will return an iterator 
     which points to w(i), where i is a vertex
     representing triangle i.
    */
  };

  template <class Triangle, class HList, class Iterator>
  inline 
  SAW_visitor<Triangle, HList, Iterator>
  visit_SAW(Triangle t, HList hl, Iterator i) {
    return SAW_visitor<Triangle, HList, Iterator>(t, hl, i);
  }

  template <class Tri, class HList, class Iter>
  inline 
  SAW_visitor< random_access_iterator_property_map<Tri*,Tri,Tri&>,
    HList, random_access_iterator_property_map<Iter*,Iter,Iter&> >
  visit_SAW_ptr(Tri* t, HList hl, Iter* i) {
    typedef random_access_iterator_property_map<Tri*,Tri,Tri&> TriD;
    typedef random_access_iterator_property_map<Iter*,Iter,Iter&> IterD;
    return SAW_visitor<TriD, HList, IterD>(t, hl, i);
  }

  // should also have combo's of pointers, and also const :(
  
}

#endif /*BOOST_SAW_H*/
