/* readcd.c
 * Copyright (C) 2004, 2005 Sylvain Cresto <scresto@gmail.com>
 *
 * This file is part of graveman!
 *
 * graveman! 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, or
 * (at your option) any later version.
 * 
 * graveman! 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
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with program; see the file COPYING. If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA. 
 * 
 * URL: http://www.nongnu.org/graveman/
 *
 */

#include "graveman.h"

/* communication avec readcd */

#define READCD_CAPACITY "end:"
#define READCD_PROGRESS "addr:"
#define READCD_MSG "readcd:"
#define READCD_DONE "Time total:"
#define READCD_READSPEED "Read  speed:"
#define READCD_READSPEED_CD "CD"

#define READCD_DETECTCAPACITY "kBytes = "
#define CDRECORD_COPY "Track"
#define CDRECORD_FIFO "fifo"
#define CDRECORD_MB "MB"
#define CDRECORD_BUF "buf"
#define CDRECORD_STATUS "fifo was "

gboolean readcd_makeimage_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GIOStatus Lstatus;
  Tgrave *Lg = (Tgrave *)Adata;
  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_data(Lg, "gravetitle"));
  GtkProgressBar *Lprogressbar = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_total"));
  GtkProgressBar *Lprogressbar2 = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_step"));
  gint *Lcont = (gint *) sc_grave_get_data(Lg, "cont"); /* on traite encore des donnees ? */
  GError **Lerreur = (GError **) sc_grave_get_data(Lg, "gerror"); /* pointeur erreur */
  gdouble *Lreadtodo = (gdouble *) sc_grave_get_data(Lg, "readtodo"); /* nombre de secteur a traiter */
  gint *Ltodo = (gint *) sc_grave_get_data(Lg, "todo"); /* nombre de piste a traiter */
  gint *Ldone = (gint *) sc_grave_get_data(Lg, "done"); /* nombre de piste deja traite */
  gchar *Lvitesse = (gchar *) sc_grave_get_data(Lg, "tmpdir");  /* en fait la vitesse de lecture */
  gchar Lbuffer[_BUF_SIZE];
  gdouble Ltotaldone, Lpct, Ltava;
  gchar *e, *f;
  gchar Lsbuf[200];
  gsize Llu = 0;
  gchar *Ltxt;

  /* fin du callback lorsque l'on recoi un signal comme quoi le pipe est ferme */
  if (Acond == G_IO_HUP || Acond == G_IO_ERR) {
    *Lcont = 0;
    return FALSE;
  }
 
  Lstatus = g_io_channel_read_chars(Astd, Lbuffer, _BUF_SIZE-1, &Llu, NULL);  
  if (!*Lbuffer || Lstatus == G_IO_STATUS_ERROR || Lstatus == G_IO_STATUS_AGAIN) return FALSE;
  Lbuffer[Llu]=0;
