/*
 * SPDX-FileCopyrightText: (C) 1999-2008 Novell, Inc. (www.novell.com)
 * SPDX-License-Identifier: LGPL-2.0-or-later
 * SPDX-FileContributor: Michael Zucchi <notzed@ximian.com>
 */

#include "evolution-data-server-config.h"

#include <glib/gi18n-lib.h>

#include "camel-stream-null.h"

struct _CamelStreamNullPrivate {
	gsize written;
	gboolean ends_with_crlf;
	gboolean ends_with_cr; /* Just for cases when the CRLF is split into two writes, CR and LF */
};

static void camel_stream_null_seekable_init (GSeekableIface *iface);

G_DEFINE_TYPE_WITH_CODE (CamelStreamNull, camel_stream_null, CAMEL_TYPE_STREAM,
	G_ADD_PRIVATE (CamelStreamNull)
	G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, camel_stream_null_seekable_init))

static gssize
stream_null_write (CamelStream *stream,
                   const gchar *buffer,
                   gsize n,
                   GCancellable *cancellable,
                   GError **error)
{
	CamelStreamNull *stream_null = CAMEL_STREAM_NULL (stream);

	stream_null->priv->written += n;

	if (n >= 2) {
		stream_null->priv->ends_with_crlf = buffer[n - 2] == '\r' && buffer[n - 1] == '\n';
		stream_null->priv->ends_with_cr = buffer[n - 1] == '\r';
	} else if (n == 1) {
		stream_null->priv->ends_with_crlf = stream_null->priv->ends_with_cr && buffer[n - 1] == '\n';
		stream_null->priv->ends_with_cr = buffer[n - 1] == '\r';
	}

	return n;
}

static gboolean
stream_null_eos (CamelStream *stream)
{
	return TRUE;
}

static goffset
stream_null_tell (GSeekable *seekable)
{
	return 0;
}

static gboolean
stream_null_can_seek (GSeekable *seekable)
{
	return TRUE;
}

static gboolean
stream_null_seek (GSeekable *seekable,
                  goffset offset,
                  GSeekType type,
                  GCancellable *cancellable,
                  GError **error)
{
	if (type != G_SEEK_SET || offset != 0) {
		g_set_error_literal (
			error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
			_("Only reset to beginning is supported with CamelHttpStream"));
		return FALSE;
	}

	CAMEL_STREAM_NULL (seekable)->priv->written = 0;

	return TRUE;
}

static gboolean
stream_null_can_truncate (GSeekable *seekable)
{
	return FALSE;
}

static gboolean
stream_null_truncate_fn (GSeekable *seekable,
                         goffset offset,
                         GCancellable *cancellable,
                         GError **error)
{
	/* XXX Don't bother translating this.  Camel never calls it. */
	g_set_error_literal (
		error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
		"Truncation is not supported");

	return FALSE;
}

static void
camel_stream_null_class_init (CamelStreamNullClass *class)
{
	CamelStreamClass *stream_class;

	stream_class = CAMEL_STREAM_CLASS (class);
	stream_class->write = stream_null_write;
	stream_class->eos = stream_null_eos;
}

static void
camel_stream_null_seekable_init (GSeekableIface *iface)
{
	iface->tell = stream_null_tell;
	iface->can_seek = stream_null_can_seek;
	iface->seek = stream_null_seek;
	iface->can_truncate = stream_null_can_truncate;
	iface->truncate_fn = stream_null_truncate_fn;
}

static void
camel_stream_null_init (CamelStreamNull *stream_null)
{
	stream_null->priv = camel_stream_null_get_instance_private (stream_null);
	stream_null->priv->ends_with_crlf = FALSE;
	stream_null->priv->ends_with_cr = FALSE;
}

/**
 * camel_stream_null_new:
 *
 * Returns a null stream.  A null stream is always at eof, and
 * always returns success for all reads and writes.
 *
 * Returns: (transfer full): a new #CamelStreamNull
 **/
CamelStream *
camel_stream_null_new (void)
{
	return g_object_new (CAMEL_TYPE_STREAM_NULL, NULL);
}

/**
 * camel_stream_null_get_bytes_written:
 * @stream_null: a #CamelStreamNull
 *
 * Returns: how many bytes had been written to the @stream_null since
 *   it was created or rewind to the beginning.
 *
 * Since: 3.24
 **/
gsize
camel_stream_null_get_bytes_written (CamelStreamNull *stream_null)
{
	g_return_val_if_fail (CAMEL_IS_STREAM_NULL (stream_null), -1);

	return stream_null->priv->written;
}

/**
 * camel_stream_null_get_ends_with_crlf:
 * @stream_null: a #CamelStreamNull
 *
 * Returns: Whether the data being written to @stream_null ended with CRLF.
 *
 * Since: 3.30
 **/
gboolean
camel_stream_null_get_ends_with_crlf (CamelStreamNull *stream_null)
{
	g_return_val_if_fail (CAMEL_IS_STREAM_NULL (stream_null), FALSE);

	return stream_null->priv->ends_with_crlf;
}
