/*! \file pimap.h
 * \brief Associative array with custom types of key and value
 * 
 * This file declares PIMap
*/
/*
	PIP - Platform Independent Primitives
	Associative array with custom types of key and value
	Ivan Pelipenko peri4ko@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIMAP_H
#define PIMAP_H

#include "pivector.h"
#include "pideque.h"
#include "pipair.h"


template<class T>
void piQuickSort(T * a, ssize_t N) {
	if (N < 1) return;
	if (N < 46) {
		T tmp;
		ssize_t i,j;
		for(i=1; i<=N; i++) {
			tmp = a[i];
			j = i-1;
			while(tmp<a[j] && j>=0) {
				a[j+1] = a[j];
				j = j-1;
			}
			a[j+1] = tmp;
		}
	} else {
		ssize_t i = 0, j = N;
		T & p(a[N >> 1]);
		do {
			while (a[i] < p) i++;
			while (a[j] > p) j--;
			if (i <= j) {
				if (i != j) {
					//piCout << "swap" << i << j << a[i] << a[j];
					piSwap<T>(a[i], a[j]);
				}
				i++; j--;
			}
		} while (i <= j);
		if (j > 0) piQuickSort(a, j);
		if (N > i) piQuickSort(a + i, N - i);
	}
}


template <typename Key, typename T>
class PIMapIterator;


template <typename Key, typename T>
class PIMap {
	template <typename Key1, typename T1> friend PIByteArray & operator >>(PIByteArray & s, PIMap<Key1, T1> & v);
	template <typename Key1, typename T1> friend PIByteArray & operator <<(PIByteArray & s, const PIMap<Key1, T1> & v);
	template <typename Key1, typename T1> friend class PIMapIterator;
public:
	PIMap() {;}
	PIMap(const PIMap<Key, T> & other) {*this = other;}
	PIMap(PIMap<Key, T> && other) : pim_content(std::move(other.pim_content)), pim_index(std::move(other.pim_index)) {}
	virtual ~PIMap() {;}

	PIMap<Key, T> & operator =(const PIMap<Key, T> & other) {
		if (this == &other) return *this;
		clear();
		pim_content = other.pim_content;
		pim_index = other.pim_index;
		return *this;
	}

	PIMap<Key, T> & operator =(PIMap<Key, T> && other) {
		swap(other);
		return *this;
	}

	typedef T mapped_type;
	typedef Key key_type;
	typedef PIPair<Key, T> value_type;
	
	class iterator {
		friend class PIMap<Key, T>;
	private:
		iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * parent;
		ssize_t pos;
	public:
		iterator(): parent(0), pos(0) {}
		const Key & key() const {return const_cast<PIMap<Key, T> * >(parent)->_key(pos);}
		T & value() {return const_cast<PIMap<Key, T> * >(parent)->_value(pos);}
		void operator ++() {++pos;}
		void operator ++(int) {++pos;}
		void operator --() {--pos;}
		void operator --(int) {--pos;}
		bool operator ==(const iterator & it) const {return (pos == it.pos);}
		bool operator !=(const iterator & it) const {return (pos != it.pos);}
	};
	
	class reverse_iterator {
		friend class PIMap<Key, T>;
	private:
		reverse_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * parent;
		ssize_t pos;
	public:
		reverse_iterator(): parent(0), pos(0) {}
		const Key & key() const {return const_cast<PIMap<Key, T> * >(parent)->_key(pos);}
		T & value() const {return const_cast<PIMap<Key, T> * >(parent)->_value(pos);}
		void operator ++() {--pos;}
		void operator ++(int) {--pos;}
		void operator --() {++pos;}
		void operator --(int) {++pos;}
		bool operator ==(const reverse_iterator & it) const {return (pos == it.pos);}
		bool operator !=(const reverse_iterator & it) const {return (pos != it.pos);}
	};
	
	class const_iterator {
		friend class PIMap<Key, T>;
	private:
		const_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * parent;
		ssize_t pos;
	public:
		const_iterator(): parent(0), pos(0) {}
		const value_type operator *() const {return parent->_pair(pos);}
		const value_type* operator ->() const {cval = parent->_pair(pos); return &cval;}
		const Key & key() const {return const_cast<PIMap<Key, T> * >(parent)->_key(pos);}
		const T & value() const {return const_cast<PIMap<Key, T> * >(parent)->_value(pos);}
		void operator ++() {++pos;}
		void operator ++(int) {++pos;}
		void operator --() {--pos;}
		void operator --(int) {--pos;}
		bool operator ==(const const_iterator & it) const {return (pos == it.pos);}
		bool operator !=(const const_iterator & it) const {return (pos != it.pos);}
		mutable value_type cval;
	};
	
	class const_reverse_iterator {
		friend class PIMap<Key, T>;
	private:
		const_reverse_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * parent;
		ssize_t pos;
	public:
		const_reverse_iterator(): parent(0), pos(0) {}
		const value_type operator *() const {return parent->_pair(pos);}
		const value_type* operator ->() const {cval = parent->_pair(pos); return &cval;}
		void operator ++() {--pos;}
		void operator ++(int) {--pos;}
		void operator --() {++pos;}
		void operator --(int) {++pos;}
		bool operator ==(const const_reverse_iterator & it) const {return (pos == it.pos);}
		bool operator !=(const const_reverse_iterator & it) const {return (pos != it.pos);}
		mutable value_type cval;
	};

 	iterator begin() {return iterator(this, 0);}
 	iterator end() {return iterator(this, size());}
	const_iterator begin() const {return const_iterator(this, 0);}
	const_iterator end() const {return const_iterator(this, size());}
	const_iterator constBegin() const {return const_iterator(this, 0);}
	const_iterator constEnd() const {return const_iterator(this, size());}
 	reverse_iterator rbegin() {return reverse_iterator(this, size() - 1);}
 	reverse_iterator rend() {return reverse_iterator(this, -1);}
	const_reverse_iterator rbegin() const {return const_reverse_iterator(this, size() - 1);}
	const_reverse_iterator rend() const {return const_reverse_iterator(this, -1);}
	const_reverse_iterator constRbegin() const {return const_reverse_iterator(this, size() - 1);}
	const_reverse_iterator constRend() const {return const_reverse_iterator(this, -1);}

	PIMapIterator<Key, T> makeIterator()  const {return PIMapIterator<Key, T>(*this);}
	PIMapIterator<Key, T> makeReverseIterator() const {return PIMapIterator<Key, T>(*this, true);}

	size_t size() const {return pim_content.size();}
	int size_s() const {return pim_content.size_s();}
	size_t length() const {return pim_content.size();}
	bool isEmpty() const {return (pim_content.size() == 0);}
	
	T & operator [](const Key & key) {
		bool f(false);
		ssize_t i = _find(key, f);
		if (f) return pim_content[pim_index[i].index];
		pim_content.push_back(T());
		pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
		return pim_content.back();
	}
	const T operator [](const Key & key) const {bool f(false); ssize_t i = _find(key, f); if (f) return pim_content[pim_index[i].index]; return T();}
	const T at(const Key & key) const {return (*this)[key];}
	
	PIMap<Key, T> & operator <<(const PIMap<Key, T> & other) {
		assert(&other != this);
		if (other.isEmpty()) return *this;
		if (other.size() == 1) {insert(other.pim_index[0].key, other.pim_content[0]); return *this;}
		if (other.size() == 2) {insert(other.pim_index[0].key, other.pim_content[0]); insert(other.pim_index[1].key, other.pim_content[1]); return *this;}
		for (int i = 0; i < other.pim_index.size_s(); ++i)
			insert(other.pim_index[i].key, other.pim_content[other.pim_index[i].index]);
		return *this;
	}

	bool operator ==(const PIMap<Key, T> & t) const {return (pim_content == t.pim_content && pim_index == t.pim_index);}
	bool operator !=(const PIMap<Key, T> & t) const {return (pim_content != t.pim_content || pim_index != t.pim_index);}
	bool contains(const Key & key) const {bool f(false); _find(key, f); return f;}

	PIMap<Key, T> & reserve(size_t new_size) {pim_content.reserve(new_size); pim_index.reserve(new_size); return *this;}

	PIMap<Key, T> & removeOne(const Key & key) {bool f(false); ssize_t i = _find(key, f); if (f) _remove(i); return *this;}
	PIMap<Key, T> & remove(const Key & key) {return removeOne(key);}
	PIMap<Key, T> & erase(const Key & key) {return removeOne(key);}
	PIMap<Key, T> & clear() {pim_content.clear(); pim_index.clear(); return *this;}
	
	void swap(PIMap<Key, T> & other) {
		pim_content.swap(other.pim_content);
		pim_index.swap(other.pim_index);
	}

	PIMap<Key, T> & insert(const Key & key, const T & value) {
		bool f(false);
		ssize_t i = _find(key, f);
		//piCout << "insert key=" << key << "found=" << f << "index=" << i << "value=" << value;
		if (f) {
			pim_content[pim_index[i].index] = value;
		} else {
			pim_content.push_back(value);
			pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
		}
		return *this;
	}
	PIMap<Key, T> & insert(const Key & key, T && value) {
		bool f(false);
		ssize_t i = _find(key, f);
		//piCout << "insert key=" << key << "found=" << f << "index=" << i << "value=" << value;
		if (f) {
			pim_content[pim_index[i].index] = std::move(value);
		} else {
			pim_content.push_back(std::move(value));
			pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
		}
		return *this;
	}
	const T value(const Key & key, const T & default_ = T()) const {bool f(false); ssize_t i = _find(key, f); if (!f) return default_; return pim_content[pim_index[i].index];}
	PIVector<T> values() const {return pim_content;}
	Key key(const T & value_, const Key & default_ = Key()) const {for (int i = 0; i < pim_index.size_s(); ++i) if (pim_content[pim_index[i].index] == value_) return pim_index[i].key; return default_;}
	PIVector<Key> keys() const {
		PIVector<Key> ret;
		for (int i = 0; i < pim_index.size_s(); ++i)
			ret << pim_index[i].key;
		return ret;
	}
	
	void dump() {
		piCout << "PIMap" << size() << "entries" << PICoutManipulators::NewLine << "content:";
		for (size_t i = 0; i < pim_content.size(); ++i)
			piCout << PICoutManipulators::Tab << i << ":" << pim_content[i];
		piCout << "index:";
		for (size_t i = 0; i < pim_index.size(); ++i)
			piCout << PICoutManipulators::Tab << i << ":" << pim_index[i].key << "->" << pim_index[i].index;
	}
	
protected:
	struct MapIndex {
		MapIndex(Key k = Key(), size_t i = 0): key(k), index(i) {;}
		Key key;
		size_t index;
		bool operator ==(const MapIndex & s) const {return key == s.key;}
		bool operator !=(const MapIndex & s) const {return key != s.key;}
		bool operator <(const MapIndex & s) const {return key < s.key;}
		bool operator >(const MapIndex & s) const {return key > s.key;}
	};
	template <typename Key1, typename T1> friend PIByteArray & operator >>(PIByteArray & s, PIDeque<typename PIMap<Key1, T1>::MapIndex> & v);
	template <typename Key1, typename T1> friend PIByteArray & operator <<(PIByteArray & s, const PIDeque<typename PIMap<Key1, T1>::MapIndex> & v);

	ssize_t binarySearch(ssize_t first, ssize_t last, const Key & key, bool & found) const {
		ssize_t mid;
		while (first <= last) {
			mid = (first + last) / 2;
			if (key > pim_index[mid].key) first = mid + 1;
			else if (key < pim_index[mid].key) last = mid - 1;
			else {found = true; return mid;}
		}
		found = false;
		return first;
	}
	void _sort() {piQuickSort<MapIndex>(pim_index.data(), pim_index.size_s() - 1);}
	ssize_t _find(const Key & k, bool & found) const {
		if (pim_index.isEmpty()) {
			found = false;
			return 0;
		}
		return binarySearch(0, pim_index.size_s() - 1, k, found);
	}
	void _remove(ssize_t index) {
		//if (index >= pim_index.size()) return;
		size_t ci = pim_index[index].index, bi = pim_index.size() - 1;
		pim_index.remove(index);
		for (size_t i = 0; i < pim_index.size(); ++i)
			if (pim_index[i].index == bi) {
				pim_index[i].index = ci;
				break;
			}
		piSwap<T>(pim_content[ci], pim_content.back());
		pim_content.resize(pim_index.size());
	}
	const value_type _pair(ssize_t index) const {
		if (index < 0 || index >= pim_index.size_s())
			return value_type();
		//piCout << "_pair" << index << pim_index[index].index;
		return value_type(pim_index[index].key, pim_content[pim_index[index].index]);
	}
	Key & _key(ssize_t index) {return pim_index[index].key;}
	T & _value(ssize_t index) {return pim_content[pim_index[index].index];}
	
	PIVector<T> pim_content;
	PIDeque<MapIndex> pim_index;
};


template <typename Key, typename T>
class PIMapIterator {
	typedef PIMap<Key, T> MapType;
public:
	PIMapIterator(const PIMap<Key, T> & map, bool reverse = false): m(map), pos(-1), rev(reverse) {
		if (rev) pos = m.size_s();
	}
	const Key & key() const {return const_cast<MapType & >(m)._key(pos);}
	const T & value() const {return const_cast<MapType & >(m)._value(pos);}
	T & valueRef() const {return const_cast<MapType & >(m)._value(pos);}
	inline bool hasNext() const {
		if (rev) {
			return pos > 0;
		} else {
			return pos < (m.size_s() - 1);
		}
		return false;
	}
	inline bool next() {
		if (rev) {
			--pos;
			return pos >= 0;
		} else {
			++pos;
			return pos < m.size_s();
		}
		return false;
	}
	inline void reset() {
		if (rev) {
			pos = m.size_s();
		} else {
			pos = -1;
		}
	}
private:
	const MapType & m;
	ssize_t pos;
	bool rev;
};


#ifdef PIP_STD_IOSTREAM
template<typename Key, typename Type>
inline std::ostream & operator <<(std::ostream & s, const PIMap<Key, Type> & v) {
	s << "{";
	bool first = true;
	for (typename PIMap<Key, Type>::const_iterator i = v.begin(); i != v.end(); ++i) {
		if (!first)
			s << ", ";
		first = false;
		s << i->first << ": " << i->second;
	}
	s << "}";
	return s;
}
#endif

template<typename Key, typename Type>
inline PICout operator <<(PICout s, const PIMap<Key, Type> & v) {
	s.space();
	s.setControl(0, true);
	s << "{";
	bool first = true;
	for (typename PIMap<Key, Type>::const_iterator i = v.begin(); i != v.end(); ++i) {
		if (!first)
			s << ", ";
		first = false;
		s << i->first << ": " << i->second;
	}
	s << "}";
	s.restoreControl();
	return s;
}

template<typename Key, typename Type> inline void piSwap(PIMap<Key, Type> & f, PIMap<Key, Type> & s) {f.swap(s);}


#endif // PIMAP_H
