/*
 * managed_value.hpp
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

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

 * Algorithms library toolkit 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 General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Created on: Sep 13, 2016
 * Author: Jan Travnicek
 */

#ifndef __MANAGED_VALUE_HPP_
#define __MANAGED_VALUE_HPP_

#include <extensions/container/linear_set.hpp>

namespace ext {

/**
 * \brief
 * Implementation of managed value mimicking the behavior of the underlying value. The class is designed to fire change events when the value is modified.
 *
 * \tparam T the type of stored values
 */
template < class T >
class managed_value {
	/**
	 * \brief
	 * The inner values holder.
	 */
	T m_data;

	std::vector < std::function < void ( const T & ) > > changeCallbacks;

	void fireChange ( const T & element ) {
		for ( const std::function < void ( const T & ) > & callback : changeCallbacks ) {
			callback ( element );
		}
	}

public:
	void addChangeCallback ( const std::function < void ( const T & ) > & callback ) {
		changeCallbacks.push_back ( callback );
	}

	/**
	 * \brief
	 * The type of values in the set.
	 */
	typedef T value_type;

	/**
	 * Constructor from range of values.
	 *
	 * \tparam Iterator the type of range iterator
	 *
	 * \param range the source range
	 */
	managed_value ( const T & value ) : m_data ( value ) {
	}

	/**
	 * Constructor from range of values.
	 *
	 * \tparam Iterator the type of range iterator
	 *
	 * \param range the source range
	 */
	managed_value ( T && value ) : m_data ( std::move ( value ) ) {
	}

	/**
	 * \brief
	 * Copy constructor.
	 *
	 * \param x the other linear set instance
	 */
	managed_value ( const managed_value & x ) : m_data ( x.m_data ) {
	}

	/**
	 * \brief
	 * Move constructor.
	 *
	 * \param x the other linear set instance
	 */
	managed_value ( managed_value && x ) : m_data ( std::move ( x.m_data ) ) {
	}

	/**
	 * \brief
	 * The destructor of the linear set.
	 */
	~managed_value ( ) {
	}

	/**
	 * \brief
	 * Copy operator of assignmet.
	 *
	 * \param x the other instance
	 *
	 * \return the modified instance
	 */
	managed_value & operator= ( const managed_value & x ) {
		m_data = x.m_data;
		fireChange ( m_data );
		return *this;
	}

	/**
	 * \brief
	 * Move operator of assignmet.
	 *
	 * \param x the other instance
	 *
	 * \return the modified instance
	 */
	managed_value& operator= ( managed_value && x ) {
		m_data = std::move ( x.m_data );
		fireChange ( m_data );
		return *this;
	}

	/**
	 * \brief
	 * Swaps two instances of linear set
	 *
	 * \param x the other instance to swap with
	 */
	void swap ( managed_value & x ) {
		// intentionally swaped values the change fired is before its execution
		this->fireChange ( x.getValue ( ) );
		x.fireChange ( this->getValue ( ) );

		using std::swap;
		swap ( m_data, x.m_data );
	}

	/**
	 * \brief
	 * Compares two set instances for equvalence.
	 *
	 * \param lhs the first instance to compare
	 * \param rhs the second instance to compare
	 *
	 * \return true if the two compared instance are equal, false othervise
	 */
	friend bool operator== ( const managed_value < T > & lhs, const managed_value < T > & rhs ) {
		return lhs.m_data == rhs.m_data;
	}

	/**
	 * \brief
	 * Compares two set instances for non equvalence.
	 *
	 * \param lhs the first instance to compare
	 * \param rhs the second instance to compare
	 *
	 * \return true if the two compared instance are not equal, false othervise
	 */
	friend bool operator!= ( const managed_value < T > & lhs, const managed_value < T > & rhs ) {
		return lhs.m_data != rhs.m_data;
	}

	/**
	 * \brief
	 * Compares two set instances by less relation.
	 *
	 * \param lhs the first instance to compare
	 * \param rhs the second instance to compare
	 *
	 * \return true if the first compared instance is less than the other instance, false othervise
	 */
	friend bool operator<  ( const managed_value < T > & lhs, const managed_value < T > & rhs ) {
		return lhs.m_data < rhs.m_data;
	}

	/**
	 * \brief
	 * Compares two set instances by less or equal relation.
	 *
	 * \param lhs the first instance to compare
	 * \param rhs the second instance to compare
	 *
	 * \return true if the first compared instance is less than or equal than the other instance, false othervise
	 */
	friend bool operator<= ( const managed_value < T > & lhs, const managed_value < T > & rhs ) {
		return lhs.m_data <= rhs.m_data;
	}

	/**
	 * \brief
	 * Compares two set instances by greater relation.
	 *
	 * \param lhs the first instance to compare
	 * \param rhs the second instance to compare
	 *
	 * \return true if the first compared instance is greater than the other instance, false othervise
	 */
	friend bool operator>  ( const managed_value < T > & lhs, const managed_value < T > & rhs ) {
		return lhs.m_data > rhs.m_data;
	}

	/**
	 * \brief
	 * Compares two set instances by greater or equal relation.
	 *
	 * \param lhs the first instance to compare
	 * \param rhs the second instance to compare
	 *
	 * \return true if the first compared instance is greater than or equal than the other instance, false othervise
	 */
	friend bool operator>= ( const managed_value < T > & lhs, const managed_value < T > & rhs ) {
		return lhs.m_data >= rhs.m_data;
	}

	/**
	 * Operator to give the managed value as raw constant reference.
	 *
	 * \return the managed value
	 */
	operator const T & ( ) const {
		return m_data;
	}

	/**
	 * Managed value getter providing the managed value as raw constant reference.
	 *
	 * \return the managed value
	 */
	const T & getValue ( ) const {
		return m_data;
	}

};

/**
 * \brief
 * Specialisation of swap for linear set
 *
 * \tparam T the value of the set
 *
 * \param x the first instance
 * \param y the second instance
 */
template < class T >
void swap ( ext::managed_value < T > & x, ext::managed_value < T > & y) {
	x.swap ( y );
}

/**
 * \brief
 * Operator to print the set to the output stream.
 *
 * \param out the output stream
 * \param value the array to print
 *
 * \tparam T the type of values inside the array
 *
 * \return the output stream from the \p out
 */
template< class T >
std::ostream & operator << ( std::ostream & out, const ext::managed_value < T > & value ) {
	out << value.getValue ( );
	return out;
}

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison.
 *
 * \tparam T the type of values inside the set
 */
template < class T >
struct compare < ext::managed_value < T > > {

	/**
	 * \brief
	 * Implementation of the three-way comparison.
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( const ext::managed_value < T > & first, const ext::managed_value < T > & second ) const {
		static compare < typename std::decay < T >::type > comp;
		return comp ( first.getValue ( ), second.getValue ( ) );
	}
};

/**
 * \brief
 * Overload of to_string function.
 *
 * \param value the set to be converted to string
 *
 * \tparam T the type of values inside the set
 *
 * \return string representation
 */
template < class T >
std::string to_string ( const ext::managed_value < T > & value ) {
	return to_string < T > ( value.getValue ( ) );
}

} /* namespace ext */

#endif /* __MANAGED_VALUE_HPP_ */
