//
// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Tables.cc --
//
// Author           : Andrew Beach
// Created On       : Mon Aug 31 11:11:00 2020
// Last Modified By : Andrew Beach
// Last Modified On : Tue Sep  3 14:56:00 2020
// Update Count     : 0
//

#include <SynTree/Declaration.h>
#include <SynTree/Expression.h>
#include <SynTree/Type.h>

namespace Virtual {

std::string vtableTypeName( std::string const & name ) {
	return name + "_vtable";
}

std::string instanceName( std::string const & name ) {
	return std::string("_") + name + "_instance";
}

std::string vtableInstanceName( std::string const & name ) {
	return instanceName( vtableTypeName( name ) );
}

bool isVTableInstanceName( std::string const & name ) {
	// There are some delicate length calculations here.
	return 17 < name.size() && '_' == name[0] &&
		std::string("_vtable_instance") == name.substr(1, name.size() - 17);
}

// Fuse base polymorphic declaration and forall arguments into a new type.
static StructInstType * vtableInstType(
		StructDecl * polyDecl, std::list< Expression * > && parameters ) {
	assert( parameters.size() == polyDecl->parameters.size() );
	StructInstType * type = new StructInstType(
			Type::Qualifiers( /* Type::Const */ ), polyDecl );
	type->parameters = std::move( parameters );
	return type;
}

static ObjectDecl * makeVtableDeclaration(
		StructInstType * type, Initializer * init ) {
	std::string const & name = instanceName( type->name );
	Type::StorageClasses storage = noStorageClasses;
	if ( nullptr == init ) {
		storage.is_extern = true;
	}
	return new ObjectDecl(
		name,
		storage,
		LinkageSpec::Cforall,
		nullptr,
		type,
		init
	);
}

ObjectDecl * makeVtableForward( StructInstType * type ) {
	return makeVtableDeclaration( type, nullptr );
}

ObjectDecl * makeVtableForward(
		StructDecl * polyDecl, std::list< Expression * > && parameters ) {
	return makeVtableForward( vtableInstType( polyDecl, std::move( parameters ) ) );
}

ObjectDecl * makeVtableInstance(
		StructInstType * vtableType, Type * vobject_type, Initializer * init ) {
	StructDecl * vtableStruct = vtableType->baseStruct;
	// Build the initialization
	if ( nullptr == init ) {
		std::list< Initializer * > inits;

		// This is going to have to be run before the resolver to connect expressions.
		for ( auto field : vtableStruct->members ) {
			if ( std::string( "parent" ) == field->name ) {
				// This will not work with polymorphic state.
				auto oField = strict_dynamic_cast< ObjectDecl * >( field );
				auto fieldType = strict_dynamic_cast< PointerType * >( oField->type );
				auto parentType = strict_dynamic_cast< StructInstType * >( fieldType->base );
				std::string const & parentInstance = instanceName( parentType->name );
				inits.push_back(
						new SingleInit( new AddressExpr( new NameExpr( parentInstance ) ) ) );
			} else if ( std::string( "size" ) == field->name ) {
				inits.push_back( new SingleInit( new SizeofExpr( vobject_type->clone() ) ) );
			} else if ( std::string( "align" ) == field->name ) {
				inits.push_back( new SingleInit( new AlignofExpr( vobject_type->clone() ) ) );
			} else {
				inits.push_back( new SingleInit( new NameExpr( field->name ) ) );
			}
		}
		init = new ListInit( inits );
	// This should initialize everything except the parent pointer, the
	// size-of and align-of fields. These should be inserted.
	} else {
		assert(false);
	}
	return makeVtableDeclaration( vtableType, init );
}

ObjectDecl * makeVtableInstance(
		StructDecl * polyDecl, std::list< Expression * > && parameters,
		Type * vobject, Initializer * init ) {
	return makeVtableInstance(
		vtableInstType( polyDecl, std::move( parameters ) ), vobject, init );
}

}
