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

#include <abstraction/Value.hpp>
#include <abstraction/OperationAbstraction.hpp>

namespace abstraction {

std::shared_ptr < abstraction::Value > Value::getProxyAbstraction ( ) {
	return shared_from_this ( );
}

std::string Value::getType ( ) const {
	return ext::to_string ( getTypeIndex ( ) );
}

std::shared_ptr < abstraction::Value > Value::clone ( abstraction::TypeQualifiers::TypeQualifierSet typeQualifiers, bool isTemporary ) {
	if ( TypeQualifiers::isRef ( typeQualifiers ) ) {
		if ( TypeQualifiers::isConst ( this->getTypeQualifiers ( ) ) && ! TypeQualifiers::isConst ( typeQualifiers ) )
			throw std::domain_error ( "Cannot bind const value to non-const reference." );
		if ( ! this->isTemporary ( ) && isTemporary )
			throw std::domain_error ( "Cannot bind without move." );
		return std::make_shared < abstraction::ValueReference > ( this->getProxyAbstraction ( ), typeQualifiers, isTemporary );
	} else {
		return this->asValue ( this->isTemporary ( ), isTemporary );
	}
}


std::shared_ptr < abstraction::Value > ValueReference::asValue ( bool move, bool isTemporary ) {
	return getProxyAbstraction ( )->asValue ( move, isTemporary );
}

ValueReference::ValueReference ( std::shared_ptr < abstraction::Value > value, abstraction::TypeQualifiers::TypeQualifierSet typeQualifiers, bool isTemporary ) : m_value ( value ), m_typeQualifiers ( typeQualifiers ), m_isTemporary ( isTemporary ) {
	if ( ! abstraction::TypeQualifiers::isRef ( typeQualifiers ) )
		throw std::domain_error ( "Reference qualifier required" );
	if ( TypeQualifiers::isLvalueRef ( m_typeQualifiers ) && m_isTemporary )
		throw std::domain_error ( "Lvalue references cannot be temporaries." );
}

std::shared_ptr < abstraction::Value > ValueReference::getProxyAbstraction ( ) {
	if ( m_value.expired ( ) )
		throw std::domain_error ( "Use of expired reference" );
	return m_value.lock ( )->getProxyAbstraction ( );
}

abstraction::TypeQualifiers::TypeQualifierSet ValueReference::getTypeQualifiers ( ) const {
	return m_typeQualifiers;
}

ext::type_index ValueReference::getTypeIndex ( ) const {
	if ( m_value.expired ( ) )
		throw std::domain_error ( "Use of expired reference" );
	return m_value.lock ( )->getTypeIndex ( );
}

bool ValueReference::isTemporary ( ) const {
	return m_isTemporary;
}


std::shared_ptr < abstraction::Value > Void::asValue ( bool, bool ) {
	throw std::domain_error ( "Void variables are not allowed" );
}

ext::type_index Void::getTypeIndex ( ) const {
	return ext::type_index ( typeid ( void ) );
}

abstraction::TypeQualifiers::TypeQualifierSet Void::getTypeQualifiers ( ) const {
	return abstraction::TypeQualifiers::typeQualifiers < void > ( );
}

bool Void::isTemporary ( ) const {
	return false;
}

} /* namespace abstraction */
