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

#pragma once

#include <boost/utility.hpp>

#include <ostream>
#include <unordered_set>
#include <unordered_map>
#include "dev_support/stacktrace.hpp"
#include <algorithm>
#include <map>
#include "dev_support/openmp_wrapper.h"
#include "folly/PackedSyncPtr.h"


namespace restricted {

//todo discuss with Anton
static const uint16_t MAX_THREAD_CNT = 128;

class IdDistributor {
public:
    virtual size_t GetId() = 0;

    virtual ~IdDistributor() {
    }
};

template<class Iter>
class ListIdDistributor : public IdDistributor {
    friend class IdSegmentStorage;

private:
    Iter left_;
    Iter right_;
    size_t shift_;
    size_t max_;

    ListIdDistributor(Iter left, Iter right, size_t shift = 0, size_t max = size_t(-1)) : left_(left),
                                                                                          right_(right),
                                                                                          shift_(shift), max_(max) {
    }

public:
    bool valid() {
        return left_ < right_;
    }

    size_t GetId() {
        size_t result = *(left_);
        VERIFY(result < max_);
        ++left_;
        return shift_ + result;
    }
};

class SegmentIterator {
private:
    size_t value_;
public:
    SegmentIterator(size_t value) : value_(value) {
    }

    size_t operator*() const {
        return value_;
    }

    void operator++() {
        value_++;
    }

    void operator++(int) {
        ++value_;
    }

    bool operator==(const SegmentIterator &that) const {
        return value_ == that.value_;
    }

    bool operator!=(const SegmentIterator &that) const {
        return value_ != that.value_;
    }
};

class IdSegmentStorage {
    friend class LocalIdDistributor;

public:
    ListIdDistributor<SegmentIterator> GetSegmentIdDistributor(size_t left, size_t right) {
        VERIFY(left < right);
        VERIFY(right <= size_);
        return ListIdDistributor<SegmentIterator>(SegmentIterator(left), SegmentIterator(right), min_value_, size_);
    }

    template<class Iter>
    ListIdDistributor<Iter> GetSegmentIdDistributor(Iter left, Iter right) {
        VERIFY(left < right);
        return ListIdDistributor<Iter>(left, right, min_value_, size_);
    }

    IdSegmentStorage() : min_value_(0), size_(0) { }

private:
    IdSegmentStorage(size_t min_value, size_t size) : min_value_(min_value), size_(size) { }

    size_t min_value_;
    size_t size_;
};

// Id distributor for pure_pointer. Singleton.
class LocalIdDistributor : public IdDistributor, boost::noncopyable {
    friend class PeriodicIdDistributor;

    static const size_t INITIAL_MAX_INT_ID = 2;
public:
    size_t GetId() {
        return max_int_id_++;
    }

    IdSegmentStorage Reserve(size_t size) {
        max_int_id_ += size;
        return IdSegmentStorage(max_int_id_ - size, size);
    }

    IdSegmentStorage ReserveUpTo(size_t max) {
        VERIFY(max_int_id_ == INITIAL_MAX_INT_ID);
        max_int_id_ = max;
        return IdSegmentStorage(0, max);
    }

//  static GlobalIdDistributor &GetInstance() {
//    static GlobalIdDistributor instance(INITIAL_MAX_INT_ID);
//    return instance;
//  }

    size_t GetMax() const {
        return max_int_id_;
    }

    LocalIdDistributor(size_t min_id_value = INITIAL_MAX_INT_ID) : max_int_id_(min_id_value) { }

private:
    size_t max_int_id_;
};

/* id distributor used for concurrent algorithms.
* each thread use their own PeriodicIdDistributor with period equals to
* the quantity of threads. After thread's job is done Synchronize call are required
* to increase id in GlobalIdDistributor.
*/
class PeriodicIdDistributor : public IdDistributor {

public:
    PeriodicIdDistributor(LocalIdDistributor &id_distributor, size_t first_id, size_t period)
            : id_distributor_(id_distributor), cur_id_(first_id), period_(period) {
    }

    virtual size_t GetId() {
        size_t id = cur_id_;
        cur_id_ += period_;

        return id;
    }

