//
// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Convert.cpp --
//
// Author           : Thierry Delisle
// Created On       : Thu May 09 15::37::05 2019
// Last Modified By :
// Last Modified On :
// Update Count     :
//

#include "Convert.hpp"

#include "AST/Pass.hpp"

#include "AST/Attribute.hpp"
#include "AST/Decl.hpp"
#include "AST/Expr.hpp"
#include "AST/Init.hpp"
#include "AST/Stmt.hpp"


#include "SynTree/Attribute.h"
#include "SynTree/Declaration.h"

//================================================================================================
// Utilities
template<template <class...> class C>
struct to {
	template<typename T>
	static auto from( T && v ) -> C< typename T::value_type > {
		C< typename T::value_type > l;
		std::move(std::begin(v), std::end(v), std::back_inserter(l));
		return l;
	}
};

//================================================================================================
class ConverterNewToOld {
public:
	std::list< Declaration * > translationUnit;
};

std::list< Declaration * > convert( std::list< ast::ptr< ast::Decl > > & translationUnit ) {
	ast::Pass<ConverterNewToOld> converter;
	ast::accept_all(translationUnit, converter);
	return converter.pass.translationUnit;
}

//================================================================================================

#define ACCEPT_1(name, child, type) \
	old->child->accept(*this); \
	auto name = strict_dynamic_cast< ast::type * >( node );

#define ACCEPT_N(name, child, type) \
	std::vector< ast::ptr<ast::type> > name; \
	name.reserve( old->child.size() ); \
	for( auto a : old->child ) { \
		a->accept( *this ); \
		name.emplace_back( strict_dynamic_cast< ast::type * >(node) ); \
	}

class ConverterOldToNew : public Visitor {
public:
	ast::Decl * decl() {
		return strict_dynamic_cast< ast::Decl * >( node );
	}
private:
	ast::Node * node;

	template<template <class...> class C>
	C<ast::Label> make_labels(C<Label> olds) {
		C<ast::Label> ret;
		for(auto oldn : olds) {
			auto old = &oldn; // to reuse the MACRO
			ACCEPT_N(attr, attributes, Attribute)
			ast::Label l(
				{},
				old->get_name(),
				to<std::vector>::from( std::move( attr ) )
			);
			ret.push_back( l );
		}
		return ret;
	}

	virtual void visit( ObjectDecl * old ) override final {
		ACCEPT_1(type, type, Type)
		ACCEPT_1(init, init, Init)
		ACCEPT_1(bitWd, bitfieldWidth, Expr)
		ACCEPT_N(attr, attributes, Attribute)

		auto decl = new ast::ObjectDecl(
			old->location,
			old->name,
			type,
			init,
			{ old->get_storageClasses().val },
			{ old->linkage.val },
			bitWd,
			std::move( attr ),
			{ old->get_funcSpec().val }
		);
		decl->scopeLevel = old->scopeLevel;
		decl->mangleName = old->mangleName;
		decl->isDeleted  = old->isDeleted;
		decl->uniqueId   = old->uniqueId;
		decl->extension  = old->extension;

		this->node = decl;
	}

	virtual void visit( FunctionDecl * ) override final {

	}

	virtual void visit( StructDecl * old ) override final {
		ACCEPT_N(members, members, Decl)
		ACCEPT_N(params, parameters, TypeDecl)
		ACCEPT_1(parent, parent, AggregateDecl)
		ACCEPT_N(attr, attributes, Attribute)

		auto decl = new ast::StructDecl(
			old->location,
			old->name,
			old->kind,
			std::move( attr ),
			{ old->linkage.val }
		);
		decl->parent = parent;
		decl->body   = old->body;
		decl->parameters = params;
		decl->members    = members;
		decl->extension  = old->extension;
		decl->uniqueId   = old->uniqueId;
		decl->storage    = { old->storageClasses.val };

		this->node = decl;
	}

	virtual void visit( UnionDecl * old ) override final {
		ACCEPT_N(members, members, Decl)
		ACCEPT_N(params, parameters, TypeDecl)
		ACCEPT_1(parent, parent, AggregateDecl)
		ACCEPT_N(attr, attributes, Attribute)

		auto decl = new ast::UnionDecl(
			old->location,
			old->name,
			std::move( attr ),
			{ old->linkage.val }
		);
		decl->parent = parent;
		decl->body   = old->body;
		decl->parameters = params;
		decl->members    = members;
		decl->extension  = old->extension;
		decl->uniqueId   = old->uniqueId;
		decl->storage    = { old->storageClasses.val };

		this->node = decl;
	}

