/*
 * ptr_tuple.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: Feb 28, 2014
 * Author: Jan Travnicek
 */

#ifndef __PTR_VALUE_HPP_
#define __PTR_VALUE_HPP_

#include <ostream>
#include <sstream>
#include <string>

#include <extensions/iterator.hpp>
#include <extensions/compare.hpp>
#include <extensions/clone.hpp>

#include <extensions/common/tuple_common.hpp>

namespace ext {

/**
 * \brief
 * Class representing wrapper of dynamically allocated object behaving like rvalue reference.
 *
 * The class takes ownership of the dynamically allocated object
 *
 * \tparam the type of value wrapped
 */
template < class T >
class ptr_value {
	/**
	 * \brief
	 * Owned dynamically allocated object
	 */
	T * m_data;

public:
	/**
	 * \brief
	 * Constructor of the ptr_value from dynaically allocated object. The class will take ownerhip of the pointer.
	 */
	explicit ptr_value ( const T & value ) : m_data ( ext::clone ( value ) ) {

	}

	/**
	 * \brief
	 * Constructor of the ptr_value from dynaically allocated object. The class will take ownerhip of the pointer.
	 */
	explicit ptr_value ( T && value ) : m_data ( ext::clone ( std::forward < T > ( value ) ) ) {

	}

	/**
	 * \brief
	 * User conversion constructor from ptr_value for type U where T is its predecessor
	 *
	 * \tparam U the subtype of T
	 *
	 * \param other the other instance
	 */
	template < class U >
	ptr_value ( ptr_value < U > && other ) : m_data ( other.m_data ) {
		other.m_data = nullptr;
	}

	/**
	 * \brief
	 * Copy construction will clone the pointer referenced value in source instance to create new one for the constructed object.
	 *
	 * \param other the other instance
	 */
	ptr_value ( const ptr_value & other ) : m_data ( ext::clone ( other.m_data ) ) {

	}

	/**
	 * \brief
	 * Move construction will transfer ownership of the pointer from source instance to newly created one.
	 *
	 * \param other the other instance
	 */
	ptr_value ( ptr_value && other ) noexcept : m_data ( other.m_data ) {
		other.m_data = nullptr;
	};

	/**
	 * \brief
	 * Copy assignment will clone the pointer referenced value in source instance to create new one for the constructed object.
	 *
	 * \param other the other instance
	 */
	ptr_value & operator = ( const ptr_value & other ) {
		if ( this == & other )
			return *this;

		delete m_data;
		m_data = ext::clone ( other.m_data );
		return * this;
	}

	/**
	 * \param
	 * Move assignment will swap holded pointers between the two instances.
	 *
	 * \param other the other instance.
	 */
	ptr_value & operator = ( ptr_value && other ) noexcept {
		using std::swap;

		swap ( m_data, other.m_data );

		return *this;
	}

	/**
	 * \brief
	 * Destructor will free the owned instance.
	 */
	~ptr_value ( ) noexcept {
		delete m_data;
	}

	/**
	 * \brief
	 * Arrow operator to chain dereference.
	 *
	 * \return the owned pointer
	 */
	T * operator ->( ) && {
		return m_data;
	}

	/**
	 * \brief
	 * Arrow operator to chain dereference.
	 *
	 * \return the owned pointer
	 */
	T * operator ->( ) & {
		return m_data;
	}

	/**
	 * \brief
	 * Arrow operator to chain dereference.
	 *
	 * \return the owned pointer
	 */
	const T * operator ->( ) const & {
		return m_data;
	}

	/**
	 * \brief
	 * Allow automatic cast to const reference.
	 *
	 * \return the holded value
	 */
	operator T & ( ) & {
		return * m_data;
	}

	/**
	 * \brief
	 * Allow automatic cast to const rvalue reference which can be itself binded to const reference.
	 *
	 * The expected cast type const T & causes ambuguity in cast resolution, therefore a hack like this was found.
	 *
	 * \return the holded value
	 */
	operator const T && ( ) const & {
		return std::move ( * m_data );
	}

	/**
	 * \brief
	 * Allow automatic cast to rvalue reference which can be itself binded to const reference.
	 *
	 * \return the holded value
	 */
	operator T && ( ) && {
		return std::move ( * m_data );
	}

	/**
	 * \brief
	 * Getter of the holded value.
	 *
	 * \return constant reference to the holded value
	 */
	const T & get ( ) const {
		return * m_data;
	}

	template < class U >
	friend class ptr_value;
};

} /* namespace ext */

#endif /* __PTR_VALUE_HPP_ */
