// TextFileNode.cpp
//------------------------------------------------------------------------------

// Includes
//------------------------------------------------------------------------------
#include "TextFileNode.h"

// FBuildCore
#include "Tools/FBuild/FBuildCore/BFF/Functions/Function.h"
#include "Tools/FBuild/FBuildCore/FBuild.h"
#include "Tools/FBuild/FBuildCore/FLog.h"
#include "Tools/FBuild/FBuildCore/Graph/DirectoryListNode.h"
#include "Tools/FBuild/FBuildCore/Graph/NodeGraph.h"

// Core
#include "Core/Env/ErrorFormat.h"
#include "Core/FileIO/FileIO.h"
#include "Core/FileIO/FileStream.h"
#include "Core/Math/Conversions.h"
#include "Core/Process/Process.h"
#include "Core/Strings/AStackString.h"

// Reflection
//------------------------------------------------------------------------------
REFLECT_NODE_BEGIN( TextFileNode, Node, MetaName( "TextFileOutput" ) + MetaFile() )
    REFLECT( m_TextFileInputStrings, MetaRequired() )
    REFLECT( m_TextFileAlways )
    REFLECT( m_Hidden )
    REFLECT_RENAME( m_PreBuildDependencyNames, "PreBuildDependencies", MetaFile() + MetaAllowNonFile() )
REFLECT_END( TextFileNode )

// CONSTRUCTOR
//------------------------------------------------------------------------------
TextFileNode::TextFileNode()
    : FileNode()
    , m_TextFileAlways( false )
{
    m_Type = TEXT_FILE_NODE;
    m_LastBuildTimeMs = 10; // higher default than a file node
}

// Initialize
//------------------------------------------------------------------------------
/*virtual*/ bool TextFileNode::Initialize( NodeGraph & /*nodeGraph*/, const BFFToken * /*iter*/, const Function * /*function*/ )
{
    // .PreBuildDependencies
    m_PreBuildDependencies.Add( m_PreBuildDependencyNames );

    return true;
}

// DESTRUCTOR
//------------------------------------------------------------------------------
TextFileNode::~TextFileNode() = default;

// DetermineNeedToBuildStatic
//------------------------------------------------------------------------------
/*virtual*/ bool TextFileNode::DetermineNeedToBuildStatic() const
{
    if ( m_TextFileAlways )
    {
        FLOG_VERBOSE( "Need to build '%s' (TextFileAlways = true)", GetName().Get() );
        return true;
    }
    return Node::DetermineNeedToBuildStatic();
}

// DoBuild
//------------------------------------------------------------------------------
/*virtual*/ Node::BuildResult TextFileNode::DoBuild( Job * /*job*/ )
{
    EmitCompilationMessage();

    // Generate the file contents
    AStackString<4096> textFileContents;
    for ( const AString & string : m_TextFileInputStrings )
    {
        textFileContents += string;

        // It's not always safe to include a \r, such as when generating a shell script
#if defined( __WINDOWS__ )
        textFileContents += "\r\n";
#else
        textFileContents += '\n';
#endif
    }

    FileStream stream;
    if ( !stream.Open( GetName().Get(), FileStream::WRITE_ONLY ) )
    {
        FLOG_ERROR( "Could not open '%s' for writing", GetName().Get() );
        return BuildResult::eFailed;
    }
    const uint64_t nWritten = stream.WriteBuffer( textFileContents.Get(), textFileContents.GetLength() );
    stream.Close();

    if ( nWritten != textFileContents.GetLength() )
    {
        FLOG_ERROR( "Failed to write all to '%s'", GetName().Get() );
        return BuildResult::eFailed;
    }

    Node::RecordStampFromBuiltFile();

    return BuildResult::eOk;
}

// EmitCompilationMessage
//------------------------------------------------------------------------------
void TextFileNode::EmitCompilationMessage() const
{
    if ( FBuild::Get().GetOptions().m_ShowCommandSummary )
    {
        AStackString output;
        output += "Txt: ";
        output += GetName();
        output += '\n';

        // output all at once for contiguousness
        FLOG_OUTPUT( output );
    }
}

//------------------------------------------------------------------------------
