/* Copyright 2015 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <errno.h>
#include <stdint.h>
#include <sys/param.h>

#include "cras_client.h"
#include "cras_util.h"

struct buffer_data {
	const uint8_t *buffer;
	unsigned int offset;
	unsigned int frame_bytes;
	unsigned int len;
};

static int play_buffer_callback(struct cras_client *client,
				cras_stream_id_t stream_id,
				uint8_t *captured_samples,
				uint8_t *playback_samples,
				unsigned int frames,
				const struct timespec *captured_time,
				const struct timespec *playback_time,
				void *user_arg)
{
	struct buffer_data *data = (struct buffer_data *)user_arg;
	int to_copy = data->len - data->offset;

	if (to_copy <= 0) {
		free(user_arg);
		return EOF;
	}

	to_copy = MIN(to_copy, frames * data->frame_bytes);

	memcpy(playback_samples, data->buffer + data->offset, to_copy);

	data->offset += to_copy;

	return to_copy / data->frame_bytes;
}

static int play_buffer_error(struct cras_client *client,
			     cras_stream_id_t stream_id,
			     int error,
			     void *user_arg)
{
	free(user_arg);
	return 0;
}

int cras_helper_create_connect_async(struct cras_client **client,
				     cras_connection_status_cb_t connection_cb,
				     void *user_arg)
{
	int rc;

	rc = cras_client_create(client);
	if (rc < 0)
		return rc;

	cras_client_set_connection_status_cb(*client, connection_cb, user_arg);

	rc = cras_client_run_thread(*client);
	if (rc < 0)
		goto client_start_error;

	rc = cras_client_connect_async(*client);
	if (rc < 0)
		goto client_start_error;

	return 0;

client_start_error:
	cras_client_destroy(*client);
	return rc;
}

int cras_helper_create_connect(struct cras_client **client)
{
	int rc;

	rc = cras_client_create(client);
	if (rc < 0)
		return rc;

	rc = cras_client_connect(*client);
	if (rc < 0)
		goto client_start_error;

	rc = cras_client_run_thread(*client);
	if (rc < 0)
		goto client_start_error;

	rc = cras_client_connected_wait(*client);
	if (rc < 0)
		goto client_start_error;

	return 0;

client_start_error:
	cras_client_destroy(*client);
	return rc;
}

int cras_helper_add_stream_simple(struct cras_client *client,
				  enum CRAS_STREAM_DIRECTION direction,
				  void *user_data,
				  cras_unified_cb_t unified_cb,
				  cras_error_cb_t err_cb,
				  snd_pcm_format_t format,
				  unsigned int frame_rate,
				  unsigned int num_channels,
				  int dev_idx,
				  cras_stream_id_t *stream_id_out)
{
	struct cras_audio_format *aud_format;
	struct cras_stream_params *params;
	int rc;

	aud_format = cras_audio_format_create(format, frame_rate, num_channels);
	if (!aud_format)
		return -ENOMEM;

	params = cras_client_unified_params_create(CRAS_STREAM_OUTPUT,
			2048, CRAS_STREAM_TYPE_DEFAULT, 0, user_data,
			unified_cb, err_cb, aud_format);
	if (!params) {
		rc = -ENOMEM;
		goto done_add_stream;
	}

	if (dev_idx < 0)
		dev_idx = NO_DEVICE;
	rc = cras_client_add_pinned_stream(client, dev_idx, stream_id_out,
					   params);

done_add_stream:
	cras_audio_format_destroy(aud_format);
	cras_client_stream_params_destroy(params);
	return rc;
}

int cras_helper_play_buffer(struct cras_client *client,
			    const void *buffer,
			    unsigned int frames,
			    snd_pcm_format_t format,
			    unsigned int frame_rate,
			    unsigned int num_channels,
			    int dev_idx)
{
	struct buffer_data *data;
	cras_stream_id_t stream_id;

	data = malloc(sizeof(*data));

	data->buffer = buffer;
	data->frame_bytes = num_channels * PCM_FORMAT_WIDTH(format) / 8;
	data->offset = 0;
	data->len = frames * data->frame_bytes;

	return cras_helper_add_stream_simple(client, CRAS_STREAM_OUTPUT, data,
			play_buffer_callback, play_buffer_error, format,
			frame_rate, num_channels, dev_idx, &stream_id);
}