/* growisofs.c
 * Copyright (C) 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"

#define GROWISOFS_CURRENT_WRITE_SPEED "\"Current Write Speed\" is "
#define GROWISOFS_DONE " done, estimate finish"
#define GROWISOFS_FLUSHING_CACHE ": flushing cache"
#define GROWISOFS_WRITING_LEADOUT ": writing lead-out"
#define GROWISOFS_FAILED ":-( write failed:"
#define GROWISOFS_USING "Using "

/* communication avec growisofs */

/* mise a jour du titre depuis le template prepare avec prepare_title */
void set_title(Tgrave *Ag, gchar *Avitesse) {
  GtkLabel *Llabel = GTK_LABEL(sc_grave_get_data(Ag, "gravetitle"));
  gchar *Ltitleshow = g_strdup_printf(_("Writing DVD in progress at %sx..."), Avitesse);

  gtk_label_set_text(Llabel, Ltitleshow);
  g_free(Ltitleshow);
}

gchar *get_title_preparing(gint Anbrgravure, gint Acurcd, gboolean Adosimul)
{
  if (Anbrgravure==1) {
    if (!Adosimul) {
      return g_strdup(_("DVD writing will start..."));
    } else {
      return g_strdup(_("Simulated DVD writing will start..."));
    }
  } else {
    if (!Adosimul) {
      return g_strdup_printf(_("DVD writing %d/%d will start..."), Acurcd, Anbrgravure);
    } else {
      return g_strdup_printf(_("Simulated DVD writing %d/%d will start..."), Acurcd, Anbrgravure);
    }
  }
}

/* callback appele lorsque cdrecord grave les pistes */
gboolean growisofs_grave_callback(GIOChannel *Astd, GIOCondition Acond, gpointer Adata)
{
  GIOStatus Lstatus;
  Tgrave *Lg = (Tgrave *)Adata;
  gchar *Lbuffer;
  gchar *s, *t;
  GtkProgressBar *Lprogressbar = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_total"));
  GtkProgressBar *Lprogressbar2 = GTK_PROGRESS_BAR(sc_grave_get_data(Lg, "pb_step"));
  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_data(Lg, "gravetitle"));
  GError **Lerreur = (GError **) sc_grave_get_data(Lg, "gerror"); /* pointeur erreur */
  gdouble Lpct, Ltava;
  gchar Lsbuf[100], Lsbuf2[100];
  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 */
  gint *Ldone = (gint *) sc_grave_get_data(Lg, "done"); /* nombre de piste deja traite */

  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) {
    if (Lstatus == G_IO_STATUS_ERROR || Lstatus == G_IO_STATUS_EOF)
      return FALSE;
    else
      return TRUE;
  }

_DEB("===>%s", Lbuffer);


  if ((s=strstr(Lbuffer, GROWISOFS_CURRENT_WRITE_SPEED))) {
    /* mise a jour du titre lorsque l'on recois la vitesse */ 
    s+=strlen(GROWISOFS_CURRENT_WRITE_SPEED);
    if ((t = strchr(s, 'x'))) {
      *t = 0;
      set_title(Lg, s);
    }
  } else if (!strncmp(Lbuffer, GROWISOFS_USING, strlen(GROWISOFS_USING))) {
    /* rien pour le moment */
  } else if (strstr(Lbuffer, GROWISOFS_DONE)) {
    /* gravure en cours ... */
    if ((t=strchr(Lbuffer, '.'))) {
      s=ltrim(Lbuffer);

      /* avancement tache */
      Lpct = 0.01 * atof(s);
      *t=0;

      gtk_progress_bar_set_fraction(Lprogressbar2, Lpct);
      g_snprintf(Lsbuf, sizeof(Lsbuf)-1, "%s%%", s);
      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(Lsbuf2, sizeof(Lsbuf2)-1, "%.0f%%", Ltava*100);
      gtk_progress_bar_set_text(Lprogressbar, Lsbuf2);
    }
  } else if (strstr(Lbuffer, GROWISOFS_FLUSHING_CACHE)) {
    gtk_label_set_text(Ltitle, _("Flushing cache..."));
    gtk_progress_bar_set_fraction(Lprogressbar2, 1);
    gtk_progress_bar_set_text(Lprogressbar2, _("100%"));
    Ltava = (1.0 / (*Ltodo)) * ((*Ldone));
    gtk_progress_bar_set_fraction(Lprogressbar, Ltava);
    g_snprintf(Lsbuf2, sizeof(Lsbuf2)-1, "%.0f%%", Ltava*100);
    gtk_progress_bar_set_text(Lprogressbar, Lsbuf2);
  } else if (strstr(Lbuffer, GROWISOFS_WRITING_LEADOUT)) {
    gtk_label_set_text(Ltitle, _("Writing lead-out..."));
  } else if ((s=strstr(Lbuffer, GROWISOFS_FAILED))) {
    gtk_label_set_text(Ltitle, _("Operation failed !"));
    s+=strlen(GROWISOFS_FAILED);
    g_set_error(Lerreur, GRAVEMAN_ERROR, _ERR_GROWISOFS, s ? s : _("Operation failed !"));
    return FALSE;
  }

  g_free(Lbuffer);

  return TRUE;
}

