/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <log4cxx/logstring.h>
#include <log4cxx/helpers/fileoutputstream.h>
#include <log4cxx/helpers/pool.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/bytebuffer.h>
#include <apr_file_io.h>

using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::helpers;

struct FileOutputStream::FileOutputStreamPrivate
{
	FileOutputStreamPrivate(const LogString& filename)
		: path(filename)
		{}

	File path;
	Pool pool;
	apr_file_t* fileptr{ nullptr };
	void open(bool append);
};

IMPLEMENT_LOG4CXX_OBJECT(FileOutputStream)

FileOutputStream::FileOutputStream(const LogString& filename, bool append)
	: m_priv(std::make_unique<FileOutputStreamPrivate>(filename))
{
	m_priv->open(append);
}

FileOutputStream::FileOutputStream(const logchar* filename,	bool append)
	: m_priv(std::make_unique<FileOutputStreamPrivate>(filename))
{
	m_priv->open(append);
}

void FileOutputStream::FileOutputStreamPrivate::open(bool append)
{
	apr_fileperms_t perm = APR_OS_DEFAULT;
	apr_int32_t flags = APR_WRITE | APR_CREATE;

	if (append)
	{
		flags |= APR_APPEND;
	}
	else
	{
		flags |= APR_TRUNCATE;
	}

	apr_status_t stat = apr_file_open(&this->fileptr, this->path.getAPRPath(), flags, perm, this->pool.getAPRPool());

	if (stat != APR_SUCCESS)
	{
		throw IOException(this->path.getAPRPath(), stat);
	}
}

FileOutputStream::~FileOutputStream()
{
	if (m_priv->fileptr)
	{
		apr_file_close(m_priv->fileptr);
	}
}

void FileOutputStream::close( LOG4CXX_CLOSE_OUTPUT_STREAM_FORMAL_PARAMETERS )
{
	if (m_priv->fileptr)
	{
		apr_status_t stat = apr_file_close(m_priv->fileptr);

		if (stat != APR_SUCCESS)
		{
			throw IOException(stat);
		}

		m_priv->fileptr = NULL;
	}
}

void FileOutputStream::flush( LOG4CXX_FLUSH_OUTPUT_STREAM_FORMAL_PARAMETERS )
{
}

void FileOutputStream::write( LOG4CXX_WRITE_OUTPUT_STREAM_FORMAL_PARAMETERS )
{
	if (m_priv->fileptr == NULL)
	{
		throw NullPointerException(LOG4CXX_STR("FileOutputStream"));
	}

	size_t nbytes = buf.remaining();
	while (nbytes > 0)
	{
		apr_status_t stat = apr_file_write(
				m_priv->fileptr, buf.current(), &nbytes);

		if (stat != APR_SUCCESS)
		{
			throw IOException(stat);
		}

		nbytes = buf.increment_position(nbytes);
	}
}

apr_file_t* FileOutputStream::getFilePtr() const{
	return m_priv->fileptr;
}

