/***************************************************************************
 *            cdrdao.c
 *
 *  dim jan 22 15:38:18 2006
 *  Copyright  2006  Rouquier Philippe
 *  brasero-app@wanadoo.fr
 ***************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <glib.h>
#include <glib-object.h>
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>

#include <nautilus-burn-drive.h>

#include "burn-cdrdao.h"
#include "burn-common.h"
#include "burn-basics.h"
#include "burn-process.h"
#include "burn-recorder.h"
#include "burn-imager.h"
#include "brasero-ncb.h"

static void brasero_cdrdao_class_init (BraseroCdrdaoClass *klass);
static void brasero_cdrdao_init (BraseroCdrdao *sp);
static void brasero_cdrdao_finalize (GObject *object);
static void brasero_cdrdao_iface_init_record (BraseroRecorderIFace *iface);
static void brasero_cdrdao_iface_init_image (BraseroImagerIFace *iface);

static BraseroBurnResult
brasero_cdrdao_read_stderr (BraseroProcess *process,
			    const char *line);
static BraseroBurnResult
brasero_cdrdao_set_argv (BraseroProcess *process,
			 GPtrArray *argv,
			 gboolean has_master, 
			 GError **error);
static BraseroBurnResult
brasero_cdrdao_post (BraseroProcess *process,
		     BraseroBurnResult retval);

static BraseroBurnResult
brasero_cdrdao_get_written (BraseroJob *job,
			    gint64 *written);
static BraseroBurnResult
brasero_cdrdao_get_fifo (BraseroRecorder *recorder,
			 gint *fifo);
				  
static BraseroBurnResult
brasero_cdrdao_set_drive (BraseroRecorder *recorder,
			  NautilusBurnDrive *drive,
			  GError **error);

static BraseroBurnResult
brasero_cdrdao_set_flags (BraseroRecorder *recorder,
			  BraseroRecorderFlag flags,
			  GError **error);
static BraseroBurnResult
brasero_cdrdao_set_rate (BraseroJob *job,
			  gint64 rate);

static BraseroBurnResult
brasero_cdrdao_record (BraseroRecorder *recorder,
		       GError **error);
static BraseroBurnResult
brasero_cdrdao_blank (BraseroRecorder *recorder,
		      GError **error);

static BraseroBurnResult
brasero_cdrdao_get_track_type (BraseroImager *imager,
			       BraseroTrackSourceType *type,
			       BraseroImageFormat *format);

static BraseroBurnResult
brasero_cdrdao_get_size_image (BraseroImager *imager,
			       gint64 *size,
			       gboolean sectors,
			       GError **error);
static BraseroBurnResult
brasero_cdrdao_get_track (BraseroImager *imager,
			  BraseroTrackSource **track,
			  GError **error);

static BraseroBurnResult
brasero_cdrdao_set_source (BraseroJob *job,
			   const BraseroTrackSource *source,
			   GError **error);
static BraseroBurnResult
brasero_cdrdao_set_output (BraseroImager *imager,
			   const char *ouput,
			   gboolean overwrite,
			   gboolean clean,
			   GError **error);
static BraseroBurnResult
brasero_cdrdao_set_output_type (BraseroImager *imager,
				BraseroTrackSourceType type,
				BraseroImageFormat format,
				GError **error);

static BraseroBurnResult
brasero_cdrdao_get_action_string (BraseroJob *job, 
				  BraseroBurnAction action,
				  char **string);

typedef enum {
	BRASERO_CDRDAO_ACTION_NONE,
	BRASERO_CDRDAO_ACTION_IMAGE,
	BRASERO_CDRDAO_ACTION_GET_SIZE,
	BRASERO_CDRDAO_ACTION_RECORD,
	BRASERO_CDRDAO_ACTION_BLANK,
} BraseroCdrdaoAction;

struct BraseroCdrdaoPrivate {
	BraseroCdrdaoAction action;
	BraseroTrackSource *track;
	NautilusBurnDrive *drive;

	gchar *src_output;
	gchar *toc;

	gint fifo;
	gint speed;               /* speed at which we should write */

	gint track_num;
	gint track_start;

	gint64 isosize;
	gint sectors_num;
	gint64 b_written;

	int dao:1;
	int dummy:1;
	int multi:1;
	int nograce:1;
	int overburn:1;
	int burnproof:1;

	int overwrite:1;
	int clean:1;

	int blank_fast:1;

	int track_ready:1;
};