	virtual void visit( EnumDecl * old ) override final {
		ACCEPT_N(members, members, Decl)
		ACCEPT_N(params, parameters, TypeDecl)
		ACCEPT_1(parent, parent, AggregateDecl)
		ACCEPT_N(attr, attributes, Attribute)

		auto decl = new ast::UnionDecl(
			old->location,
			old->name,
			std::move( attr ),
			{ old->linkage.val }
		);
		decl->parent = parent;
		decl->body   = old->body;
		decl->parameters = params;
		decl->members    = members;
		decl->extension  = old->extension;
		decl->uniqueId   = old->uniqueId;
		decl->storage    = { old->storageClasses.val };

		this->node = decl;
	}

	virtual void visit( TraitDecl * old ) override final {
		ACCEPT_N(members, members, Decl)
		ACCEPT_N(params, parameters, TypeDecl)
		ACCEPT_1(parent, parent, AggregateDecl)
		ACCEPT_N(attr, attributes, Attribute)

		auto decl = new ast::UnionDecl(
			old->location,
			old->name,
			std::move( attr ),
			{ old->linkage.val }
		);
		decl->parent = parent;
		decl->body   = old->body;
		decl->parameters = params;
		decl->members    = members;
		decl->extension  = old->extension;
		decl->uniqueId   = old->uniqueId;
		decl->storage    = { old->storageClasses.val };

		this->node = decl;
	}

	virtual void visit( TypeDecl * ) override final {

	}

	virtual void visit( TypedefDecl * old ) override final {
		ACCEPT_1(type, base, Type)
		ACCEPT_N(params, parameters, TypeDecl)
		ACCEPT_N(asserts, assertions, DeclWithType)
		auto decl = new ast::TypedefDecl(
			old->location,
			old->name,
			{ old->storageClasses.val },
			type,
			{ old->linkage.val }
		);

		decl->assertions = asserts;
		decl->parameters = params;
		decl->extension  = old->extension;
		decl->uniqueId   = old->uniqueId;
		decl->storage    = { old->storageClasses.val };

		this->node = decl;
	}

	virtual void visit( AsmDecl * ) override final {

	}

	virtual void visit( StaticAssertDecl * ) override final {

	}

	virtual void visit( CompoundStmt * old ) override final {
		ACCEPT_N(kids, kids, Stmt)
		auto stmt = new ast::CompoundStmt(
			old->location,
			to<std::list>::from( std::move(kids) )
		);
		stmt->labels = to<std::vector>::from( make_labels( std::move( old->labels ) ) );

		this->node = stmt;
	}

	virtual void visit( ExprStmt * old ) override final {
		ACCEPT_1(expr, expr, Expr)
		auto stmt = new ast::ExprStmt(
			old->location,
			expr
		);
		stmt->labels = to<std::vector>::from( make_labels( std::move( old->labels ) ) );

		this->node = stmt;
	}

	virtual void visit( AsmStmt * ) override final {

	}

	virtual void visit( DirectiveStmt * ) override final {

	}

	virtual void visit( IfStmt * ) override final {

	}

	virtual void visit( WhileStmt * ) override final {

	}

	virtual void visit( ForStmt * ) override final {

	}

	virtual void visit( SwitchStmt * ) override final {

	}

	virtual void visit( CaseStmt * ) override final {

	}

	virtual void visit( BranchStmt * ) override final {

	}

	virtual void visit( ReturnStmt * ) override final {

	}

	virtual void visit( ThrowStmt * ) override final {

	}

	virtual void visit( TryStmt * ) override final {

	}

	virtual void visit( CatchStmt * ) override final {

	}

	virtual void visit( FinallyStmt * ) override final {

	}

	virtual void visit( WaitForStmt * ) override final {

	}

	virtual void visit( WithStmt * ) override final {

	}

	virtual void visit( NullStmt * old ) override final {
		auto stmt = new ast::NullStmt(
			old->location,
			to<std::vector>::from( make_labels( std::move( old->labels ) ) )
		);

		this->node = stmt;
	}

	virtual void visit( DeclStmt * ) override final {

	}

	virtual void visit( ImplicitCtorDtorStmt * ) override final {

	}