    void Synchronize() const {
        size_t &global_max_id = id_distributor_.max_int_id_;
        global_max_id = std::max(cur_id_, global_max_id);
    }

private:
    LocalIdDistributor &id_distributor_;
    size_t cur_id_;
    size_t period_;
};

template<class PurePtrT>
class PurePtrLock;

template<class PurePtrT>
class PurePtrMarker;

//todo maybe make it extend folly::PackedSyncPtr<T>?
template<class T>
struct pure_pointer {
    typedef T type;
    typedef T *pointer_type;

    explicit pure_pointer()
            : int_id_(0) {
        ptr_.init(pointer_type(0), MAX_THREAD_CNT);
    }

    explicit pure_pointer(T *ptr)
            : int_id_(size_t(ptr)) {
        ptr_.init(ptr, MAX_THREAD_CNT);
        VERIFY(int_id_ < 2);
    }

    explicit pure_pointer(T *ptr, IdDistributor &idDistributor)
            : int_id_(generate_id(ptr, idDistributor)) {
        ptr_.init(ptr, MAX_THREAD_CNT);
    }

//    lock_pointer_type& get_lockable() {
//        return ptr_;
//    }

    T *get() const {
        return ptr_.get();
    }

    T &operator*() const {
        return *ptr_;
    }

    T *operator->() const {
        return ptr_.get();
    }

    bool operator==(const pure_pointer &rhs) const {
        if (int_id_ == rhs.int_id_) {
            VERIFY(ptr_.get() == rhs.ptr_.get());
            return true;
        }
        return false;
    }

    bool operator!=(const pure_pointer &rhs) const {
        return !operator==(rhs);
    }

    bool operator<(const pure_pointer &rhs) const {
        return this->int_id_ < rhs.int_id_;
    }

    bool operator<=(const pure_pointer &rhs) const {
        return *this < rhs || *this == rhs;
    }

    size_t hash() const {
        return this->int_id_;
    }

    size_t int_id() const {
        return int_id_;
    }

private:
    friend class PurePtrLock<pure_pointer<T>>;

    friend class PurePtrMarker<pure_pointer<T>>;

    typedef folly::PackedSyncPtr<T> lock_pointer_type;

    static size_t generate_id(T *ptr, IdDistributor &idDistributor) {
        if (ptr == 0 || ptr == (T *) 1 || ptr == (T *) (-1)) {
            return size_t(ptr);
        }

        return idDistributor.GetId();
    }

    lock_pointer_type ptr_;

    size_t int_id_;
};

template<class LockT>
class ReEnteringLock {
    LockT &lock_;
    bool reentered_;

    uint16_t locking_thread() const {
        //don't need barrier here (as folly documentation says)
        return lock_.extra();
    }

    uint16_t current_thread() const {
        return uint16_t(omp_get_thread_num());
    }

    void Lock() {
        lock_.lock();
        lock_.setExtra(current_thread());
    }

    void Unlock() {
        lock_.setExtra(MAX_THREAD_CNT);
        lock_.unlock();
    }

public:
    ReEnteringLock(LockT &lock) :
            lock_(lock),
            reentered_(false) {
        if (locking_thread() == current_thread()) {
            reentered_ = true;
        } else {
            Lock();
        }
    }

    ~ReEnteringLock() {
        if (!reentered_) {
            Unlock();
        }
    }
};

/**
* Lock that uses a pure ptr as a target.
* Be careful NOT to pass a COPY of pure ptr you want to use as locked object!
*/
template<class PurePtrT>
class PurePtrLock {
    ReEnteringLock<typename PurePtrT::lock_pointer_type> inner_lock_;

public:
    PurePtrLock(PurePtrT &pure_ptr) :
            inner_lock_(pure_ptr.ptr_) {
    }

};

/**
* Way to "mark" pure pointer without using additional memory.
* Marking/unmarking operations are atomic
* Be careful NOT to pass a COPY of pure ptr you want to mark!
* Do not use with PurePtrLocks, they use the same space for storing data...
*/
template<class PurePtrT>
class PurePtrMarker {
    typedef typename PurePtrT::lock_pointer_type LockWithData;