static GObjectClass *parent_class = NULL;

GType
brasero_cdrdao_get_type ()
{
	static GType type = 0;

	if(type == 0) {
		static const GTypeInfo our_info = {
			sizeof (BraseroCdrdaoClass),
			NULL,
			NULL,
			(GClassInitFunc)brasero_cdrdao_class_init,
			NULL,
			NULL,
			sizeof (BraseroCdrdao),
			0,
			(GInstanceInitFunc)brasero_cdrdao_init,
		};

		static const GInterfaceInfo imager_info =
		{
			(GInterfaceInitFunc) brasero_cdrdao_iface_init_image,
			NULL,
			NULL
		};
		static const GInterfaceInfo recorder_info =
		{
			(GInterfaceInitFunc) brasero_cdrdao_iface_init_record,
			NULL,
			NULL
		};

		type = g_type_register_static (BRASERO_TYPE_PROCESS, 
					       "BraseroCdrdao", 
					       &our_info,
					       0);
		g_type_add_interface_static (type,
					     BRASERO_TYPE_IMAGER,
					     &imager_info);
		g_type_add_interface_static (type,
					     BRASERO_TYPE_RECORDER,
					     &recorder_info);
	}

	return type;
}

static void
brasero_cdrdao_iface_init_record (BraseroRecorderIFace *iface)
{
	iface->set_drive = brasero_cdrdao_set_drive;
	iface->set_flags = brasero_cdrdao_set_flags;
	iface->get_fifo = brasero_cdrdao_get_fifo;
	iface->record = brasero_cdrdao_record;
	iface->blank = brasero_cdrdao_blank;
}

static void
brasero_cdrdao_iface_init_image (BraseroImagerIFace *iface)
{
	iface->get_size = brasero_cdrdao_get_size_image;
	iface->get_track = brasero_cdrdao_get_track;
	iface->get_track_type = brasero_cdrdao_get_track_type;

	iface->set_output = brasero_cdrdao_set_output;
	iface->set_output_type = brasero_cdrdao_set_output_type;
}

static void
brasero_cdrdao_class_init (BraseroCdrdaoClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	BraseroJobClass *job_class = BRASERO_JOB_CLASS (klass);
	BraseroProcessClass *process_class = BRASERO_PROCESS_CLASS (klass);

	parent_class = g_type_class_peek_parent(klass);
	object_class->finalize = brasero_cdrdao_finalize;

	job_class->get_rate = brasero_job_task_get_average_rate;
	job_class->set_rate = brasero_cdrdao_set_rate;
	job_class->set_source = brasero_cdrdao_set_source;
	job_class->get_written = brasero_cdrdao_get_written;
	job_class->get_action_string = brasero_cdrdao_get_action_string;

	process_class->stderr_func = brasero_cdrdao_read_stderr;
	process_class->set_argv = brasero_cdrdao_set_argv;
	process_class->post = brasero_cdrdao_post;
}

static void
brasero_cdrdao_init (BraseroCdrdao *obj)
{
	obj->priv = g_new0(BraseroCdrdaoPrivate, 1);
	obj->priv->isosize = -1;
	obj->priv->clean = TRUE;
}

