// Copyright (c) 1997,2005
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel).  All rights reserved.
//
// This file is part of CGAL (www.cgal.org)
//
// $URL: https://github.com/CGAL/cgal/blob/v6.1/Stream_support/include/CGAL/IO/OFF/Scanner_OFF.h $
// $Id: include/CGAL/IO/OFF/Scanner_OFF.h b26b07a1242 $
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Lutz Kettner  <kettner@mpi-sb.mpg.de>
//                 Ralf Osbild   <osbild@mpi-sb.mpg.de>

#ifndef CGAL_IO_OFF_SCANNER_OFF_H
#define CGAL_IO_OFF_SCANNER_OFF_H 1

#include <CGAL/basic.h>
#include <CGAL/IO/OFF/File_scanner_OFF.h>

#include <iterator>
#include <vector>
#include <utility>
#include <limits>

namespace CGAL {

// The Facet_iterator's value type is vector<std::size_t>
// that contains the vertex indices.

template <class Pt>
class I_Scanner_OFF_vertex_iterator
{
public:
    typedef std::input_iterator_tag  iterator_category;
    typedef Pt                       value_type;
    typedef std::ptrdiff_t           difference_type;
    typedef const Pt*                pointer;
    typedef const Pt&                reference;
private:
    File_scanner_OFF*  m_scan;
    std::size_t        m_cnt;
    Pt                 m_point;

    void next_vertex() {
        CGAL_assertion( m_scan != nullptr);
        if ( m_cnt < m_scan->size_of_vertices()) {
            file_scan_vertex( *m_scan, m_point);
            m_scan->skip_to_next_vertex( m_cnt);
            ++m_cnt;
        } else
            m_cnt = m_scan->size_of_vertices() + 1;
    }
public:
    typedef Pt                                 Point;
    typedef File_scanner_OFF                   Scanner;
    typedef I_Scanner_OFF_vertex_iterator<Pt>  Self;

  I_Scanner_OFF_vertex_iterator(std::size_t cnt) : m_scan(nullptr), m_cnt(cnt+1) {}
    I_Scanner_OFF_vertex_iterator( Scanner& s, int cnt)
        : m_scan(&s), m_cnt(cnt)
    {
        next_vertex();
    }
    std::size_t  count()              const { return m_cnt; }
    bool   operator==( const Self& i) const { return m_cnt == i.m_cnt; }
    bool   operator!=( const Self& i) const { return m_cnt != i.m_cnt; }
    Self&  operator++() {
        next_vertex();
        return *this;
    }
    Self   operator++(int) {
        Self tmp = *this;
        ++(*this);
        return tmp;
    }
    const Point& operator*()  const {
        CGAL_assertion( m_scan != nullptr);
        return m_point;
    }
    const Point* operator->() const { return & operator*(); }
};

template <class Pt, class Nrm>
class I_Scanner_OFF_vertex_and_normals_iterator
{
public:
    typedef Pt                                              Point;
    typedef Nrm                                             Normal;
    typedef File_scanner_OFF                                Scanner;
    typedef I_Scanner_OFF_vertex_and_normals_iterator<Pt,Nrm> Self;

    typedef std::input_iterator_tag                         iterator_category;
    typedef std::pair<Point,Normal>                         value_type;
    typedef std::ptrdiff_t                                  difference_type;
    typedef const value_type*                               pointer;
    typedef const value_type&                               reference;
private:
    File_scanner_OFF*  m_scan;
    std::size_t        m_cnt;
    value_type         m_current;


    void next() {
        CGAL_assertion( m_scan != nullptr);
        if ( m_cnt < m_scan->size_of_vertices()) {
            file_scan_vertex( *m_scan, m_current.first);
            if ( m_scan->has_normals())
                file_scan_normal( *m_scan, m_current.second);
            m_scan->skip_to_next_vertex( m_cnt);
            ++m_cnt;
        } else
            m_cnt = m_scan->size_of_vertices() + 1;
    }
public:

    I_Scanner_OFF_vertex_and_normals_iterator( int cnt)
        : m_scan(nullptr), m_cnt(cnt+1) {}
    I_Scanner_OFF_vertex_and_normals_iterator( Scanner& s, int cnt)
        : m_scan(&s), m_cnt(cnt)
    {
        next();
    }
    std::size_t  count()              const { return m_cnt; }
    bool   operator==( const Self& i) const { return m_cnt == i.m_cnt; }
    bool   operator!=( const Self& i) const { return m_cnt != i.m_cnt; }
    Self&  operator++() {
        next();
        return *this;
    }
    Self   operator++(int) {
        Self tmp = *this;
        ++(*this);
        return tmp;
    }
    reference operator*()  const {
        CGAL_assertion( m_scan != nullptr);
        return m_current;
    }
    pointer   operator->() const { return & operator*(); }
};

class I_Scanner_OFF_facet_iterator
{
public:
    typedef std::input_iterator_tag  iterator_category;
  typedef std::vector<std::size_t>  value_type;
    typedef std::ptrdiff_t           difference_type;
    typedef value_type*              pointer;
    typedef value_type&              reference;
private:
    File_scanner_OFF*  m_scan;
    std::size_t        m_cnt;
    value_type         m_indices;

