/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef avro_BufferDetailIterator_hh__
#define avro_BufferDetailIterator_hh__

#include "BufferDetail.hh"

/** 
 * \file BufferDetailIterator.hh
 *
 * \brief The implementation details for the Buffer iterators.
 **/

namespace avro {

namespace detail {

/** 
 * \brief Implements conversion from a chunk to asio::const_buffer
 * 
 * Iterators for an InputBuffer will iterate over the avro of chunks, so
 * internally they contain an iterator.  But the iterator needs to be
 * convertable to an asio buffer for use in boost::asio functions.  This class
 * wraps the iterator with a cast operator to do this conversion.
 **/

struct InputIteratorHelper
{
    /// Construct a helper with an unnassigned iterator. 
    InputIteratorHelper() :
        iter_()
    {}

    /// Construct a helper with an iterator.
    InputIteratorHelper(const BufferImpl::ChunkList::const_iterator &iter) :
        iter_(iter)
    {}

    /// The location of valid data in this chunk.
    const data_type *data() const {
        return iter_->tellReadPos();
    }

    /// The size of valid data in this chunk.
    size_type size() const {
        return iter_->dataSize();
    }

    /// Conversion operator.   It doesn't check for null, because the only
    /// the only time the chunk should be null is when it's the iterator 
    /// end(), which should never be dereferenced anyway.
#ifdef HAVE_BOOST_ASIO
    operator ConstAsioBuffer() const {
        return ConstAsioBuffer(data(), size());
    }
#endif

    BufferImpl::ChunkList::const_iterator iter_; ///< the current iterator
};

/** 
 * \brief Implements conversion from a chunk to asio::buffer 
 * 
 * Iterators for an OutputBuffer will iterate over the avro of chunks, so
 * internally they contain an iterator.  But the iterator needs to be
 * convertable to an asio buffer for use in boost::asio functions.  This class
 * wraps the iterator with a cast operator to do this conversion.
 */

struct OutputIteratorHelper
{
    /// Construct a helper with an unnassigned iterator.
    OutputIteratorHelper() :
            iter_()
    {}

    /// Construct a helper with an iterator.
    OutputIteratorHelper(const BufferImpl::ChunkList::const_iterator &iter) :
            iter_(iter)
    {}

    /// The location of the first writable byte in this chunk.
    data_type *data() const {
        return iter_->tellWritePos();
    }

    /// The size of area that can be written in this chunk.
    size_type size() const {
        return iter_->freeSize();
    }

    /// Conversion operator.   It doesn't check for null, because the only
    /// the only time the chunk should be null is when it's the iterator 
    /// end(), which should never be dereferenced anyway.
#ifdef HAVE_BOOST_ASIO
    operator MutableAsioBuffer() const {
        return MutableAsioBuffer(data(), size());
    }
#endif

    BufferImpl::ChunkList::const_iterator iter_; ///< the current iterator
};

/** 
 * \brief Implements the iterator for Buffer, that iterates through the
 * buffer's chunks.
 **/

template<typename Helper>
class BufferIterator 
{

  public:

    typedef BufferIterator<Helper> this_type;

    /** 
     * @name Typedefs
     *
     * STL iterators define the following declarations.  According to
     * boost::asio documentation, the library expects the iterator to be
     * bidirectional, however this implements only the forward iterator type.
     * So far this has not created any problems with asio, but may change if
     * future versions of the asio require it.
     **/

    //@{
    typedef std::forward_iterator_tag iterator_category; // this is a lie to appease asio
    typedef Helper value_type;
    typedef std::ptrdiff_t difference_type;
    typedef value_type* pointer;
    typedef value_type& reference;
    //@}

    /// Construct an unitialized iterator.
    BufferIterator() :
        helper_()
    { }

    /* The default implementations are good here
    /// Copy constructor.
    BufferIterator(const BufferIterator &src) :
        helper_(src.helper_)
    { }
    /// Assignment.
    this_type& operator= (const this_type &rhs) {
        helper_ = rhs.helper_;
        return *this;
    }
    */

    /// Construct iterator at the position in the buffer's chunk list.
    explicit BufferIterator(BufferImpl::ChunkList::const_iterator iter) :
        helper_(iter)
    { }

    /// Dereference iterator, returns InputIteratorHelper or OutputIteratorHelper wrapper.
    reference operator *() {
        return helper_;
    }

    /// Dereference iterator, returns const InputIteratorHelper or OutputIteratorHelper wrapper.
    const value_type &operator *() const {
        return helper_;
    }

    /// Dereference iterator, returns InputIteratorHelper or OutputIteratorHelper wrapper.
    pointer operator->() {
        return &helper_;
    }

    /// Dereference iterator, returns const InputIteratorHelper or OutputIteratorHelper wrapper.
    const value_type *operator->() const {
        return &helper_;
    }
    
    /// Increment to next chunk in list, or to end() iterator.
    this_type& operator++() 
    {
        ++helper_.iter_;
        return *this;
    }

    /// Increment to next chunk in list, or to end() iterator.
    this_type operator++(int)
    {
        this_type ret = *this;
        ++helper_.iter_;
        return ret;
    }

    /// True if iterators point to same chunks.
    bool operator==(const this_type &rhs) const {
        return (helper_.iter_ == rhs.helper_.iter_);
    }

    /// True if iterators point to different chunks.
    bool operator!=(const this_type &rhs) const {
        return (helper_.iter_ != rhs.helper_.iter_);
    }

  private:

    Helper helper_;
};

typedef BufferIterator<InputIteratorHelper> InputBufferIterator;
typedef BufferIterator<OutputIteratorHelper> OutputBufferIterator;

} // detail namespace

} // namespace

#endif