static void
brasero_cdrdao_finalize (GObject *object)
{
	BraseroCdrdao *cobj;
	cobj = BRASERO_CDRDAO (object);
	
	if (cobj->priv->drive) {
		nautilus_burn_drive_unref (cobj->priv->drive);
		cobj->priv->drive = NULL;
	}
	
	if (cobj->priv->track) {
		brasero_track_source_free (cobj->priv->track);
		cobj->priv->track = NULL;
	}

	if (cobj->priv->src_output) {
		if (cobj->priv->clean && cobj->priv->track_ready)
			g_remove (cobj->priv->src_output);

		g_free (cobj->priv->src_output);
		cobj->priv->src_output = NULL;
	}

	if (cobj->priv->toc) {
		if (cobj->priv->clean  && cobj->priv->track_ready)
			g_remove (cobj->priv->toc);

		g_free (cobj->priv->toc);
		cobj->priv->toc = NULL;
	}

	g_free(cobj->priv);
	G_OBJECT_CLASS(parent_class)->finalize(object);
}

BraseroCdrdao *
brasero_cdrdao_new ()
{
	BraseroCdrdao *obj;
	
	obj = BRASERO_CDRDAO (g_object_new (BRASERO_TYPE_CDRDAO, NULL));
	
	return obj;
}

static gboolean
brasero_cdrdao_read_stderr_image (BraseroCdrdao *cdrdao, const char *line)
{
	int min, sec, sub, s1;

	if (sscanf (line, "%d:%d:%d", &min, &sec, &sub) == 3) {
		gdouble fraction;
		guint64 secs = min * 60 + sec;

		fraction = (gdouble) secs / cdrdao->priv->isosize;
		cdrdao->priv->b_written = cdrdao->priv->sectors_num * 2352 * fraction;

		if (secs > 2
		&&  brasero_job_task_get_elapsed (BRASERO_JOB (cdrdao), NULL) == BRASERO_BURN_NOT_READY)
			brasero_job_task_start_progress (BRASERO_JOB (cdrdao));
	}
	else if (sscanf (line, "Leadout %*s %*d %d:%d:%*d(%i)", &min, &sec, &s1) == 3) {
		/* we get the number of sectors. As we added -raw each sector = 2352 bytes */
		cdrdao->priv->sectors_num = s1;
		cdrdao->priv->isosize = min * 60 + sec;

		if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_GET_SIZE)
			brasero_job_finished (BRASERO_JOB (cdrdao));
	}
	else if (strstr (line, "Copying audio tracks")) {
		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_DRIVE_COPY,
					    FALSE);
	}
	else if (strstr (line, "Copying data track")) {
		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_DRIVE_COPY,
					    FALSE);
	}
	else if (strstr (line, "toc and track data finished successfully"))
		cdrdao->priv->b_written = cdrdao->priv->sectors_num * 2352;
	else
		return FALSE;

	return TRUE;
}

static gboolean
brasero_cdrdao_read_stderr_record (BraseroCdrdao *cdrdao, const char *line)
{
	int fifo, track, min, sec;
	guint written, total;

	if (sscanf (line, "Wrote %u of %u (Buffers %d%%  %*s", &written, &total, &fifo) >= 2) {
		brasero_job_set_dangerous (BRASERO_JOB (cdrdao), TRUE);
		cdrdao->priv->fifo = fifo;
		cdrdao->priv->b_written = written * 1048576;

		if (brasero_job_task_get_elapsed (BRASERO_JOB (cdrdao), NULL) == BRASERO_BURN_NOT_READY)
			brasero_job_task_start_progress (BRASERO_JOB (cdrdao));

		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_WRITING,
					    FALSE);
	}
	else if (sscanf (line, "Wrote %*s blocks. Buffer fill min") == 1) {
		/* this is for fixating phase */
		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_FIXATING,
					    FALSE);
	}
	else if (sscanf (line, "Analyzing track %d %*s start %d:%d:%*d, length %*d:%*d:%*d", &track, &min, &sec) == 3) {
		cdrdao->priv->track_num = track;
		cdrdao->priv->track_start = min * 60 + sec;

		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_ANALYSING,
					    TRUE);
	}
	else if (sscanf (line, "%d:%d:%*d", &min, &sec) == 2) {
		gdouble fraction;
		guint64 secs = min * 60 + sec + cdrdao->priv->track_start;

		if (!cdrdao->priv->track_start && !cdrdao->priv->isosize)
			return TRUE;

		cdrdao->priv->b_written = sec * 75 * 2352;

		fraction = (gdouble) secs / (gdouble) cdrdao->priv->isosize;
	}
	else if (strstr (line, "Q sub-channels with CRC errors")) {
		cdrdao->priv->track_num = 0;
	}
	else if (strstr (line, "Starting on-the-fly CD copy")
	      ||  strstr (line, "Starting write at speed")) {
		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_PREPARING,
					    FALSE);
	}
	else if (strstr (line, "Executing power calibration")) {
		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_PREPARING,
					    FALSE);
	}
	else if (strstr (line, "Writing track")) {
		brasero_job_set_dangerous (BRASERO_JOB (cdrdao), TRUE);
		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_WRITING,
					    FALSE);
	}
	else if (strstr (line, "Writing finished successfully")
	     ||  strstr (line, "On-the-fly CD copying finished successfully")) {
		brasero_job_set_dangerous (BRASERO_JOB (cdrdao), FALSE);
	}
	else /* this is to report progress when a cd is being analysed before copy */
		return FALSE;

	return TRUE;
}