/* copie de donnees */
gboolean burn_data2dvd(Tgrave *Ag, GError **Aerror) {
  gchar **Lcmd;
  gchar *Lcommandline, *Ltxt;
  gchar *Lrepertoire = (gchar *)sc_grave_get_data(Ag, "tmpdir");
  gint *Lcont = (gint *) sc_grave_get_data(Ag, "cont");
  gboolean *Labort = (gboolean *) sc_grave_get_data(Ag, "gabort");
  GtkWindow *Lwindow = GTK_WINDOW(sc_grave_get_data(Ag, "window_burn"));
  gboolean Lsimul = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sc_grave_get_widget(Ag, "dvddatasimul")));
  gint Lnbrgravure = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sc_grave_get_widget(Ag, "nbrdvddata"))); 
  gint *Ldone = (gint *)sc_grave_get_data(Ag, "done"); /* fais */

  gboolean Lwanteject = conf_get_boolean("eject");

  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");
  GtkWidget *Lvitesse = sc_grave_get_widget(Ag, "dstdvddataspeed");
  Tdriveinfo *Ldevice = matos_get_drive_info(Ag, "dstdvddatacombo");
  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_data(Ag, "gravetitle"));
  gchar *Loperation = sc_grave_get_data(Ag, "typeburn");
  gboolean Leject = FALSE;
  gboolean Ldosimul;
  gchar *Lbufvitesse;
  gint Lcurcd;
  gint Lnbrpass=1;
  gint g_out, g_err, Lnbrarg;
  guint Lcomevent, Lcomerrevent;
  GIOChannel *Lcom, *Lcomerr;
  gchar *Lisopara = NULL;
  gboolean Lstatus;
  gint Lmediadetect1 = _MEDIA_CDRW;
  GtkToggleButton *Lbtnnotfix = GTK_TOGGLE_BUTTON(sc_grave_get_widget(Ag, "dvddatanotfix"));
  gboolean Lnotfix = Lbtnnotfix ? gtk_toggle_button_get_active(Lbtnnotfix) : FALSE;