	virtual void visit( ApplicationExpr * ) override final {

	}

	virtual void visit( UntypedExpr * ) override final {

	}

	virtual void visit( NameExpr * ) override final {

	}

	virtual void visit( CastExpr * ) override final {

	}

	virtual void visit( KeywordCastExpr * ) override final {

	}

	virtual void visit( VirtualCastExpr * ) override final {

	}

	virtual void visit( AddressExpr * ) override final {

	}

	virtual void visit( LabelAddressExpr * ) override final {

	}

	virtual void visit( UntypedMemberExpr * ) override final {

	}

	virtual void visit( MemberExpr * ) override final {

	}

	virtual void visit( VariableExpr * ) override final {

	}

	virtual void visit( ConstantExpr * ) override final {

	}

	virtual void visit( SizeofExpr * ) override final {

	}

	virtual void visit( AlignofExpr * ) override final {

	}

	virtual void visit( UntypedOffsetofExpr * ) override final {

	}

	virtual void visit( OffsetofExpr * ) override final {

	}

	virtual void visit( OffsetPackExpr * ) override final {

	}

	virtual void visit( AttrExpr * ) override final {

	}

	virtual void visit( LogicalExpr * ) override final {

	}

	virtual void visit( ConditionalExpr * ) override final {

	}

	virtual void visit( CommaExpr * ) override final {

	}

	virtual void visit( TypeExpr * ) override final {

	}

	virtual void visit( AsmExpr * ) override final {

	}

	virtual void visit( ImplicitCopyCtorExpr * ) override final {

	}

	virtual void visit( ConstructorExpr *  ) override final {

	}

	virtual void visit( CompoundLiteralExpr * ) override final {

	}

	virtual void visit( RangeExpr * ) override final {

	}

	virtual void visit( UntypedTupleExpr * ) override final {

	}

	virtual void visit( TupleExpr * ) override final {

	}

	virtual void visit( TupleIndexExpr * ) override final {

	}

	virtual void visit( TupleAssignExpr * ) override final {

	}

	virtual void visit( StmtExpr *  ) override final {

	}

	virtual void visit( UniqueExpr *  ) override final {

	}

	virtual void visit( UntypedInitExpr *  ) override final {

	}

	virtual void visit( InitExpr *  ) override final {

	}

	virtual void visit( DeletedExpr * ) override final {

	}

	virtual void visit( DefaultArgExpr * ) override final {

	}

	virtual void visit( GenericExpr * ) override final {

	}

	virtual void visit( VoidType * ) override final {

	}

	virtual void visit( BasicType * ) override final {

	}

	virtual void visit( PointerType * ) override final {

	}

	virtual void visit( ArrayType * ) override final {

	}

	virtual void visit( ReferenceType * ) override final {

	}

	virtual void visit( QualifiedType * ) override final {

	}

	virtual void visit( FunctionType * ) override final {

	}

	virtual void visit( StructInstType * ) override final {

	}

	virtual void visit( UnionInstType * ) override final {

	}

	virtual void visit( EnumInstType * ) override final {

	}

	virtual void visit( TraitInstType * ) override final {

	}

	virtual void visit( TypeInstType * ) override final {

	}

	virtual void visit( TupleType * ) override final {

	}

	virtual void visit( TypeofType * ) override final {

	}

	virtual void visit( AttrType * ) override final {

	}

	virtual void visit( VarArgsType * ) override final {

	}

	virtual void visit( ZeroType * ) override final {

	}

	virtual void visit( OneType * ) override final {

	}

	virtual void visit( GlobalScopeType * ) override final {

	}

	virtual void visit( Designation * ) override final {

	}

	virtual void visit( SingleInit * ) override final {

	}

	virtual void visit( ListInit * ) override final {

	}

	virtual void visit( ConstructorInit * ) override final {

	}

	virtual void visit( Constant * ) override final {

	}

	virtual void visit( Attribute * ) override final {

	}
};

#undef ACCEPT_N
#undef ACCEPT_1

std::list< ast::ptr< ast::Decl > > convert( const std::list< Declaration * > & translationUnit ) {
	ConverterOldToNew c;
	std::list< ast::ptr< ast::Decl > > decls;
	for(auto d : translationUnit) {
		d->accept( c );
		decls.emplace_back( c.decl() );
		delete d;
	}
	return decls;
}