// Initialize
//------------------------------------------------------------------------------
bool XCodeProjectNode::Initialize( NodeGraph & nodeGraph, const BFFIterator & iter, const Function * function )
{
	ProjectGeneratorBase::FixupAllowedFileExtensions( m_ProjectAllowedFileExtensions );

	Dependencies dirNodes( m_ProjectInputPaths.GetSize() );
	if ( !function->GetDirectoryListNodeList( nodeGraph, iter, m_ProjectInputPaths, m_ProjectInputPathsExclude, m_ProjectFilesToExclude, true, &m_ProjectAllowedFileExtensions, "ProjectInputPaths", dirNodes ) )
	{
		return false; // GetDirectoryListNodeList will have emitted an error
	}

	// TODO:B use m_ProjectFiles instead of finding it again
	Dependencies fileNodes( m_ProjectFiles.GetSize() );
	if ( !function->GetNodeList( nodeGraph, iter, ".ProjectFiles", fileNodes ) )
	{
		return false; // GetNodeList will have emitted an error		
	}

	ASSERT( m_StaticDependencies.IsEmpty() );
	m_StaticDependencies.Append( dirNodes );
	m_StaticDependencies.Append( fileNodes );

	return true;
}
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionVCXProject::Commit( const BFFIterator & funcStartIter ) const
{
	// required
	AStackString<> projectOutput;
	AStackString<> rootNamespace;
	AStackString<> projectGuid;
	AStackString<> defaultLanguage;
	AStackString<> applicationEnvironment;
	if ( !GetString( funcStartIter, projectOutput,		".ProjectOutput", true ) ||
		 !GetString( funcStartIter, rootNamespace,		".RootNamespace", false ) ||
		 !GetString( funcStartIter, projectGuid,		".ProjectGuid", false ) ||
		 !GetString( funcStartIter, defaultLanguage,	".DefaultLanguage", false ) ||
		 !GetString( funcStartIter, applicationEnvironment,	".ApplicationEnvironment", false ) )
	{
		return false;
	}

	// optional inputs
	Array< AString > inputPaths;
	Array< AString > inputPathsExclude;
	if ( !GetStrings( funcStartIter, inputPaths,		".ProjectInputPaths", false ) ||
		 !GetStrings( funcStartIter, inputPathsExclude,	".ProjectInputPathsExclude", false ) )
	{
		return false;
	}

	// project base
	Array< AString > basePaths;
	if ( !GetStrings( funcStartIter, basePaths,	".ProjectBasePath", false ) )
	{
		return false;
	}
	CleanFolderPaths( basePaths );

	// references
	Array< AString > references;
	Array< AString > projectReferences;
	if ( !GetStrings( funcStartIter, references,		".ProjectReferences", false ) ||
		 !GetStrings( funcStartIter, projectReferences,	".ProjectProjectReferences", false ) )
	{
		return false;
	}

	// permitted file extensions
	Array< AString > allowedFileExtensions( 8, true );
	if ( !GetStrings( funcStartIter, allowedFileExtensions, ".ProjectAllowedFileExtensions", false ) )
	{
		return true;
	}
	if ( allowedFileExtensions.IsEmpty() )
	{
		const char * extensions[] = { ".cpp", ".hpp", ".cxx",".hxx",".c",".h",".cc",".hh",
									  ".cp",".hp",".cs",".inl",".bff",".rc",".resx",".m",".mm",
									  ".cu",
									  nullptr };
		AStackString<> tmp;
		const char ** item = extensions;
		while ( *item )
		{
			tmp.Assign( *item );
			allowedFileExtensions.Append( tmp );
			++item;
		}
	}

	// files and filesToExclude
	Array< AString > files( 8, true );
	Array< AString > filesToExclude( 8, true );	
	if ( !GetStrings( funcStartIter, files,				".ProjectFiles", false ) ||
 		 !GetStrings( funcStartIter, filesToExclude,	".ProjectFilesToExclude", false ) )
	{
		return false;
	}

	// filetypes
	Array< VSProjectFileType > fileTypes;
	const BFFVariable * projectFileTypes = BFFStackFrame::GetVar( ".ProjectFileTypes" );
	if ( projectFileTypes )
	{
		if ( projectFileTypes->IsArrayOfStructs() == false )
		{
			Error::Error_1050_PropertyMustBeOfType( funcStartIter, this, ".ProjectFileTypes", projectFileTypes->GetType(), BFFVariable::VAR_ARRAY_OF_STRUCTS );
			return false;
		}

		const Array< const BFFVariable * > & structs = projectFileTypes->GetArrayOfStructs();
		const BFFVariable * const * end = structs.End();
		for ( const BFFVariable ** it = structs.Begin(); it != end; ++it )
		{
			const BFFVariable * s = *it;

			VSProjectFileType ft;

			// .FileType must be provided
			if ( !GetStringFromStruct( s, ".FileType",	ft.m_FileType ) )
			{
				// TODO:B custom error
				Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".FileType" ) );
				return false;
			}

			// .Pattern must be provided
			if ( !GetStringFromStruct( s, ".Pattern",	ft.m_Pattern ) )
			{
				// TODO:B custom error
				Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".Pattern" ) );
				return false;
			}

			fileTypes.Append( ft );
		}
	}

	// path cleaning
	CleanFolderPaths( inputPaths );			// input paths
	CleanFolderPaths( inputPathsExclude );	// exclude paths
	CleanFilePaths( files );				// explicit files

	// per-config options
	VSProjectConfig baseConfig;

	// various options
	if ( !GetString( funcStartIter, baseConfig.m_BuildCommand,	".ProjectBuildCommand", false ) ||
		 !GetString( funcStartIter, baseConfig.m_RebuildCommand,".ProjectRebuildCommand", false ) ||
		 !GetString( funcStartIter, baseConfig.m_CleanCommand,	".ProjectCleanCommand", false ) ||
		 !GetString( funcStartIter, baseConfig.m_Output,		".Output", false ) ||
		 !GetString( funcStartIter, baseConfig.m_PreprocessorDefinitions,	".PreprocessorDefinitions", false ) ||
		 !GetString( funcStartIter, baseConfig.m_IncludeSearchPath,		".IncludeSearchPath", false ) ||
		 !GetString( funcStartIter, baseConfig.m_ForcedIncludes,		".ForcedIncludes", false ) ||
		 !GetString( funcStartIter, baseConfig.m_AssemblySearchPath,	".AssemblySearchPath", false ) ||
		 !GetString( funcStartIter, baseConfig.m_ForcedUsingAssemblies,	".ForcedUsingAssemblies", false ) ||
		 !GetString( funcStartIter, baseConfig.m_AdditionalOptions,		".AdditionalOptions", false ) ||
		 !GetString( funcStartIter, baseConfig.m_OutputDirectory,		".OutputDirectory", false ) ||
		 !GetString( funcStartIter, baseConfig.m_IntermediateDirectory,	".IntermediateDirectory", false ) ||
		 !GetString( funcStartIter, baseConfig.m_Xbox360DebuggerCommand,".Xbox360DebuggerCommand", false ) ||
		 !GetString( funcStartIter, baseConfig.m_LayoutDir,				".LayoutDir", false ) ||
		 !GetString( funcStartIter, baseConfig.m_LayoutExtensionFilter,	".LayoutExtensionFilter", false ) ||
		 !GetString( funcStartIter, baseConfig.m_DebuggerFlavor,		".DebuggerFlavor", false ) ||
		 !GetString( funcStartIter, baseConfig.m_AumidOverride,			".AumidOverride", false ) ||
		 !GetString( funcStartIter, baseConfig.m_PlatformToolset,		".PlatformToolset", false ) ||
		 !GetString( funcStartIter, baseConfig.m_DeploymentType,		".DeploymentType", false ) ||
		 !GetString( funcStartIter, baseConfig.m_DeploymentFiles,		".DeploymentFiles", false ) ||
		 !GetString( funcStartIter, baseConfig.m_LocalDebuggerCommandArguments,	".LocalDebuggerCommandArguments", false ) ||
		 !GetString( funcStartIter, baseConfig.m_LocalDebuggerWorkingDirectory,	".LocalDebuggerWorkingDirectory", false ) ||
		 !GetString( funcStartIter, baseConfig.m_LocalDebuggerCommand,			".LocalDebuggerCommand", false ) ||
		 !GetString( funcStartIter, baseConfig.m_LocalDebuggerEnvironment,		".LocalDebuggerEnvironment", false ) )
	{
		return false;
	}

	// create configs
	Array< VSProjectConfig > configs( 16, true );

	const BFFVariable * projectConfigs = BFFStackFrame::GetVar( ".ProjectConfigs" );
	if ( projectConfigs )
	{
		if ( projectConfigs->IsArrayOfStructs() == false )
		{
			Error::Error_1050_PropertyMustBeOfType( funcStartIter, this, ".ProjectConfigs", projectConfigs->GetType(), BFFVariable::VAR_ARRAY_OF_STRUCTS );
			return false;
		}

		const Array< const BFFVariable * > & structs = projectConfigs->GetArrayOfStructs();
		const BFFVariable * const * end = structs.End();
		for ( const BFFVariable ** it = structs.Begin(); it != end; ++it )
		{
			const BFFVariable * s = *it;

			// start with the base configuration
			VSProjectConfig newConfig( baseConfig );

			// .Platform must be provided
			if ( !GetStringFromStruct( s, ".Platform",	newConfig.m_Platform ) )
			{
				// TODO:B custom error
				Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".Platform" ) );
				return false;
			}

			// .Config must be provided
			if ( !GetStringFromStruct( s, ".Config",	newConfig.m_Config ) )
			{
				// TODO:B custom error
				Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".Config" ) );
				return false;
			}

			GetStringFromStruct( s, ".ProjectBuildCommand",		newConfig.m_BuildCommand );
			GetStringFromStruct( s, ".ProjectRebuildCommand",	newConfig.m_RebuildCommand );
			GetStringFromStruct( s, ".ProjectCleanCommand",		newConfig.m_CleanCommand );
			GetStringFromStruct( s, ".Output",					newConfig.m_Output );
			GetStringFromStruct( s, ".PreprocessorDefinitions",	newConfig.m_PreprocessorDefinitions );
			GetStringFromStruct( s, ".IncludeSearchPath",		newConfig.m_IncludeSearchPath );
			GetStringFromStruct( s, ".ForcedIncludes",			newConfig.m_ForcedIncludes );
			GetStringFromStruct( s, ".AssemblySearchPath",		newConfig.m_AssemblySearchPath );
			GetStringFromStruct( s, ".ForcedUsingAssemblies",	newConfig.m_ForcedUsingAssemblies );
			GetStringFromStruct( s, ".AdditionalOptions",		newConfig.m_AdditionalOptions );
			GetStringFromStruct( s, ".OutputDirectory",			newConfig.m_OutputDirectory );
			GetStringFromStruct( s, ".IntermediateDirectory",	newConfig.m_IntermediateDirectory );
		 	GetStringFromStruct( s, ".LayoutDir",				newConfig.m_LayoutDir );
			GetStringFromStruct( s, ".LayoutExtensionFilter",	newConfig.m_LayoutExtensionFilter );
			GetStringFromStruct( s, ".Xbox360DebuggerCommand",	newConfig.m_Xbox360DebuggerCommand );
			GetStringFromStruct( s, ".DebuggerFlavor",			newConfig.m_DebuggerFlavor );
			GetStringFromStruct( s, ".AumidOverride",			newConfig.m_AumidOverride );
			GetStringFromStruct( s, ".PlatformToolset",			newConfig.m_PlatformToolset );
			GetStringFromStruct( s, ".DeploymentType",			newConfig.m_DeploymentType );
			GetStringFromStruct( s, ".DeploymentFiles",			newConfig.m_DeploymentFiles );
			GetStringFromStruct( s, ".LocalDebuggerCommandArguments",	newConfig.m_LocalDebuggerCommandArguments );
			GetStringFromStruct( s, ".LocalDebuggerWorkingDirectory",	newConfig.m_LocalDebuggerWorkingDirectory );
			GetStringFromStruct( s, ".LocalDebuggerCommand",			newConfig.m_LocalDebuggerCommand );
			GetStringFromStruct( s, ".LocalDebuggerEnvironment",		newConfig.m_LocalDebuggerEnvironment );

			configs.Append( newConfig );
		}
	}
	else
	{
		// no user specified configs, make some defaults

		// start from the default
		VSProjectConfig config( baseConfig );

		// make the configs
		config.m_Platform	= "Win32";
		config.m_Config		= "Debug";
		configs.Append( config );
		config.m_Config		= "Release";
		configs.Append( config );
		config.m_Platform	= "x64";
		configs.Append( config );
		config.m_Config		= "Debug";
		configs.Append( config );
	}

	NodeGraph & ng = FBuild::Get().GetDependencyGraph();

	// create all of the DirectoryListNodes we need
	Dependencies dirNodes( inputPaths.GetSize() );
	if ( !GetDirectoryListNodeList( funcStartIter, inputPaths, Array< AString >(), Array< AString >(), true, nullptr, "ProjectInputPaths", dirNodes ) )
	{
		return false; // GetDirectoryListNodeList will have emitted an error
	}

	// Check for existing node
	if ( ng.FindNode( projectOutput ) )
	{
		Error::Error_1100_AlreadyDefined( funcStartIter, this, projectOutput );
		return false;
	}

	VCXProjectNode * pn = ng.CreateVCXProjectNode( projectOutput,
												   basePaths,
												   dirNodes,
												   inputPathsExclude, // TODO:B Remove this (handled by DirectoryListNode now)
												   allowedFileExtensions, // TODO:B Remove this (handled by DirectoryListNode now)
												   files,
												   filesToExclude,
												   rootNamespace,
												   projectGuid,
												   defaultLanguage,
												   applicationEnvironment,
												   configs,
												   fileTypes,
												   references,
												   projectReferences );

	ASSERT( pn );

	return ProcessAlias( funcStartIter, pn );
}
// GetInputs
//------------------------------------------------------------------------------
bool FunctionObjectList::GetInputs( const BFFIterator & iter, Dependencies & inputs ) const
{
	NodeGraph & ng = FBuild::Get().GetDependencyGraph();

	// do we want to build files via a unity blob?
    Array< AString > inputUnities;
    if ( !GetStrings( iter, inputUnities, ".CompilerInputUnity", false ) ) // not required
	{
		return false;
	}
    for ( const auto& unity : inputUnities )
    {
        Node * n = ng.FindNode( unity );
		if ( n == nullptr )
		{
			Error::Error_1104_TargetNotDefined( iter, this, "CompilerInputUnity", unity );
			return false;
		}
		if ( n->GetType() != Node::UNITY_NODE )
		{
			Error::Error_1102_UnexpectedType( iter, this, "CompilerInputUnity", unity, n->GetType(), Node::UNITY_NODE );
			return false;
		}
		inputs.Append( Dependency( n ) );
    }

	// do we want to build a files in a directory?
	const BFFVariable * inputPath = BFFStackFrame::GetVar( ".CompilerInputPath" );
	if ( inputPath )
	{
		// get the optional pattern and recurse options related to InputPath
		Array< AString > patterns;
		if ( !GetStrings( iter, patterns, ".CompilerInputPattern", false ) )
		{
			return false; // GetString will have emitted an error
		}
		if ( patterns.IsEmpty() )
		{
			patterns.Append( AStackString<>( "*.cpp" ) );
		}

		// recursive?  default to true
		bool recurse = true;
		if ( !GetBool( iter, recurse, ".CompilerInputPathRecurse", true, false ) )
		{
			return false; // GetBool will have emitted an error
		}

		// Support an exclusion path
		Array< AString > excludePaths;
		if ( !GetFolderPaths( iter, excludePaths, ".CompilerInputExcludePath", false ) )
		{
			return false; // GetFolderPaths will have emitted an error
		}

        Array< AString > filesToExclude;
        if ( !GetStrings( iter, filesToExclude, ".CompilerInputExcludedFiles", false ) ) // not required
        {
            return false; // GetStrings will have emitted an error
        }
	    CleanFileNames( filesToExclude );

		// Input paths
		Array< AString > inputPaths;
		if ( !GetFolderPaths( iter, inputPaths, ".CompilerInputPath", false ) )
		{
			return false; // GetFolderPaths will have emitted an error
		}

		Dependencies dirNodes( inputPaths.GetSize() );
		if ( !GetDirectoryListNodeList( iter, inputPaths, excludePaths, filesToExclude, recurse, &patterns, "CompilerInputPath", dirNodes ) )
		{
			return false; // GetDirectoryListNodeList will have emitted an error
		}
		inputs.Append( dirNodes );
	}

	// do we want to build a specific list of files?
	if ( !GetNodeList( iter, ".CompilerInputFiles", inputs, false ) )
	{
		// helper will emit error
		return false;
	}

	return true;
}
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionCSAssembly::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const
{
	// make sure all required variables are defined
	const BFFVariable * compiler;
	const BFFVariable * compilerOptions;
	const BFFVariable * compilerOutput;
	if ( !GetString( funcStartIter, compiler, ".Compiler", true ) ||
		 !GetString( funcStartIter, compilerOptions, ".CompilerOptions", true ) ||
		 !GetString( funcStartIter, compilerOutput, ".CompilerOutput", true ) )
	{
		return false;
	}

	Dependencies staticDeps( 32, true );

	// do we want to build a files in a directory?
	const BFFVariable * inputPath = BFFStackFrame::GetVar( ".CompilerInputPath" );
	if ( inputPath )
	{
		// get the optional pattern and recurse options related to InputPath
		Array< AString > patterns;
		if ( !GetStrings( funcStartIter, patterns, ".CompilerInputPattern", false ) )
		{
			return false; // GetString will have emitted an error
		}
		if ( patterns.IsEmpty() )
		{
			patterns.Append( AStackString<>( "*.cs" ) );
		}

		// recursive?  default to true
		bool recurse = true;
		if ( !GetBool( funcStartIter, recurse, ".CompilerInputPathRecurse", true, false ) )
		{
			return false; // GetBool will have emitted an error
		}

		// Support an exclusion path
		Array< AString > excludePaths;
		if ( !GetFolderPaths( funcStartIter, excludePaths, ".CompilerInputExcludePath", false ) )
		{
			return false; // GetFolderPaths will have emitted an error
		}

        Array< AString > filesToExclude(0, true);
        if ( !GetStrings( funcStartIter, filesToExclude, ".CompilerInputExcludedFiles", false ) ) // not required
        {
            return false; // GetStrings will have emitted an error
        }
	    CleanFileNames( filesToExclude );

		// Input paths
		Array< AString > inputPaths;
		if ( !GetFolderPaths( funcStartIter, inputPaths, ".CompilerInputPath", false ) )
		{
			return false; // GetFolderPaths will have emitted an error
		}

		Dependencies dirNodes( inputPaths.GetSize() );
		if ( !GetDirectoryListNodeList( nodeGraph, funcStartIter, inputPaths, excludePaths, filesToExclude, recurse, &patterns, "CompilerInputPath", dirNodes ) )
		{
			return false; // GetDirectoryListNodeList will have emitted an error
		}
		staticDeps.Append( dirNodes );
	}

	// do we want to build a specific list of files?
	if ( !GetNodeList( nodeGraph, funcStartIter, ".CompilerInputFiles", staticDeps, false ) )
	{
		// helper will emit error
		return false;
	}

	if ( staticDeps.IsEmpty() )
	{
		Error::Error_1006_NothingToBuild( funcStartIter, this );
		return false;
	}

	// additional references?
	Dependencies extraRefs( 0, true );
	if ( !GetNodeList( nodeGraph, funcStartIter, ".CompilerReferences", extraRefs, false ) )
	{
		// helper function will have emitted an error
		return false;
	}

    // Pre-build dependencies
	Dependencies preBuildDependencies;
	if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDependencies, false ) )
	{
		return false; // GetNodeList will have emitted an error
	}

	// Create library node which depends on the single file or list
	if ( nodeGraph.FindNode( compilerOutput->GetString() ) )
	{
		Error::Error_1100_AlreadyDefined( funcStartIter, this, compilerOutput->GetString() );
		return false;
	}
	Node * csNode = nodeGraph.CreateCSNode( compilerOutput->GetString(),
									 staticDeps,
									 compiler->GetString(),
									 compilerOptions->GetString(),
									 extraRefs,
									 preBuildDependencies );

	// should we create an alias?
	return ProcessAlias( nodeGraph, funcStartIter, csNode );
}