/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrOrderedSet_DEFINED
#define GrOrderedSet_DEFINED

#include "GrRedBlackTree.h"

template <typename T, typename C = GrLess<T> >
class GrOrderedSet : public SkNoncopyable {
public:
    /**
     * Creates an empty set
     */
    GrOrderedSet() : fComp() {}
    ~GrOrderedSet() {}

    class Iter;

    /**
     * @return true if there are no items in the set, false otherwise.
     */
    bool empty() const { return fRBTree.empty(); }

    /**
     * @return the number of items in the set.
     */
    int count() const { return fRBTree.count(); }

    /**
     * Removes all items in the set
     */
    void reset() { fRBTree.reset(); }

    /**
     * Adds an element to set if it does not already exists in the set.
     * @param t  the item to add
     * @return an iterator to added element or matching element already in set
     */
    Iter insert(const T& t);

    /**
     * Removes the item indicated by an iterator. The iterator will not be valid
     * afterwards.
     * @param iter      iterator of item to remove. Must be valid (not end()).
     */
    void remove(const Iter& iter);

    /**
     * @return  an iterator to the first item in sorted order, or end() if empty
     */
    Iter begin();

    /**
     * Gets the last valid iterator. This is always valid, even on an empty.
     * However, it can never be dereferenced. Useful as a loop terminator.
     * @return  an iterator that is just beyond the last item in sorted order.
     */
    Iter end();

    /**
     * @return  an iterator that to the last item in sorted order, or end() if
     * empty.
     */
    Iter last();

    /**
     * Finds an occurrence of an item.
     * @param t     the item to find.
     * @return an iterator to a set element equal to t or end() if none exists.
     */
    Iter find(const T& t);

private:
    GrRedBlackTree<T, C> fRBTree;

    const C fComp;
};

template <typename T, typename C>
class GrOrderedSet<T,C>::Iter {
public:
    Iter() {}
    Iter(const Iter& i) { fTreeIter = i.fTreeIter; }
    Iter& operator =(const Iter& i) {
        fTreeIter = i.fTreeIter;
        return *this;
    }
    const T& operator *() const { return *fTreeIter; }
    bool operator ==(const Iter& i) const {
        return fTreeIter == i.fTreeIter;
    }
    bool operator !=(const Iter& i) const { return !(*this == i); }
    Iter& operator ++() {
        ++fTreeIter;
        return *this;
    }
    Iter& operator --() {
        --fTreeIter;
        return *this;
    }
    const typename GrRedBlackTree<T,C>::Iter& getTreeIter() const {
        return fTreeIter;
    }

private:
    friend class GrOrderedSet;
    explicit Iter(typename GrRedBlackTree<T, C>::Iter iter) {
        fTreeIter = iter;
    }
    typename GrRedBlackTree<T,C>::Iter fTreeIter;
};

template <typename T, typename C>
typename GrOrderedSet<T,C>::Iter GrOrderedSet<T,C>::begin() {
    return Iter(fRBTree.begin());
}

template <typename T, typename C>
typename GrOrderedSet<T,C>::Iter GrOrderedSet<T,C>::end() {
    return Iter(fRBTree.end());
}

template <typename T, typename C>
typename GrOrderedSet<T,C>::Iter GrOrderedSet<T,C>::last() {
    return Iter(fRBTree.last());
}

template <typename T, typename C>
typename GrOrderedSet<T,C>::Iter GrOrderedSet<T,C>::find(const T& t) {
    return Iter(fRBTree.find(t));
}

template <typename T, typename C>
typename GrOrderedSet<T,C>::Iter GrOrderedSet<T,C>::insert(const T& t) {
    if (fRBTree.find(t) == fRBTree.end()) {
        return Iter(fRBTree.insert(t));
    } else {
        return Iter(fRBTree.find(t));
    }
}

template <typename T, typename C>
void GrOrderedSet<T,C>::remove(const typename GrOrderedSet<T,C>::Iter& iter) {
    if (this->end() != iter) {
        fRBTree.remove(iter.getTreeIter());
    }
}

#endif