    void next_facet() {
        CGAL_assertion( m_scan != nullptr);
        if ( m_cnt < m_scan->size_of_facets()) {
            m_indices.erase( m_indices.begin(), m_indices.end());
            std::size_t no;
            m_scan->scan_facet( no, m_cnt);
            m_indices.reserve( no);
            std::size_t index = (std::numeric_limits<std::size_t>::max)();
            //  A huge value helps to detect a potential
            //  error in the function scan_facet_vertex_index
            for (std::size_t i = 0; i < no; ++i) {
                m_scan->scan_facet_vertex_index( index, i+1, m_cnt);
                m_indices.push_back( index);
            }
            m_scan->skip_to_next_facet( m_cnt);
            ++ m_cnt;
        } else
            m_cnt = m_scan->size_of_facets() + 1;
    }
public:
    value_type::size_type size_of_indices () const // RO
       { return m_indices.size(); }
    typedef value_type::size_type          indices_size_type; // RO
public:
    typedef File_scanner_OFF              Scanner;
    typedef I_Scanner_OFF_facet_iterator  Self;
    typedef value_type::iterator          iterator;

  I_Scanner_OFF_facet_iterator( std::size_t cnt) : m_scan(nullptr), m_cnt(cnt+1) {}
  I_Scanner_OFF_facet_iterator( Scanner& s, std::size_t cnt)
        : m_scan(&s), m_cnt(cnt)
    {
        next_facet();
    }
    std::size_t  count()             const { return m_cnt; }
    bool  operator==( const Self& i) const { return m_cnt == i.m_cnt; }
    bool  operator!=( const Self& i) const { return m_cnt != i.m_cnt; }
    Self& operator++() {
        next_facet();
        return *this;
    }
    Self  operator++(int) {
        Self tmp = *this;
        ++(*this);
        return tmp;
    }
    value_type&       operator*()        {
        CGAL_assertion( m_scan != nullptr);
        return m_indices;
    }
    const value_type& operator*()  const {
        CGAL_assertion( m_scan != nullptr);
        return m_indices;
    }
    value_type*       operator->()       { return & operator*(); }
    const value_type* operator->() const { return & operator*(); }
};


// The distance function is implemented to work in
// constant time for both iterators.

template <class Pt, class Distance> inline
void distance( const I_Scanner_OFF_vertex_iterator<Pt>& first,
               const I_Scanner_OFF_vertex_iterator<Pt>& last,
               Distance& n) {
    n = Distance( last.count() - first.count());
}
template <class Distance> inline
void distance( const I_Scanner_OFF_facet_iterator& first,
               const I_Scanner_OFF_facet_iterator& last,
               Distance& n) {
    n = Distance( last.count() - first.count());
}
template <class Pt> inline
std::ptrdiff_t distance( const I_Scanner_OFF_vertex_iterator<Pt>& first,
                         const I_Scanner_OFF_vertex_iterator<Pt>& last) {
    return last.count() - first.count();
}

inline
std::ptrdiff_t  distance( const I_Scanner_OFF_facet_iterator& first,
                          const I_Scanner_OFF_facet_iterator& last) {
    return last.count() - first.count();
}


template <class Kernel>
class Scanner_OFF {
    File_scanner_OFF  m_scan;
public:
    typedef typename Kernel::Point_3               Point;
    typedef Point                                  Pt;
    typedef typename Kernel::Vector_3              Normal;
    typedef Scanner_OFF<Kernel>                    Self;
    typedef I_Scanner_OFF_vertex_iterator<Pt>      Vertex_iterator;
    typedef I_Scanner_OFF_vertex_and_normals_iterator<Pt,Normal>
                                                   Vertex_and_normals_iterator;
    typedef I_Scanner_OFF_facet_iterator           Facet_iterator;
    typedef I_Scanner_OFF_facet_iterator::iterator Index_iterator;

    Scanner_OFF( std::istream& in, bool verbose = false)
        : m_scan( in, verbose) {}
    Scanner_OFF( std::istream& in, const File_header_OFF& header)
        : m_scan( in, header) {}

    std::size_t  size_of_vertices()   const { return m_scan.size_of_vertices(); }
    std::size_t  size_of_halfedges()  const { return m_scan.size_of_halfedges();}
    std::size_t  size_of_facets()     const { return m_scan.size_of_facets();   }

    bool verbose()            const { return m_scan.verbose();          }
    bool skel()               const { return m_scan.skel();             }
    bool off()                const { return m_scan.off();              }
    bool binary()             const { return m_scan.binary();           }
    bool ascii()              const { return m_scan.ascii();            }

    bool has_colors()         const { return m_scan.has_colors();       }
    bool has_normals()        const { return m_scan.has_normals();      }

    File_header_OFF& header()       { return m_scan;                    }
    const File_header_OFF&
         header()             const { return m_scan;                    }

    Vertex_iterator vertices_begin(){ return Vertex_iterator( m_scan,0);}
    Vertex_iterator vertices_end()  {
        return Vertex_iterator( size_of_vertices());
    }
    Facet_iterator facets_begin()   { return Facet_iterator( m_scan,0); }
    Facet_iterator facets_end()     {
        return Facet_iterator( size_of_facets());
    }
    Vertex_and_normals_iterator vertices_and_normals_begin(){
        return Vertex_and_normals_iterator( m_scan,0);
    }
    Vertex_and_normals_iterator vertices_and_normals_end()  {
        return Vertex_and_normals_iterator( size_of_vertices());
    }
};

} //namespace CGAL
#endif // CGAL_IO_OFF_SCANNER_OFF_H //
// EOF //