static BraseroBurnResult
brasero_cdrdao_read_stderr (BraseroProcess *process, const char *line)
{
	BraseroCdrdao *cdrdao;
	gboolean result = FALSE;

	cdrdao = BRASERO_CDRDAO (process);

	if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_RECORD
	||  cdrdao->priv->action == BRASERO_CDRDAO_ACTION_BLANK)
		result = brasero_cdrdao_read_stderr_record (cdrdao, line);
	else if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_IMAGE
	      ||  cdrdao->priv->action == BRASERO_CDRDAO_ACTION_GET_SIZE)
		result = brasero_cdrdao_read_stderr_image (cdrdao, line);

	if (result)
		return BRASERO_BURN_OK;

	if (strstr (line, "Cannot setup device")) {
		brasero_job_error (BRASERO_JOB (cdrdao),
				   g_error_new (BRASERO_BURN_ERROR,
						BRASERO_BURN_ERROR_BUSY_DRIVE,
						_("the drive seems to be busy")));
	}
	else if (strstr (line, "Illegal command")) {
		brasero_job_error (BRASERO_JOB (cdrdao),
				   g_error_new (BRASERO_BURN_ERROR,
						BRASERO_BURN_ERROR_GENERAL,
						_("your version of cdrdao doesn't seem to be supported by libbrasero")));
	}
	else if (strstr (line, "Operation not permitted. Cannot send SCSI")) {
		brasero_job_error (BRASERO_JOB (cdrdao),
				   g_error_new (BRASERO_BURN_ERROR,
						BRASERO_BURN_ERROR_SCSI_IOCTL,
						_("You don't seem to have the required permission to use this drive")));
	}

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_get_track_type (BraseroImager *imager,
			       BraseroTrackSourceType *type,
			       BraseroImageFormat *format)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (imager);

	if (!cdrdao->priv->track)
		BRASERO_JOB_NOT_READY (cdrdao);

	if (type)
		*type = BRASERO_TRACK_SOURCE_IMAGE;

	if (format)
		*format = BRASERO_IMAGE_FORMAT_CUE;

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_source (BraseroJob *job,
			   const BraseroTrackSource *source,
			   GError **error)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (job);

	/* Remove any current output */
	if (cdrdao->priv->src_output) {
		if (cdrdao->priv->clean && cdrdao->priv->track_ready)
			g_remove (cdrdao->priv->src_output);
		if (cdrdao->priv->clean && cdrdao->priv->track_ready)
			g_remove (cdrdao->priv->toc);
	}
	cdrdao->priv->track_ready = 0;

	/* NOTE: we can accept ourselves as our source (cdrdao is both imager
	 * and recorder). In this case we don't delete the previous source */
	if (source->type == BRASERO_TRACK_SOURCE_IMAGER
	&&  BRASERO_IMAGER (cdrdao) == source->contents.imager.obj)
		return BRASERO_BURN_OK;

	if (cdrdao->priv->track) {
		brasero_track_source_free (cdrdao->priv->track);
		cdrdao->priv->track = NULL;
	}

	/* NOTE: we don't check the type of source precisely since this check partly
	 * depends on what we do with cdrdao afterward (it's both imager/recorder) */
        if (source->type != BRASERO_TRACK_SOURCE_DISC
	&& !(source->format & BRASERO_IMAGE_FORMAT_CUE))
		BRASERO_JOB_NOT_SUPPORTED (cdrdao);

	cdrdao->priv->track = brasero_track_source_copy (source);
	cdrdao->priv->isosize = -1;
	cdrdao->priv->sectors_num = 0;

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_output (BraseroImager *imager,
			   const char *output,
			   gboolean overwrite,
			   gboolean clean,
			   GError **error)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (imager);

	if (cdrdao->priv->src_output) {
		if (cdrdao->priv->clean && cdrdao->priv->track_ready)
			g_remove (cdrdao->priv->src_output);

		g_free (cdrdao->priv->src_output);
		cdrdao->priv->src_output = NULL;
	}

	if (cdrdao->priv->toc) {
		if (cdrdao->priv->clean && cdrdao->priv->track_ready)
			g_remove (cdrdao->priv->toc);

		g_free (cdrdao->priv->toc);
		cdrdao->priv->toc = NULL;
	}

	cdrdao->priv->track_ready = 0;

	if (output)
		cdrdao->priv->src_output = g_strdup (output);

	cdrdao->priv->overwrite = overwrite;
	cdrdao->priv->clean = clean;

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_output_type (BraseroImager *imager,
				BraseroTrackSourceType type,
				BraseroImageFormat format,
				GError **error)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (imager);

	if (type != BRASERO_TRACK_SOURCE_DEFAULT
	&&  type != BRASERO_TRACK_SOURCE_IMAGE)
		BRASERO_JOB_NOT_SUPPORTED (cdrdao);

	if (!(format & BRASERO_IMAGE_FORMAT_CUE))
		BRASERO_JOB_NOT_SUPPORTED (cdrdao);

	/* NOTE: no need to keep this value since cdrdao can only output cue */
	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_get_track (BraseroImager *imager,
			  BraseroTrackSource **track,
			  GError **error)
{
	BraseroCdrdao *cdrdao;
	BraseroTrackSource *retval;

	cdrdao = BRASERO_CDRDAO (imager);

	if (!cdrdao->priv->track)
		BRASERO_JOB_NOT_READY (cdrdao);

	if (cdrdao->priv->track->type != BRASERO_TRACK_SOURCE_DISC)
		BRASERO_JOB_NOT_SUPPORTED (cdrdao);

	if (!cdrdao->priv->track_ready) {
		BraseroBurnResult result;

		cdrdao->priv->action = BRASERO_CDRDAO_ACTION_IMAGE;
		result = brasero_job_run (BRASERO_JOB (imager), error);
		cdrdao->priv->action = BRASERO_CDRDAO_ACTION_NONE;

		if (result != BRASERO_BURN_OK)
			return result;

		cdrdao->priv->track_ready = 1;
		cdrdao->priv->b_written = cdrdao->priv->sectors_num * 2352;
	}

	retval = g_new0 (BraseroTrackSource, 1);

	retval->type = BRASERO_TRACK_SOURCE_IMAGE;
	retval->format = BRASERO_IMAGE_FORMAT_CUE;
	retval->contents.image.toc = g_strdup_printf ("file://%s.toc", cdrdao->priv->src_output);
	retval->contents.image.image = g_strdup_printf ("file://%s", cdrdao->priv->src_output);

	*track = retval;

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_get_size_image (BraseroImager *imager,
			       gint64 *size,
			       gboolean sectors,
			       GError **error)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (imager);

	if (cdrdao->priv->sectors_num < 1) {
		BraseroBurnResult result;

		if (brasero_job_is_running (BRASERO_JOB (imager)))
			return BRASERO_BURN_RUNNING;

		/* unfortunately cdrdao doesn't allow it beforehand 
		 * so we cheat here : we start cdrdao and stop it as
		 * soon as we get the size */
		cdrdao->priv->action = BRASERO_CDRDAO_ACTION_GET_SIZE;
		result = brasero_job_run (BRASERO_JOB (imager), error);
		cdrdao->priv->action = BRASERO_CDRDAO_ACTION_NONE;

		if (result != BRASERO_BURN_OK)
			return result;

		/* now we must remove the .toc and .bin in case they were created */
		g_remove (cdrdao->priv->src_output);
		g_remove (cdrdao->priv->toc);
	}

	if (sectors) {
		/* NOTE: 1 sec = 75 sectors, 1 sector = 2352 bytes */
		*size = cdrdao->priv->sectors_num;
	}
	else
		*size = cdrdao->priv->sectors_num * 2352;
	
	return BRASERO_BURN_OK;
}

