// Copyright (c) 2016, the SDSL Project Authors.  All rights reserved.
// Please see the AUTHORS file for details.  Use of this source code is governed
// by a BSD license that can be found in the LICENSE file.
/*!\file cst_iterators.hpp
 * \brief cst_iterators.hpp contains iterator classes for traversing (compressed) suffix arrays.
 * \author Simon Gog
 */
#ifndef INCLUDED_SDSL_CST_ITERATORS
#define INCLUDED_SDSL_CST_ITERATORS

#include <cstdint>
#include <queue>

namespace sdsl
{

//! An forward iterator for (compressed) suffix trees.
/*! The cst_dfs_const_forward_iterator iterates through the nodes of a (compressed) suffix tree in
 * depth first search (dfs) order. Note, that
 * - each inner node is visited twice, and
 * - each leaf node is visited only once.
 *
 * If the current node is a inner node, the method visit() returns 1 for the first visit and 2 for
 * the second one.
 *
 * \par Time complexity
 * \f$\Order{1}\f$ for all methods
 * \par Space complexity
 * \f$\Order{1} \f$
 *
 * This iterator is the standard iterator for the classes
 * - sdsl::cst_sada,
 * - sdsl::cst_sct3
 */
// TODO: implement operator--
template <class Cst, uint32_t cache_size = 128>
class cst_dfs_const_forward_iterator
{
  public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = typename Cst::node_type;
    using difference_type = std::ptrdiff_t;
    using pointer = value_type *;
    using reference = value_type &;

    typedef const value_type const_reference;
    typedef typename Cst::size_type size_type;
    typedef cst_dfs_const_forward_iterator<Cst> iterator;
    typedef typename Cst::node_type node_type;

  private:
    const Cst * m_cst;
    node_type m_v;
    bool m_visited;
    bool m_valid;
    node_type * m_stack_cache;
    uint32_t m_stack_size;

    inline node_type parent()
    {
        --m_stack_size; // decrease stack size
        if (m_stack_cache != nullptr and m_stack_size < cache_size) { return m_stack_cache[m_stack_size]; }
        else
            return m_cst->parent(m_v);
    }

    inline node_type first_child()
    {
        if (m_stack_cache != nullptr and m_stack_size < cache_size) // push node to the stack
            m_stack_cache[m_stack_size] = m_v;
        m_stack_size++;
        return m_cst->select_child(m_v, 1);
    }

    //! Default constructor
    cst_dfs_const_forward_iterator()
      : m_cst(nullptr)
      , m_visited(false)
      , m_valid(false)
      , m_stack_cache(nullptr)
    {}

  public:
    //! Constructor
    cst_dfs_const_forward_iterator(const Cst * cst, const value_type node, bool visited = false, bool valid = true)
      : m_visited(visited)
      , m_valid(valid)
      , m_stack_cache(nullptr)
    {
        m_cst = cst;
        m_v = node;
        if (m_cst == nullptr) { m_valid = false; }
        else if (m_v == m_cst->root() and !m_visited and m_valid)
        { // if the iterator equal cst.begin()
            m_stack_cache = new node_type[cache_size];
            m_stack_size = 0;
            //			std::cerr<<"#creating stack "<<m_cst->lb(m_v)<<" "<<m_cst->rb(m_v)<<std::endl;
        }
    }

    //! Copy Constructor
    //	cst_dfs_const_forward_iterator(const cst_dfs_const_forward_iterator
    //&it):m_cst(it.cst),m_v(it.m_v),m_valid(m_valid), m_stack_cache(nullptr),m_stack_size(0){
    //	}

    ~cst_dfs_const_forward_iterator()
    {
        if (m_stack_cache != nullptr)
        {
            //			std::cerr<<"#deleting stack "<<m_cst->lb(m_v)<<" "<<m_cst->rb(m_v)<<std::endl;
            delete[] m_stack_cache;
        }
    }

    //! Returns how often the current node was already visited.
    uint8_t visit() const { return 1 + (uint8_t)m_visited; }

    void skip_subtree()
    {
        if (m_valid)
        {
            if (!m_visited) { m_visited = true; }
        }
    }

    //! Method for dereferencing the iterator.
    const_reference operator*() const { return m_v; }

    //! Prefix increment of the iterator.
    iterator & operator++()
    {
        if (!m_valid) return *this;
        if (m_v == m_cst->root() and m_visited)
        {
            m_valid = false;
            return *this;
        }
        value_type w;
        if (!m_visited)
        { // go down, if possible
            if (m_cst->is_leaf(m_v))
            {
                w = m_cst->sibling(m_v); // determine sibling of leaf v
                if (w == m_cst->root())
                { // if there exists no right sibling of the leaf v
                  //					w = m_cst->parent(m_v);
                    w = parent();
                    m_visited = true; // go up
                }
            }
            else
            { // v is not a leaf => go down the tree
                w = first_child();
            }
        }
        else
        { //
            w = m_cst->sibling(m_v);
            if (w == m_cst->root())
            { // if there exists no right sibling
                w = parent();
            }
            else
            {
                m_visited = false;
            }
        }
        m_v = w;
        return *this;
    }

    //! Postfix increment of the iterator.
    void operator++(int) { ++(*this); }

