//===-- SBStream.cpp ----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/API/SBStream.h"

#include "lldb/Core/Error.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StreamString.h"

using namespace lldb;
using namespace lldb_private;

SBStream::SBStream () :
    m_opaque_ap (new StreamString()),
    m_is_file (false)
{
}

SBStream::SBStream (SBStream &&rhs) :
    m_opaque_ap (std::move(rhs.m_opaque_ap)),
    m_is_file (rhs.m_is_file)
{
}


SBStream::~SBStream ()
{
}

bool
SBStream::IsValid() const
{
    return (m_opaque_ap.get() != NULL);
}

// If this stream is not redirected to a file, it will maintain a local
// cache for the stream data which can be accessed using this accessor.
const char *
SBStream::GetData ()
{
    if (m_is_file || m_opaque_ap.get() == NULL)
        return NULL;
    
    return static_cast<StreamString *>(m_opaque_ap.get())->GetData();
}

// If this stream is not redirected to a file, it will maintain a local
// cache for the stream output whose length can be accessed using this 
// accessor.
size_t
SBStream::GetSize()
{
    if (m_is_file || m_opaque_ap.get() == NULL)
        return 0;
    
    return static_cast<StreamString *>(m_opaque_ap.get())->GetSize();
}

void
SBStream::Printf (const char *format, ...)
{
    if (!format)
        return;
    va_list args;
    va_start (args, format);
    ref().PrintfVarArg (format, args);
    va_end (args);
}

void
SBStream::RedirectToFile (const char *path, bool append)
{
    if (path == nullptr)
        return;

    std::string local_data;
    if (m_opaque_ap.get())
    {
        // See if we have any locally backed data. If so, copy it so we can then
        // redirect it to the file so we don't lose the data
        if (!m_is_file)
            local_data.swap(static_cast<StreamString *>(m_opaque_ap.get())->GetString());
    }
    StreamFile *stream_file = new StreamFile;
    uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate;
    if (append)
        open_options |= File::eOpenOptionAppend;
    else
        open_options |= File::eOpenOptionTruncate;
    stream_file->GetFile().Open (path, open_options, lldb::eFilePermissionsFileDefault);

    m_opaque_ap.reset (stream_file);

    if (m_opaque_ap.get())
    {
        m_is_file = true;

        // If we had any data locally in our StreamString, then pass that along to 
        // the to new file we are redirecting to.
        if (!local_data.empty())
            m_opaque_ap->Write (&local_data[0], local_data.size());
    }
    else
        m_is_file = false;
}

void
SBStream::RedirectToFileHandle (FILE *fh, bool transfer_fh_ownership)
{
    if (fh == nullptr)
        return;

    std::string local_data;
    if (m_opaque_ap.get())
    {
        // See if we have any locally backed data. If so, copy it so we can then
        // redirect it to the file so we don't lose the data
        if (!m_is_file)
            local_data.swap(static_cast<StreamString *>(m_opaque_ap.get())->GetString());
    }
    m_opaque_ap.reset (new StreamFile (fh, transfer_fh_ownership));

    if (m_opaque_ap.get())
    {
        m_is_file = true;

        // If we had any data locally in our StreamString, then pass that along to 
        // the to new file we are redirecting to.
        if (!local_data.empty())
            m_opaque_ap->Write (&local_data[0], local_data.size());
    }
    else
        m_is_file = false;
}

void
SBStream::RedirectToFileDescriptor (int fd, bool transfer_fh_ownership)
{
    std::string local_data;
    if (m_opaque_ap.get())
    {
        // See if we have any locally backed data. If so, copy it so we can then
        // redirect it to the file so we don't lose the data
        if (!m_is_file)
            local_data.swap(static_cast<StreamString *>(m_opaque_ap.get())->GetString());
    }

    m_opaque_ap.reset (new StreamFile (::fdopen (fd, "w"), transfer_fh_ownership));
    if (m_opaque_ap.get())
    {
        m_is_file = true;

        // If we had any data locally in our StreamString, then pass that along to 
        // the to new file we are redirecting to.
        if (!local_data.empty())
            m_opaque_ap->Write (&local_data[0], local_data.size());
    }
    else
        m_is_file = false;

}

lldb_private::Stream *
SBStream::operator->()
{
    return m_opaque_ap.get();
}

lldb_private::Stream *
SBStream::get()
{
    return m_opaque_ap.get();
}

lldb_private::Stream &
SBStream::ref()
{
    if (m_opaque_ap.get() == NULL)
        m_opaque_ap.reset (new StreamString());
    return *m_opaque_ap.get();
}

void
SBStream::Clear ()
{
    if (m_opaque_ap.get())
    {
        // See if we have any locally backed data. If so, copy it so we can then
        // redirect it to the file so we don't lose the data
        if (m_is_file)
            m_opaque_ap.reset();
        else
            static_cast<StreamString *>(m_opaque_ap.get())->GetString().clear();
    }
}