static void
brasero_cdrdao_set_argv_device (BraseroCdrdao *cdrdao,
				GPtrArray *argv)
{
	/* it seems that cdrdao (1.2.0) doesn't like the cdrecord_id on my computer */
	g_ptr_array_add (argv, g_strdup ("--device"));
	if (NCB_DRIVE_GET_DEVICE (cdrdao->priv->drive))
		g_ptr_array_add (argv, g_strdup (NCB_DRIVE_GET_DEVICE (cdrdao->priv->drive)));
}

static void
brasero_cdrdao_set_argv_common_rec (BraseroCdrdao *cdrdao,
				    GPtrArray *argv)
{
	char *speed_str;

	g_ptr_array_add (argv, g_strdup ("--speed"));
	speed_str = g_strdup_printf ("%d", cdrdao->priv->speed);
	g_ptr_array_add (argv, speed_str);

	if (cdrdao->priv->overburn)
		g_ptr_array_add (argv, g_strdup ("--overburn"));
	if (cdrdao->priv->multi)
		g_ptr_array_add (argv, g_strdup ("--multi"));
}

static void
brasero_cdrdao_set_argv_common (BraseroCdrdao *cdrdao,
				    GPtrArray *argv)
{
	if (cdrdao->priv->dummy)
		g_ptr_array_add (argv, g_strdup ("--simulate"));

	/* cdrdao manual says it is a similar option to gracetime */
	if (cdrdao->priv->nograce)
		g_ptr_array_add (argv, g_strdup ("-n"));

	g_ptr_array_add (argv, g_strdup ("-v"));
	g_ptr_array_add (argv, g_strdup ("2"));
}