_DEB("=>%s", Lbuffer);
  if ((f=strstr(Lbuffer, READCD_READSPEED))) {
    f=f+strlen(READCD_READSPEED);
    f=strstr(f, READCD_READSPEED_CD);

    if (f) {
      f=ltrim(f+strlen(READCD_READSPEED_CD));

      if ((e=strchr(f, 'x'))) *e=0;

      g_strlcpy(Lvitesse, f, MAXPATHLEN-1);
    }
    
  } else if ((f=strstr(Lbuffer, READCD_CAPACITY))) {
    /* on reçoi la taille du cd, c'est l'une des premieres lignes renvoyes par readcd,
     * une foi cette ligne reçu c'est le veritable debut de la creation de l'image cd */
    f=f+strlen(READCD_CAPACITY);
    f=ltrim(f);
    if ((e=strchr(f, ' '))) *e=0;
    *Lreadtodo = atof(f);

    if (*Lreadtodo == 0) {
      g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_UNKNOWN_ERROR, 
            _("Communication error with readcd while trying to read cd size."));
      *Lcont = 0;
      return FALSE;
    }
    if (*Lvitesse) {
      Ltxt=g_strdup_printf(_("Reading CD at %sx..."), Lvitesse);
    } else {
      Ltxt = g_strdup(_("Reading CD..."));
    }
    gtk_label_set_text(GTK_LABEL(Ltitle), Ltxt);
    g_free(Ltxt);
  } else if (!strncmp(Lbuffer, READCD_PROGRESS, strlen(READCD_PROGRESS))) {
    f=Lbuffer+strlen(READCD_PROGRESS);
    f=ltrim(f);
    if ((e=strchr(f, ' '))) *e=0;
    Ltotaldone = atof(ltrim(f));
    /* avancement operation */
    Lpct = (1.0/ *Lreadtodo) * Ltotaldone;
    gtk_progress_bar_set_fraction(Lprogressbar2, Lpct);
    g_snprintf(Lsbuf, sizeof(Lsbuf)-1, "%.0f%%", Lpct*100);
    gtk_progress_bar_set_text(Lprogressbar2, Lsbuf); 

    /* avancement total */
    Ltava = (1.0/ (*Ltodo)) * ((*Ldone)-1+Lpct);
    gtk_progress_bar_set_fraction(Lprogressbar, Ltava);
    g_snprintf(Lsbuf, sizeof(Lsbuf)-1, "%.0f%%", Ltava*100);
    gtk_progress_bar_set_text(Lprogressbar, Lsbuf);
  } else if ((f=strstr(Lbuffer, READCD_MSG))) {
    /* erreur readcd !*/
    _DEB(" MSG = [%s]\n", Lbuffer);

    Ltxt = g_strdup_printf(_("Cannot create image: %s"), Lbuffer);
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_READCD, Ltxt);
    g_free(Ltxt);
    return FALSE;
  } else if ((f=strstr(Lbuffer, READCD_DONE))) {
    /* fin de l'operation */
    gboolean *Lstatus = (gboolean *) sc_grave_get_data(Lg, "operationstatus");
    *Lstatus = TRUE;
  }

  return TRUE;
}

/* creation d'une image iso */
gboolean make_image(Tgrave *Ag, GError **Aerror)
{
  gchar **Lcmd;
  gchar *Lcommandline;
  GIOChannel *Lcom, *Lcomerr;
  guint Lcomevent, Lcomerrevent;
  gint *Lcont = sc_grave_get_data(Ag, "cont");
  gboolean *Labort = (gboolean *) sc_grave_get_data(Ag, "gabort");
  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_widget(Ag, "gravetitle"));
  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");
  GtkWidget *Lvitesse = sc_grave_get_widget(Ag, "dstcopyspeed");
  gchar *Liso = (gchar *)sc_grave_get_data(Ag, "iso"); /* image iso */
  gdouble Lreadtodo = 0;
  gint g_in, g_out, g_err, Lnbrarg;
  gboolean Lstatus = FALSE;
  gchar *Lbufvitesse;
  Tdriveinfo *Ldevice = matos_get_drive_info(Ag, "srccopycombo");

  gtk_label_set_text(Ltitle, _("Duplication will start shortly..."));

  Lbufvitesse = get_combo_value(Lvitesse);

  /* try to umount device before device access */
  matos_umount_device(Ldevice, NULL);

  Lcommandline = g_strdup_printf("%s dev=%s f=\"%s\" %s%s -v",
        conf_get_string("readcd"), DRIVE_DEV(Ldevice), Liso, *Lbufvitesse != '0' ? "speed=" : "", *Lbufvitesse != '0' ? Lbufvitesse : "");
  g_free(Lbufvitesse);

  _DEB("execution [%s]\n", Lcommandline);
  Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
  g_free(Lcommandline);
  if (Lstatus == FALSE) {
    return FALSE;
  }
  Lstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, /* env argument */
    (GSpawnFlags ) (G_SPAWN_DO_NOT_REAP_CHILD),
     NULL, NULL, Lpid, &g_in, &g_out, &g_err, Aerror);
  g_strfreev(Lcmd);

  if (Lstatus == FALSE) {
    g_warning("ERROR EXECUTION !\n");
    return FALSE;
  }
  *Lcont = 1;
  sc_grave_set_data(Ag, &Lreadtodo, "readtodo");

  Lcom = g_io_channel_unix_new( g_out );
  g_io_channel_set_encoding (Lcom, NULL, NULL);
  g_io_channel_set_flags( Lcom, G_IO_FLAG_NONBLOCK, NULL );
  Lcomevent = g_io_add_watch (Lcom, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      readcd_makeimage_callback, Ag);

  Lcomerr = g_io_channel_unix_new( g_err );
  g_io_channel_set_encoding (Lcomerr, NULL, NULL);
  g_io_channel_set_flags( Lcomerr, G_IO_FLAG_NONBLOCK, NULL );
  Lcomerrevent = g_io_add_watch (Lcomerr, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
                                      readcd_makeimage_callback, Ag); 
  while (*Lcont>0 && *Labort == FALSE) {
   gtk_main_iteration();    
  }

  exit_prog(*Lpid, *Labort, Aerror, _("Error while reading CD"));

  g_source_remove(Lcomevent);
  g_source_remove(Lcomerrevent);

  /* fermeture readcd */
  g_io_channel_shutdown(Lcomerr, FALSE, NULL);
  g_io_channel_unref(Lcomerr);  
  g_io_channel_shutdown(Lcom, FALSE, NULL);
  g_io_channel_unref(Lcom);

  g_spawn_close_pid(*Lpid);
  *Lpid = 0;

  sc_grave_del_data(Ag, "readtodo");

  if (*Aerror) {
    _DEB("IL Y A UNE ERREUR !!");
    return FALSE;
  }

  return Lstatus;  
}