    void ChangeMark(PurePtrT &pure_ptr, uint16_t new_mark) const {
        LockWithData &lock_with_data = pure_ptr.ptr_;
        lock_with_data.lock();
        lock_with_data.setExtra(new_mark);
        lock_with_data.unlock();
    }

public:

    void mark(PurePtrT &pure_ptr) const {
        ChangeMark(pure_ptr, 0);
    }

    void unmark(PurePtrT &pure_ptr) const {
        ChangeMark(pure_ptr, MAX_THREAD_CNT);
    }

    bool is_marked(const PurePtrT &pure_ptr) const {
        uint16_t curr_mark = pure_ptr.ptr_.extra();
        VERIFY(curr_mark == 0 || curr_mark == MAX_THREAD_CNT);
        return curr_mark == 0;
    }

};

//template<class T>
//struct Comparator
//{
//  typedef pure_pointer<T> pointer_type_t;
//
//  bool operator()(pointer_type_t const& a, pointer_type_t const& b) const {
//    return a.get() < b.get();
//  }
//};

template<class T>
struct Hash {
    typedef pure_pointer<T> pointer_type_t;
    std::hash<T *> inner_hash_;

    size_t operator()(pointer_type_t const &a) const {
        return inner_hash_(a.get());
    }
};

template<class It>
struct iterator_wrapper {
    typedef typename It::value_type value_type;
    typedef typename It::difference_type difference_type;
    typedef typename It::reference reference;
    typedef typename It::pointer pointer;

    explicit iterator_wrapper(It it) : it_(it) { }

    reference   operator*() const { return it_.operator*(); }

    pointer   operator->() const { return it_.operator->(); }

    bool operator==(const iterator_wrapper &rhs) const { return it_ == rhs.it_; }

    bool operator!=(const iterator_wrapper &rhs) const { return it_ != rhs.it_; }

private:
    It it_;
};

template<class T>
struct set {
    typedef Hash<typename T::type> hash_t;
    typedef std::unordered_set<T, hash_t> base_set_t;
    typedef typename base_set_t::value_type value_type;

    typedef iterator_wrapper<typename base_set_t::iterator> iterator;
    typedef iterator_wrapper<typename base_set_t::const_iterator> const_iterator;

public:
    set() : base_set_(10, hash_t()) {
    }

    template<class It>
    set(It begin, It end) : base_set_(begin, end, 10, hash_t()) {
    }

    const_iterator begin() const { return const_iterator(base_set_.begin()); }

    const_iterator end() const { return const_iterator(base_set_.end()); }

    iterator begin() { return iterator(base_set_.begin()); }

    iterator end() { return iterator(base_set_.end()); }

    const_iterator find(const T &key) const { return const_iterator(base_set_.find(key)); }

    iterator find(const T &key) { return iterator(base_set_.find(key)); }

    size_t count(T const &item) const { return base_set_.count(item); }

    std::pair<iterator, bool> insert(value_type const &item) {
        const std::pair<iterator, bool> &ret = base_set_.insert(item);
        return make_pair(iterator(ret.first), ret.second);
    }

    template<class It>
    void insert(It first, It last) { base_set_.insert(first, last); }

    size_t erase(const T &x) { return base_set_.erase(x); }

    void clear() { base_set_.clear(); }

    size_t size() const { return base_set_.size(); }

    bool operator==(const set &rhs) const {
        if (this->size() != rhs.size())
            return false;

        for (auto i = base_set_.begin(), j = rhs.base_set_.begin();
             i != base_set_.end() && j != rhs.base_set_.end();
             ++i, ++j) {
            if (*i != *j)
                return false;
        }

        return true;
    }

    bool operator!=(const set &rhs) const {
        return !(*this == rhs);
    }

    template<class Comparator>
    void Copy(std::set<T, Comparator> &container) const {
        container.insert(base_set_.begin(), base_set_.end());
    }

private:
    base_set_t base_set_;
};


template<class Key, class Value>
struct map {
    typedef Hash<typename Key::type> hash_t;
    typedef std::unordered_map<Key, Value, hash_t> base_map_t;
    typedef typename base_map_t::value_type value_type;

    typedef iterator_wrapper<typename base_map_t::iterator> iterator;
    typedef iterator_wrapper<typename base_map_t::const_iterator> const_iterator;

public:
    map()
            : base_map_(10, hash_t()) {
    }

