//***************************************************************************
//* Copyright (c) 2015 Saint Petersburg State University
//* Copyright (c) 2011-2014 Saint Petersburg Academic University
//* All Rights Reserved
//* See file LICENSE for details.
//***************************************************************************

#ifndef __ADT_CHAINED_ITERATOR_HPP__
#define __ADT_CHAINED_ITERATOR_HPP__

#include <boost/iterator/iterator_facade.hpp>

#include <iterator>
#include <vector>

namespace adt {

template<class It>
class chained_iterator :
        public boost::iterator_facade<chained_iterator<It>,
                typename std::iterator_traits<It>::value_type,
                boost::forward_traversal_tag,
                typename std::iterator_traits<It>::value_type> {
public:
    chained_iterator(It begin, It end) :
            section_(0), current_(begin) {
        join(begin, end);
    }

    void join(It begin, It end) {
        begins_.push_back(begin);
        ends_.push_back(end);
        skip_empty();
    }

private:
    friend class boost::iterator_core_access;

    bool is_end() const {
        return current_ == ends_[section_];
    }

    void skip_empty() {
        while ((section_ + 1) < begins_.size() &&
               current_ == ends_[section_])
            current_ = begins_[++section_];
    }

    void increment() {
        skip_empty();
        ++current_;
        skip_empty();
    }

    bool equal(const chained_iterator &other) const {
        // Special case: both ends
        bool other_end = other.is_end(), current_end = is_end();
        if (current_end || other_end)
            return other_end == current_end;

        // Now, make sure we are comparing the iterators from the same sequences
        // (actually, not, but this would be undefined behavior)
        return (section_ == other.section_ &&
                current_ == other.current_);
    }

    typename std::iterator_traits<It>::value_type dereference() const {
        return *current_;
    }

    size_t section_;
    It current_;
    std::vector<It> begins_;
    std::vector<It> ends_;
};

} //adt
#endif
