/*
 * ValueHolder.hpp
 *
 *  Created on: 11. 7. 2017
 *	  Author: Jan Travnicek
 */

#ifndef _VALUE_HOLDER_HPP_
#define _VALUE_HOLDER_HPP_

#include <alib/memory>
#include <alib/typeindex>
#include <alib/typeinfo>
#include <abstraction/ValueHolderInterface.hpp>

namespace abstraction {

template < class Type >
class ValueHolderImpl : public ValueHolderInterface < Type > {
	mutable std::optional < Type > m_data;

public:
	using ValueHolderInterface < Type >::ValueHolderInterface;

	Type && getValue ( ) const override {
		return std::move ( m_data.value ( ) );
	}

	void setData ( Type && data ) {
		m_data = std::move ( data );
	}
};

template < class Type >
class ValueHolderImpl < const Type > : public ValueHolderInterface < Type > {
	mutable std::optional < Type > m_data;

public:
	using ValueHolderInterface < Type >::ValueHolderInterface;

	void setData ( Type && data ) {
		m_data = std::move ( data );
	}

	Type && getValue ( ) const override {
		return std::move ( m_data.value ( ) );
	}
};

template < class Type >
class ValueHolderImpl < Type & > : public ValueHolderInterface < Type > {
	mutable std::optional < std::reference_wrapper < Type > > m_data;

public:
	using ValueHolderInterface < Type >::ValueHolderInterface;

	void setData ( Type & data ) {
		m_data = std::reference_wrapper < Type > ( data );
	}

	Type && getValue ( ) const override {
		return std::move ( m_data->get ( ) );
	}
};

template < class Type >
class ValueHolderImpl < const Type & > : public ValueHolderInterface < Type > {
	mutable std::optional < std::reference_wrapper < Type > > m_data;

public:
	using ValueHolderInterface < Type >::ValueHolderInterface;

	void setData ( const Type & data ) {
		m_data = std::reference_wrapper < Type > ( const_cast < Type & > ( data ) );
	}

	Type && getValue ( ) const override {
		return std::move ( m_data->get ( ) );
	}
};

template < class Type >
class ValueHolderImpl < Type && > : public ValueHolderInterface < Type > {
	mutable std::optional < std::reference_wrapper < Type > > m_data;

public:
	using ValueHolderInterface < Type >::ValueHolderInterface;

	void setData ( Type && data ) {
		m_data = std::reference_wrapper < Type > ( data );
	}

	Type && getValue ( ) const override {
		return std::move ( m_data->get ( ) );
	}
};

template < class Type >
class ValueHolderImpl < const Type && > : public ValueHolderInterface < Type > {
	mutable std::optional < std::reference_wrapper < Type > > m_data;

public:
	using ValueHolderInterface < Type >::ValueHolderInterface;

	void setData ( const Type && data ) {
		m_data = std::reference_wrapper < Type > ( const_cast < Type & > ( data ) );
	}

	Type && getValue ( ) const override {
		return std::move ( m_data->get ( ) );
	}
};

template < class Type >
class ValueHolder : public ValueHolderImpl < Type > {
	using ValueHolderImpl < Type >::ValueHolderImpl;

public:
	ValueHolder ( const std::shared_ptr < abstraction::OperationAbstraction > & ref, Type && value ) : ValueHolderImpl < Type > ( ref, std::is_const_v < std::remove_reference_t < Type > >, std::is_rvalue_reference_v < Type >, std::is_lvalue_reference_v < Type >, true ) {
		this->setData ( std::forward < Type > ( value ) );
	}

	std::shared_ptr < abstraction::Value > cloneAsVariable ( bool isConst, bool isRvalueRef, bool isLvalueRef, bool move ) override {
		std::shared_ptr < abstraction::ValueHolder < Type > > res = std::make_shared < abstraction::ValueHolder < Type > > ( this->getLifeReference ( ), isConst, isRvalueRef, isLvalueRef, false );
		res->setData ( retrieveValue < Type > ( this->getProxyAbstraction ( ), move ) );

		return res;
	}

	std::shared_ptr < abstraction::Value > cloneAsValue ( bool isConst, bool isRvalueRef, bool isLvalueRef, bool move ) override {
		std::shared_ptr < abstraction::ValueHolder < Type > > res = std::make_shared < abstraction::ValueHolder < Type > > ( this->getLifeReference ( ), isConst, isRvalueRef, isLvalueRef, true );
		res->setData ( retrieveValue < Type > ( this->getProxyAbstraction ( ), move ) );

		return res;
	}

	ext::set < abstraction::ParamQualifiers::ParamQualifier > getTypeQualifiers ( ) const override {
		return abstraction::ParamQualifiers::paramQualifiers < Type > ( );
	}

	ext::type_index getTypeIndex ( ) const override {
		return ext::type_index ( typeid ( std::decay_t < Type > ) );
	}

};

} /* namespace abstraction */

#endif /* _VALUE_HOLDER_HPP_ */