_DEB("DVD DATA\n");
_DEB("nbr gravure = [%d]\n", Lnbrgravure);

  Lbufvitesse = get_combo_value(Lvitesse);
  Lisopara = make_image_getextrapara(Ag, "dvddata");

  for (Lcurcd=1; Lcurcd<= Lnbrgravure;
      ((Lsimul && Lnbrpass>1) || (!Lsimul)) ? ( Lcurcd++, Lnbrpass=1 ) : ( Lnbrpass++ )) {

    Ldosimul = (Lsimul && Lnbrpass==1);
    if (Lcurcd > 1 && !Ldosimul) {
      /* copie sur un nouveau dvd, on demande a l'utilisateur d'inserer le
       * nouveau dvdvierge */
      GSList *Llmediarequis1 = sc_grave_get_data(Ag, "mediarequis1");
      gint *Lmediatitle1 = sc_grave_get_data(Ag, "mediatitle1");
      gboolean Lstatus;
      Tgrave *Ldialoghash;
      GtkWidget *Lconfirm;
      gint Lrep;

      eject_cd(matos_get_device(Ldevice), NULL);
      Lstatus = waiting_for_user(*Lmediatitle1, Ag, Llmediarequis1 , &Lmediadetect1, Ldevice);
          
      if (Lstatus==FALSE) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE; break;
      } 
      
      Ldialoghash = create_dialog_select_dvdoperation(GTK_WIDGET(Lwindow), Lmediadetect1);
      Lconfirm = sc_grave_get_data(Ldialoghash, "window");
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));

      if (Lrep != GTK_RESPONSE_YES) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE;
      } else {
        GtkRadioButton *Labradio = GTK_RADIO_BUTTON(sc_grave_get_widget(Ldialoghash, "burninitial"));
        g_free(Loperation);
        Loperation = g_strdup(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(Labradio)) ? "-Z" : "-M");
      }
      gtk_widget_destroy(Lconfirm);

      if (*Labort == TRUE) break;
    }

    /* faut il ejecter le DVD apres l'operation ?
     * oui si l'utilisateur a cocher la case "ejecter le cd" ou
     * si il faut realiser d'autre gravure sur d'autres DVD */
    Leject = ((Lcurcd > 1 && (Lwanteject || Lcurcd<Lnbrgravure)) || (Lcurcd == 1 && !Ldosimul && Lwanteject));

    _DEB("gravure du cd [%d]", Lcurcd);

    Ltxt = get_title_preparing(Lnbrgravure, Lcurcd, Ldosimul);
    gtk_label_set_text(Ltitle, Ltxt);
    g_free(Ltxt);

    Lcommandline = g_strdup_printf("%s %s %s %s %s%s %s -gui -use-the-force-luke=tty%s %s -graft-points -path-list %s/pathlist",
        conf_get_string("growisofs"), 
        Lnotfix ? "" : "-dvd-compat",
        Loperation,
        matos_get_device(Ldevice),
        *Lbufvitesse != '0' ? "-speed=" : "", *Lbufvitesse != '0' ? Lbufvitesse : "",
        conf_get_boolean("overburn") ? "-overburn" : "",
        Ldosimul ? ",dummy" : "",  /* simulation ? */
        Lisopara,  /* parametres supplementaires tel que le nom de volume du cd */
        Lrepertoire
      );

    _DEB("execution [%s]\n", Lcommandline);
    Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
    g_free(Lcommandline);

    if (Lstatus == FALSE) {
      break;
    }

    Lstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, /* env argument */
        (GSpawnFlags ) (G_SPAWN_DO_NOT_REAP_CHILD),
        NULL, NULL, Lpid, NULL, &g_out, &g_err, Aerror);
    g_strfreev(Lcmd);

    if (Lstatus == FALSE) {
      g_warning("ERROR EXECUTION !\n");
      break;
    }

    *Lcont = 1;

    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),
                                        growisofs_grave_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),
                                        growisofs_grave_callback, Ag);  
    
    while (*Lcont>0 && *Labort == FALSE) {
      gtk_main_iteration(); 
    }
    exit_prog(*Lpid, *Labort, Aerror, NULL);

    g_source_remove(Lcomerrevent);
    g_source_remove(Lcomevent);

    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;
    
    if (*Aerror) {
      Lstatus = FALSE;
      break;
    }

    if (Ldosimul) {
      /* fin de la simulation, tout s'est apparement bien passe
       * on demande confirmation avent de commencer la vrai gravure */
      gint Lrep;
      GtkWidget *Lconfirm = gtk_message_dialog_new(Lwindow,
                                            GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
                                            GTK_BUTTONS_YES_NO, 
                      _("Simulation successful. Do you want to write the DVD for real?"));
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));
      gtk_widget_destroy(Lconfirm);
      if (Lrep == GTK_RESPONSE_NO) {
        /* si c'est non alors on arrete */
        *Labort = TRUE;
        Lstatus = TRUE;
        break;
      }
    }
    (*Ldone)++;
  }

  g_free(Lisopara);
  g_free(Lbufvitesse);


  return *Aerror ? FALSE : TRUE;
}