    //! Equality operator.
    bool operator==(const iterator & it) const
    {
        return (it.m_visited == m_visited) // visited status is equal
               and (it.m_valid == m_valid) // valid status is equal => for end() iterator
               and (it.m_v == m_v)         // nodes are equal
               and (it.m_cst == m_cst);    // iterator belongs to the same cst
    }

    //! Inequality operator.
    bool operator!=(const iterator & it) const { return !(*this == it); }
};

//! A forward iterator for a bottom up traversal of a suffix tree
template <class Cst>
class cst_bottom_up_const_forward_iterator
{
  public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = typename Cst::node_type;
    using difference_type = std::ptrdiff_t;
    using pointer = value_type *;
    using reference = value_type &;

    typedef const value_type const_reference;
    typedef typename Cst::size_type size_type;
    typedef cst_bottom_up_const_forward_iterator<Cst> iterator;

  private:
    const Cst * m_cst;
    typename Cst::node_type m_v;
    bool m_valid;

  public:
    //! Default constructor
    cst_bottom_up_const_forward_iterator()
      : m_cst(nullptr)
      , m_valid(false)
    {}

    //! Constructor
    cst_bottom_up_const_forward_iterator(const Cst * cst, const value_type node, bool valid = true)
      : m_valid(valid)
    {
        m_cst = cst;
        m_v = node;
        if (m_cst == nullptr) m_valid = false;
    }

    //! Method for dereferencing the iterator.
    const_reference operator*() const { return m_v; }

    //! Prefix increment of the iterator.
    iterator & operator++()
    {
        if (!m_valid) return *this;
        if (m_v == m_cst->root())
        {
            m_valid = false;
            return *this;
        }
        value_type w = m_cst->sibling(m_v);
        if (w == m_cst->root())
        {                             // if no next right sibling exist
            m_v = m_cst->parent(m_v); // go to parent
        }
        else
        {                                  // if next right sibling exist
            m_v = m_cst->leftmost_leaf(w); // go to leaftmost leaf in the subtree of w
        }
        return *this;
    }

    //! Postfix increment of the iterator.
    iterator operator++(int)
    {
        iterator it = *this;
        ++(*this);
        return it;
    }

    //! Equality operator.
    bool operator==(const iterator & it) const
    {
        return (it.m_valid == m_valid)  // valid status is equal => for end() iterator
               and (it.m_v == m_v)      // nodes are equal
               and (it.m_cst == m_cst); // iterator belongs to the same cst
    }

    //! Inequality operator.
    bool operator!=(const iterator & it) const { return !(*this == it); }
};

//! A forward iterator for a breath first traversal of a tree
/*!
 *	\tparam Cst 	A class which fulfills the CST concept
 *  \tparam Queue	A queue for the traversal. Note that for large data,
 *                  you should use an external implementation of a queue.
 */
template <class Cst, class Queue = std::queue<typename Cst::node_type>>
class cst_bfs_iterator
{
  public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = typename Cst::node_type;
    using difference_type = std::ptrdiff_t;
    using pointer = value_type *;
    using reference = value_type &;

    typedef const value_type const_reference;
    typedef typename Cst::size_type size_type;
    typedef cst_bfs_iterator<Cst, Queue> iterator;
    typedef Queue queue_type;

  private:
    const Cst * m_cst;  // Pointer to the cst.
    queue_type m_queue; //
    bool m_valid;       // State of the iterator.

  public:
    //! Constructor
    /*!
     * \param cst	Pointer to the compressed suffix tree.
     * \param node  Root node of the traversal.
     * \param valid State of the iterator.
     * \param end   If valid=true and end=true, we get the end() iterator otherwise ``end'' has no effect.
     */
    cst_bfs_iterator(const Cst * cst, const value_type node, bool valid = true, bool end_it = false)
    {
        m_cst = cst;
        m_valid = valid;
        if (m_cst != nullptr and !end_it) { m_queue.push(node); }
    }

    //! Returns the current number of nodes in the queue.
    size_type size() const { return m_queue.size(); }

    //! Method for dereferencing the iterator.
    const_reference operator*() const { return m_queue.front(); }

    //! Prefix increment of the iterator.
    iterator & operator++()
    {
        if (!m_valid) return *this;
        if (m_queue.empty())
        {
            m_valid = false;
            return *this;
        }
        value_type v = m_queue.front();
        m_queue.pop();
        value_type child = m_cst->select_child(v, 1);
        while (m_cst->root() != child)
        {
            m_queue.push(child);
            child = m_cst->sibling(child);
        }
        return *this;
    }

    //! Postfix increment of the iterator.
    iterator operator++(int)
    {
        iterator it = *this;
        ++(*this);
        return it;
    }

    //! Equality operator.
    bool operator==(const iterator & it) const
    {
        if (m_queue.size() != it.m_queue.size())
        {                 // if the queue size is different
            return false; // the state of the to iterator are different
        }
        if (m_queue.empty())
        { // if the queue is empty, we have to check if they are valid and
            return it.m_valid == m_valid and it.m_cst == m_cst; // belong to the same cst
        }
        return (it.m_valid == m_valid)                     // valid status is equal => for end() iterator
               and (it.m_cst == m_cst)                     // iterator belongs to the same cst
               and (it.m_queue.front() == m_queue.front()) // front element and
               and (it.m_queue.back() == m_queue.back());  //  back element are the same.
    }

    //! Inequality operator.
    bool operator!=(const iterator & it) const { return !(*this == it); }
};

} // end namespace sdsl

#endif