static BraseroBurnResult
brasero_cdrdao_set_argv_record (BraseroCdrdao *cdrdao,
				GPtrArray *argv)
{
	BraseroTrackSource *track;

	if (!cdrdao->priv->drive)
		BRASERO_JOB_NOT_READY (cdrdao);

	track = cdrdao->priv->track;
	if (!track)
		BRASERO_JOB_NOT_READY (cdrdao);

        if (track->type == BRASERO_TRACK_SOURCE_DISC) {
		NautilusBurnDrive *source;

		g_ptr_array_add (argv, g_strdup ("copy"));
		brasero_cdrdao_set_argv_device (cdrdao, argv);
		brasero_cdrdao_set_argv_common (cdrdao, argv);
		brasero_cdrdao_set_argv_common_rec (cdrdao, argv);

		source = cdrdao->priv->track->contents.drive.disc;
		if (!nautilus_burn_drive_equal (source, cdrdao->priv->drive))
			g_ptr_array_add (argv, g_strdup ("--on-the-fly"));

		g_ptr_array_add (argv, g_strdup ("--source-device"));
		g_ptr_array_add (argv, g_strdup (NCB_DRIVE_GET_DEVICE (source)));
	}
	else if (track->type == BRASERO_TRACK_SOURCE_IMAGE
	      &&  track->format & BRASERO_IMAGE_FORMAT_CUE) {
		gchar *cuepath;

		g_ptr_array_add (argv, g_strdup ("write"));

		brasero_cdrdao_set_argv_device (cdrdao, argv);
		brasero_cdrdao_set_argv_common (cdrdao, argv);
		brasero_cdrdao_set_argv_common_rec (cdrdao, argv);

		cuepath = brasero_track_source_get_cue_localpath (track);
		if (!cuepath)
			return BRASERO_BURN_ERR;

		g_ptr_array_add (argv, cuepath);
	}
	else
		BRASERO_JOB_NOT_SUPPORTED (cdrdao);

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_argv_blank (BraseroCdrdao *cdrdao,
			       GPtrArray *argv)
{
	if (!cdrdao->priv->drive)
		BRASERO_JOB_NOT_READY (cdrdao);

	g_ptr_array_add (argv, g_strdup ("blank"));

	brasero_cdrdao_set_argv_device (cdrdao, argv);
	brasero_cdrdao_set_argv_common (cdrdao, argv);

	if (!cdrdao->priv->blank_fast) {
		g_ptr_array_add (argv, g_strdup ("--blank-mode"));
		g_ptr_array_add (argv, g_strdup ("full"));
	}

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_argv_image (BraseroCdrdao *cdrdao,
			       GPtrArray *argv,
			       GError **error)
{
	BraseroBurnResult result;
	NautilusBurnDrive *source;

	if (!cdrdao->priv->track)
		BRASERO_JOB_NOT_READY (cdrdao);

	g_ptr_array_add (argv, g_strdup ("read-cd"));
	g_ptr_array_add (argv, g_strdup ("--device"));

	source = cdrdao->priv->track->contents.drive.disc;
	g_ptr_array_add (argv, g_strdup (NCB_DRIVE_GET_DEVICE (source)));

	g_ptr_array_add (argv, g_strdup ("--read-raw"));

	result = brasero_burn_common_check_output (&cdrdao->priv->src_output,
						   cdrdao->priv->overwrite,
						   &cdrdao->priv->toc,
						   error);
	if (result != BRASERO_BURN_OK)
		return result;

	g_ptr_array_add (argv, g_strdup ("--datafile"));
	g_ptr_array_add (argv, g_strdup (cdrdao->priv->src_output));

	g_ptr_array_add (argv, g_strdup ("-v"));
	g_ptr_array_add (argv, g_strdup ("2"));

	g_ptr_array_add (argv, g_strdup (cdrdao->priv->toc));

	if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_GET_SIZE)
		brasero_job_task_set_action (BRASERO_JOB (cdrdao),
					    BRASERO_BURN_ACTION_GETTING_SIZE,
					    FALSE);

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_argv (BraseroProcess *process,
			 GPtrArray *argv,
			 gboolean has_master,
			 GError **error)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (process);

	if (has_master)
		BRASERO_JOB_NOT_SUPPORTED (cdrdao);

	brasero_job_set_run_slave (BRASERO_JOB (cdrdao), FALSE);

	/* sets the first argv */
	g_ptr_array_add (argv, g_strdup ("cdrdao"));

	if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_RECORD)
		return brasero_cdrdao_set_argv_record (cdrdao, argv);
	else if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_BLANK)
		return brasero_cdrdao_set_argv_blank (cdrdao, argv);
	else if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_IMAGE)
		return brasero_cdrdao_set_argv_image (cdrdao, argv, error);
	else if (cdrdao->priv->action == BRASERO_CDRDAO_ACTION_GET_SIZE)
		return brasero_cdrdao_set_argv_image (cdrdao, argv, error);


	BRASERO_JOB_NOT_READY (cdrdao);
}