/* callback appele lors de la copie d'un cd a la vole
 * renvoi en fait ce qui est lu sur cdrecord */
#if 0

 NON UTILISE POUR LE MOMENT, ET A REVOIR ..
gboolean readcd_redirect_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GtkWidget *Lwindow1 = (GtkWidget *) Adata;
  gchar Lbuffer[_BUF_SIZE];
  gint *Lcont = (gint *) g_object_get_data(G_OBJECT(Lwindow1), "contsrc"); /* on traite encore des donnees ? */
  GError **Lerreur = (GError **) g_object_get_data(G_OBJECT(Lwindow1), "gerror"); /* pointeur erreur */
  GIOChannel *Lcdrecord = (GIOChannel *) g_object_get_data(G_OBJECT(Lwindow1), "cdrecord");
  gsize Llu = 0, Lecris = 0;

  /* fin du callback lorsque l'on recoi un signal comme quoi le pipe est ferme */
  if (Acond == G_IO_HUP || Acond == G_IO_ERR) {
    *Lcont = 0;
    return FALSE;
  }
  g_io_channel_read_chars(Astd, Lbuffer, _BUF_SIZE-1, &Llu, Lerreur);  
  if (!Llu) {
    _DEB("stop !");
    return FALSE;
  }
  Lbuffer[Llu]=0;
  
  g_io_channel_write_chars(Lcdrecord, (const gchar *)Lbuffer, Llu, &Lecris, Lerreur);

  return TRUE;
}
#endif


/* callback appele lorsque readcd determine la taille d'un cd */
gboolean readcd_getsize_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GIOStatus Lstatus;
  Tgrave *Lg = (Tgrave *)Adata;
  gint *Lcont = (gint *) sc_grave_get_data(Lg, "cont"); /* on traite encore des donnees ? */
  gint *Ltodo = (gint *) sc_grave_get_data(Lg, "todo"); /* nombre de piste a traiter */
  gchar *f, *e;
  gchar *Lbuffer = NULL;  

  /* fin du callback lorsque l'on recoi un signal comme quoi le pipe est ferme */
  if (Acond == G_IO_HUP || Acond == G_IO_ERR) {
    *Lcont = 0;
    return FALSE;
  }
  
  Lstatus = g_io_channel_read_line(Astd, &Lbuffer, NULL, NULL, NULL);  
  if (!Lbuffer) return TRUE;
  if ((f=strstr(Lbuffer, READCD_DETECTCAPACITY))) {
    f=f+strlen(READCD_DETECTCAPACITY);
    if ((e=strchr(f, ' '))) {
      *e=0;
      *Ltodo = atoi(f);
    }
    *Lcont = 0;
    return FALSE;
  }
  g_free(Lbuffer);

  return TRUE;
}

/*
 * vim:et:ts=8:sts=2:sw=2
 */