    template<class It>
    map(It begin, It end)
            : base_map_(begin, end, 10, hash_t()) {
    }

    const_iterator begin() const { return const_iterator(base_map_.begin()); }

    const_iterator end() const { return const_iterator(base_map_.end()); }

    iterator begin() { return iterator(base_map_.begin()); }

    iterator end() { return iterator(base_map_.end()); }

    const_iterator find(const Key &key) const {
        return const_iterator(base_map_.find(key));
    }

    iterator find(const Key &key) { return iterator(base_map_.find(key)); }

    size_t count(Key const &item) const { return base_map_.count(item); }

    Value &operator[](Key const &x) { return base_map_[x]; }

    std::pair<iterator, bool> insert(value_type const &value) {
        std::pair<iterator, bool> ret = base_map_.insert(value);
        return make_pair(iterator(ret.first), ret.second);
    }

    template<class It>
    void insert(It first, It last) { base_map_.insert(first, last); }

    size_t erase(Key const &x) { return base_map_.erase(x); }

    void clear() { base_map_.clear(); }

    size_t size() const { return base_map_.size(); }

    bool operator==(const map &rhs) const {
        if (size() != rhs.size())
            return false;

        for (auto i = base_map_.begin(), j = rhs.base_map_.begin();
             i != base_map_.end() && j != rhs.base_map_.end();
             ++i, ++j) {
            if (*i != *j)
                return false;
        }

        return true;
    }

    bool operator!=(const map &rhs) const {
        return !(*this == rhs);
    }

    template<class Comparator>
    void Copy(std::map<Key, Value, Comparator> &container) const {
        container.insert(base_map_.begin(), base_map_.end());
    }

private:
    base_map_t base_map_;
};

template<class T>
std::ostream &operator<<(std::ostream &stream, const pure_pointer<T> &pointer) {
    stream << pointer.int_id();
    return stream;
}

} // namespace restricted

namespace std {
template<class T>
struct hash<restricted::pure_pointer<T>> {
    size_t operator()(const restricted::pure_pointer<T> &pointer) const {
        return pointer.hash();
    }
};
}

template<class T, class Comparator>
class PairComparator {
private:
    Comparator comparator_;
public:
    PairComparator(Comparator comparator) : comparator_(comparator) {
    }

    bool operator()(std::pair<T, T> a, std::pair<T, T> b) const {
        return a.first == b.first ? comparator_(a.second, b.second) : comparator_(a.first, b.first);
    }
};

//
//template<typename T, class Comparator>
//class MixedComparator {
//private:
//  Comparator c1_;
//  Comparator c2_;
//public:
//  MixedComparator(const Comparator &c1, const Comparator &c2) : c1_(c1), c2_(c2) {
//  }
//
//  bool operator()(const T &a, const T &b) const {
//    if(c1_.IsAFAKE(a) || c1_.IsAFAKE(b)) {
//      if(c1_.IsAFAKEMin(a))
//        return !c1_.IsAFAKEMin(b);
//      if(c1_.IsAFAKEMax(b))
//        return c1_.IsAFAKEMax(a);
//      return false;
//    }
//    if(c1_.IsValidId(a) && c1_.IsValidId(b))
//      return c1_(a, b);
//    if(c1_.IsValidId(a))
//      return true;
//    if(c1_.IsValidId(b))
//      return false;
//    if(c2_.IsValidId(a) && c2_.IsValidId(b)) {
//      return c2_(a, b);
//    }
//    VERIFY(false);
//    return false;
//  }
//
//  bool IsValidId(T element) {
//    return c1_.IsValid(element) || c2_.IsValid(element);
//  }
//};

template<class Container, class Comparator>
class ContainerComparator {
private:
    Comparator comparator_;
public:
    ContainerComparator(const Comparator &comparator) : comparator_(comparator) {
    }

    bool operator()(const Container &a, const Container &b) const {
        for (auto ita = a.begin, itb = b.begin(); ita != a.end() && itb != b.end(); ++ita, ++itb) {
            if (*ita != *itb)
                return comparator_(*ita, *itb);
        }
        if (a.size() < b.size()) {
            return true;
        }
        return false;
    }

};

