/*
 * 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() : fileptr(nullptr) {}

	Pool pool;
	apr_file_t* fileptr;
};

IMPLEMENT_LOG4CXX_OBJECT(FileOutputStream)

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

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

apr_file_t* FileOutputStream::open(const LogString& filename,
	bool append, Pool& pool)
{
	apr_fileperms_t perm = APR_OS_DEFAULT;
	apr_int32_t flags = APR_WRITE | APR_CREATE;

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

	File fn;
	fn.setPath(filename);
	apr_file_t* fileptr = 0;
	apr_status_t stat = fn.open(&fileptr, flags, perm, pool);

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

	return fileptr;
}

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

void FileOutputStream::close(Pool& /* p */)
{
	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(Pool& /* p */)
{
}

void FileOutputStream::write(ByteBuffer& buf, Pool& /* p */ )
{
	if (m_priv->fileptr == NULL)
	{
		throw NullPointerException(LOG4CXX_STR("FileOutputStream"));
	}

	size_t nbytes = buf.remaining();
	size_t pos = buf.position();
	const char* data = buf.data();

	while (nbytes > 0)
	{
		apr_status_t stat = apr_file_write(
				m_priv->fileptr, data + pos, &nbytes);

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

		pos += nbytes;
		buf.position(pos);
		nbytes = buf.remaining();
	}
}

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