/* copie d'une image iso */
gboolean burn_iso2dvd(Tgrave *Ag, GError **Aerror) {
  gchar **Lcmd;
  gchar *Lcommandline, *Ltxt;
  gchar *Lrepertoire = (gchar *)sc_grave_get_data(Ag, "tmpdir");
  gint *Lcont = (gint *) sc_grave_get_data(Ag, "cont");
  gboolean *Labort = (gboolean *) sc_grave_get_data(Ag, "gabort");
  GtkWindow *Lwindow = GTK_WINDOW(sc_grave_get_data(Ag, "window_burn"));
  gboolean Lsimul = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sc_grave_get_widget(Ag, "dvddatasimul")));
  gint Lnbrgravure = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sc_grave_get_widget(Ag, "nbrdvddata"))); 
  gint *Ldone = (gint *)sc_grave_get_data(Ag, "done"); /* fais */

  gboolean Lwanteject = conf_get_boolean("eject");

  gint *Lpid = (gint *) sc_grave_get_data(Ag, "pid");
  GtkWidget *Lvitesse = sc_grave_get_widget(Ag, "dstdvddataspeed");
  Tdriveinfo *Ldevice = matos_get_drive_info(Ag, "dstdvddatacombo");
  GtkLabel *Ltitle = GTK_LABEL(sc_grave_get_data(Ag, "gravetitle"));
  gchar *Loperation = sc_grave_get_data(Ag, "typeburn");
  gboolean Leject = FALSE;
  gboolean Ldosimul;
  gchar *Lbufvitesse;
  gint Lcurcd;
  gint Lnbrpass=1;
  gint g_out, g_err, Lnbrarg;
  guint Lcomevent, Lcomerrevent;
  GIOChannel *Lcom, *Lcomerr;
  gchar *Lisopara = NULL;
  gboolean Lstatus;
  gint Lmediadetect1 = _MEDIA_CDRW;
  GtkToggleButton *Lbtnnotfix = GTK_TOGGLE_BUTTON(sc_grave_get_widget(Ag, "dvddatanotfix"));
  gboolean Lnotfix = Lbtnnotfix ? gtk_toggle_button_get_active(Lbtnnotfix) : FALSE;