static BraseroBurnResult
brasero_cdrdao_post (BraseroProcess *process,
		     BraseroBurnResult retval)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (process);

	if (retval == BRASERO_BURN_CANCEL) {
		if (cdrdao->priv->src_output)
			g_remove (cdrdao->priv->src_output);
		if (cdrdao->priv->toc)
			g_remove (cdrdao->priv->toc);
	}

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_drive (BraseroRecorder *recorder,
			  NautilusBurnDrive *drive,
			  GError **error)
{
	NautilusBurnMediaType media;
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (recorder);

	media = nautilus_burn_drive_get_media_type (drive);
	if (media > NAUTILUS_BURN_MEDIA_TYPE_CDRW)
		BRASERO_JOB_NOT_SUPPORTED (cdrdao);

	if (cdrdao->priv->drive) {
		nautilus_burn_drive_unref (cdrdao->priv->drive);
		cdrdao->priv->drive = NULL;
	}

	cdrdao->priv->drive = drive;
	nautilus_burn_drive_ref (drive);

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_flags (BraseroRecorder *recorder,
			  BraseroRecorderFlag flags,
			  GError **error)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (recorder);

	cdrdao->priv->multi = (flags & BRASERO_RECORDER_FLAG_MULTI);
	cdrdao->priv->dummy = (flags & BRASERO_RECORDER_FLAG_DUMMY);
	cdrdao->priv->dao = (flags & BRASERO_RECORDER_FLAG_DAO);
	cdrdao->priv->nograce = (flags & BRASERO_RECORDER_FLAG_NOGRACE);
	cdrdao->priv->burnproof = (flags & BRASERO_RECORDER_FLAG_BURNPROOF);
	cdrdao->priv->overburn = (flags & BRASERO_RECORDER_FLAG_OVERBURN);
	cdrdao->priv->blank_fast = (flags & BRASERO_RECORDER_FLAG_FAST_BLANK);

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_set_rate (BraseroJob *job,
			 gint64 speed)
{
	BraseroCdrdao *cdrdao;

	if (brasero_job_is_running (job))
		return BRASERO_BURN_RUNNING;
	
	cdrdao = BRASERO_CDRDAO (job);
	cdrdao->priv->speed = speed / CDR_SPEED;

	return BRASERO_BURN_OK;

}

static BraseroBurnResult
brasero_cdrdao_record (BraseroRecorder *recorder,
		       GError **error)
{
	BraseroCdrdao *cdrdao;
	BraseroBurnResult result;

	cdrdao = BRASERO_CDRDAO (recorder);

	cdrdao->priv->action = BRASERO_CDRDAO_ACTION_RECORD;
	result = brasero_job_run (BRASERO_JOB (cdrdao), error);
	cdrdao->priv->action = BRASERO_CDRDAO_ACTION_NONE;

	return result;
}

static BraseroBurnResult
brasero_cdrdao_blank (BraseroRecorder *recorder,
		      GError **error)
{
	BraseroCdrdao *cdrdao;
	BraseroBurnResult result;

	cdrdao = BRASERO_CDRDAO (recorder);

	if (!nautilus_burn_drive_can_write (cdrdao->priv->drive)) {
		g_set_error (error,
			     BRASERO_BURN_ERROR,
			     BRASERO_BURN_ERROR_GENERAL,
			     _("the drive cannot rewrite CDs or DVDs"));
		return BRASERO_BURN_ERR;
	}

	cdrdao->priv->action = BRASERO_CDRDAO_ACTION_BLANK;
	result = brasero_job_run (BRASERO_JOB (cdrdao), error);
	cdrdao->priv->action = BRASERO_CDRDAO_ACTION_NONE;

	return result;
}

static BraseroBurnResult
brasero_cdrdao_get_written (BraseroJob *job,
			    gint64 *written)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (job);

	if (written)
		*written = cdrdao->priv->b_written;

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_get_fifo (BraseroRecorder *recorder,
			 gint *fifo)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (recorder);

	if (fifo)
		*fifo = cdrdao->priv->fifo;

	return BRASERO_BURN_OK;
}

static BraseroBurnResult
brasero_cdrdao_get_action_string (BraseroJob *job, 
				  BraseroBurnAction action,
				  char **string)
{
	BraseroCdrdao *cdrdao;

	cdrdao = BRASERO_CDRDAO (job);

	if (action != BRASERO_BURN_ACTION_ANALYSING) {
		const char *tmp;

		tmp = brasero_burn_action_to_string (action);
		*string = g_strdup (tmp);
	}
	else
		*string = g_strdup_printf (_("Analysing track %02i"),
					   cdrdao->priv->track_num);

	return BRASERO_BURN_OK;
}
