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

#ifndef __HAMMER_POINTER_ITERATOR_HPP__
#define __HAMMER_POINTER_ITERATOR_HPP__

#include <iterator>
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>

template<typename T>
class pointer_iterator : public std::iterator<std::random_access_iterator_tag, T> {
protected:
    T *data_;

public:
    typedef std::random_access_iterator_tag iterator_category;
    typedef typename std::iterator<std::random_access_iterator_tag, T>::value_type value_type;
    typedef typename std::iterator<std::random_access_iterator_tag, T>::difference_type difference_type;
    typedef typename std::iterator<std::random_access_iterator_tag, T>::reference reference;
    typedef typename std::iterator<std::random_access_iterator_tag, T>::pointer pointer;

    pointer_iterator() : data_(NULL) { }

    template<typename T2>
    pointer_iterator(const pointer_iterator<T2> &r) : data_(&(*r)) { }

    pointer_iterator(pointer data) : data_(data) { }

    template<typename T2>
    pointer_iterator &operator=(const pointer_iterator<T2> &r) {
        data_ = &(*r);
        return *this;
    }

    pointer_iterator &operator++() {
        data_ += 1;
        return *this;
    }

    pointer_iterator &operator--() {
        data_ -= 1;
        return *this;
    }

    pointer_iterator operator++(int) {
        pointer_iterator res = *this;
        data_ += 1;

        return res;
    }

    pointer_iterator operator--(int) {
        pointer_iterator res = *this;
        data_ -= 1;

        return res;
    }

    pointer_iterator operator+(const difference_type &n) const {
        return pointer_iterator(data_ + n);
    }

    pointer_iterator &operator+=(const difference_type &n) {
        data_ += n;
        return *this;
    }

    pointer_iterator operator-(const difference_type &n) const {
        return pointer_iterator(pointer(data_ - n));
    }

    pointer_iterator &operator-=(const difference_type &n) {
        data_ -= n;
        return *this;
    }

    reference operator*() const {
        return *data_;
    }

    pointer operator->() const {
        return data_;
    }

    reference operator[](const difference_type &n) const {
        return data_[n];
    }

    template<typename T2>
    friend bool operator==(const pointer_iterator<T2> &r1,
                           const pointer_iterator<T2> &r2);

    template<typename T2>
    friend bool operator!=(const pointer_iterator<T2> &r1,
                           const pointer_iterator<T2> &r2);

    template<typename T2>
    friend bool operator<(const pointer_iterator<T2> &r1,
                          const pointer_iterator<T2> &r2);

    template<typename T2>
    friend bool operator>(const pointer_iterator<T2> &r1,
                          const pointer_iterator<T2> &r2);

    template<typename T2>
    friend bool operator<=(const pointer_iterator<T2> &r1,
                           const pointer_iterator<T2> &r2);

    template<typename T2>
    friend bool operator>=(const pointer_iterator<T2> &r1,
                           const pointer_iterator<T2> &r2);

    template<typename T2>
    friend typename pointer_iterator<T2>::difference_type
            operator+(const pointer_iterator<T2> &r1,
                      const pointer_iterator<T2> &r2);

    template<typename T2>
    friend typename pointer_iterator<T2>::difference_type
            operator-(const pointer_iterator<T2> &r1,
                      const pointer_iterator<T2> &r2);
};

template<typename T>
inline bool operator==(const pointer_iterator<T> &r1,
                       const pointer_iterator<T> &r2) {
    return (r1.data_ == r2.data_);
}

template<typename T>
inline bool operator!=(const pointer_iterator<T> &r1,
                       const pointer_iterator<T> &r2) {
    return (r1.data_ != r2.data_);
}

template<typename T>
inline bool operator<(const pointer_iterator<T> &r1,
                      const pointer_iterator<T> &r2) {
    return (r1.data_ < r2.data_);
}

template<typename T>
inline bool operator>(const pointer_iterator<T> &r1,
                      const pointer_iterator<T> &r2) {
    return (r1.data_ > r2.data_);
}

template<typename T>
inline bool operator<=(const pointer_iterator<T> &r1,
                       const pointer_iterator<T> &r2) {
    return (r1.data_ <= r2.data_);
}

template<typename T>
inline bool operator>=(const pointer_iterator<T> &r1,
                       const pointer_iterator<T> &r2) {
    return (r1.data_ >= r2.data_);
}

template<typename T>
inline typename pointer_iterator<T>::difference_type
operator-(const pointer_iterator<T> &r1,
          const pointer_iterator<T> &r2) {
    return (r1.data_ - r2.data_);
}

#endif // __HAMMER_POINTER_ITERATOR_HPP__