_DEB("DVD DATA\n");
_DEB("nbr gravure = [%d]\n", Lnbrgravure);

  Lbufvitesse = get_combo_value(Lvitesse);
  Lisopara = make_image_getextrapara(Ag, "dvddata");

  for (Lcurcd=1; Lcurcd<= Lnbrgravure;
      ((Lsimul && Lnbrpass>1) || (!Lsimul)) ? ( Lcurcd++, Lnbrpass=1 ) : ( Lnbrpass++ )) {

    Ldosimul = (Lsimul && Lnbrpass==1);
    if (Lcurcd > 1 && !Ldosimul) {
      /* copie sur un nouveau dvd, on demande a l'utilisateur d'inserer le
       * nouveau dvdvierge */
      GSList *Llmediarequis1 = sc_grave_get_data(Ag, "mediarequis1");
      gint *Lmediatitle1 = sc_grave_get_data(Ag, "mediatitle1");
      gboolean Lstatus;
      Tgrave *Ldialoghash;
      GtkWidget *Lconfirm;
      gint Lrep;

      eject_cd(matos_get_device(Ldevice), NULL);
      Lstatus = waiting_for_user(*Lmediatitle1, Ag, Llmediarequis1 , &Lmediadetect1, Ldevice);
          
      if (Lstatus==FALSE) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE; break;
      } 
      
      Ldialoghash = create_dialog_select_dvdoperation(GTK_WIDGET(Lwindow), Lmediadetect1);
      Lconfirm = sc_grave_get_data(Ldialoghash, "window");
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));

      if (Lrep != GTK_RESPONSE_YES) {
        /* si c'est non alors on arrete */
        *Labort = TRUE; Lstatus = TRUE;
      } else {
        GtkRadioButton *Labradio = GTK_RADIO_BUTTON(sc_grave_get_widget(Ldialoghash, "burninitial"));
        g_free(Loperation);
        Loperation = g_strdup(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(Labradio)) ? "-Z" : "-M");
      }
      gtk_widget_destroy(Lconfirm);

      if (*Labort == TRUE) break;
    }

    /* faut il ejecter le DVD apres l'operation ?
     * oui si l'utilisateur a cocher la case "ejecter le cd" ou
     * si il faut realiser d'autre gravure sur d'autres DVD */
    Leject = ((Lcurcd > 1 && (Lwanteject || Lcurcd<Lnbrgravure)) || (Lcurcd == 1 && !Ldosimul && Lwanteject));

    _DEB("gravure du cd [%d]", Lcurcd);

    Ltxt = get_title_preparing(Lnbrgravure, Lcurcd, Ldosimul);
    gtk_label_set_text(Ltitle, Ltxt);
    g_free(Ltxt);

    Lcommandline = g_strdup_printf("%s %s %s %s %s%s %s -gui -use-the-force-luke=tty%s %s -graft-points -path-list %s/pathlist",
        conf_get_string("growisofs"), 
        Lnotfix ? "" : "-dvd-compat",
        Loperation,
        matos_get_device(Ldevice),
        *Lbufvitesse != '0' ? "-speed=" : "", *Lbufvitesse != '0' ? Lbufvitesse : "",
        conf_get_boolean("overburn") ? "-overburn" : "",
        Ldosimul ? ",dummy" : "",  /* simulation ? */
        Lisopara,  /* parametres supplementaires tel que le nom de volume du cd */
        Lrepertoire
      );

    _DEB("execution [%s]\n", Lcommandline);
    Lstatus = g_shell_parse_argv(Lcommandline, &Lnbrarg, &Lcmd, Aerror);
    g_free(Lcommandline);

    if (Lstatus == FALSE) {
      break;
    }

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

    Lstatus = g_spawn_async_with_pipes(NULL, Lcmd, NULL, /* env argument */
        (GSpawnFlags ) (G_SPAWN_DO_NOT_REAP_CHILD),
        NULL, NULL, Lpid, NULL, &g_out, &g_err, Aerror);
    g_strfreev(Lcmd);

    if (Lstatus == FALSE) {
      g_warning("ERROR EXECUTION !\n");
      break;
    }

    *Lcont = 1;

    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),
                                        growisofs_grave_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),
                                        growisofs_grave_callback, Ag);  
    
    while (*Lcont>0 && *Labort == FALSE) {
      gtk_main_iteration(); 
    }
    exit_prog(*Lpid, *Labort, Aerror, NULL);

    g_source_remove(Lcomerrevent);
    g_source_remove(Lcomevent);

    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;
    
    if (*Aerror) {
      Lstatus = FALSE;
      break;
    }

    if (Ldosimul) {
      /* fin de la simulation, tout s'est apparement bien passe
       * on demande confirmation avent de commencer la vrai gravure */
      gint Lrep;
      GtkWidget *Lconfirm = gtk_message_dialog_new(Lwindow,
                                            GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
                                            GTK_BUTTONS_YES_NO, 
                      _("Simulation successful. Do you want to write the DVD for real?"));
      Lrep = gtk_dialog_run(GTK_DIALOG(Lconfirm));
      gtk_widget_destroy(Lconfirm);
      if (Lrep == GTK_RESPONSE_NO) {
        /* si c'est non alors on arrete */
        *Labort = TRUE;
        Lstatus = TRUE;
        break;
      }
    }
    (*Ldone)++;
  }

  g_free(Lisopara);
  g_free(Lbufvitesse);


  return *Aerror ? FALSE : TRUE;
}

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