//This file is part of AcetoneISO. Copyright 2006,2007,2008,2009 Marco Di Antonio and Fabrizio Di Marco (acetoneiso@gmail.com)
//
//    AcetoneISO 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 3 of the License, or
//    (at your option) any later version.
//
//    AcetoneISO 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 AcetoneISO.  If not, see <http://www.gnu.org/licenses/>.
#include <QtGui>
#include <QByteArray>
#include <QProcess>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QList>
#include <QVariant>
#include <fcntl.h>
#include <iostream>
#include <QDebug>
#include <Phonon/MediaObject>
#include <Phonon/AudioOutput>
#include "erase_dvd.h"

/*
cancellare cd completo: wodim -verbose dev=/dev/sr0  blank=all
cancellare cd rapido: wodim -verbose dev=/dev/sr0  blank=fast
dice il tipo di media inserito: hal-get-property "--key volume.disc.type --udi $udi
*/

//
erasedvd::erasedvd( QWidget * parent, Qt::WFlags f) 
	: QDialog(parent, f)
{

    setupUi(this);
    is_erasing = false;
    loaded_success = false;
    QCoreApplication::setApplicationName("AcetoneISO");
    connect( start, SIGNAL( clicked() ), this, SLOT( start_erase() ) );

    textBrowser->clear();
    device_scan();
   

}




void erasedvd::device_scan() {


/*creo la connessione dbus a hal*/
QDBusConnection conn = QDBusConnection::systemBus();
QDBusInterface hal("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager", conn);
/*cerco tutti i dispositivi storage.cdrom con hal via dbus*/
QDBusMessage msg = hal.call( "FindDeviceByCapability", "storage.cdrom");

QList<QVariant> devices = msg.arguments(); 
/*per ogni udi (dispositivo cdrom) che trovo, ricavo vendor, model, velocità e path*/

//disabilita tutto se non viene trovato almeno 1 periferica
int count = devices.count();
if (count < 1) {
 QMessageBox::warning(this, "AcetoneISO::warning", tr("No CD/DVD device found.\nIf you think this is a bug please report it."));
 disable_buttons();
 devices_combo->addItem( tr("No CD/DVD device found") );
 return;
}


QIcon icon_optical_drive( ":/images/drive-optical.png" );

//scannerizzo tutte le periferiche e le aggiungo nella combobox. questa funzione viene chiamato una sola volta.
QVariant name(devices);
int cc = 0;
int success = -1;
while (cc < count) {
		  QString cdrom = name.toStringList()[0]; 
		  qDebug() << "Found device: " << cdrom; 
		  QDBusInterface device("org.freedesktop.Hal", cdrom, "org.freedesktop.Hal.Device", conn);
		  
		  //vedo se effittivamente il device puo scrivere su dischi dvd-rw
//controllo in seguito se puo scrivere su dvd+rw dal momento che se puo scrivere su dvd+rw puo anche farlo su dvd-rw ma questo ragionamento non funziona al contrario
		  msg = device.call("GetProperty", "storage.cdrom.dvdrw");
		  QVariant vcan_write_cdrw = msg.arguments()[0];
		  bool can_write_cdrw = vcan_write_cdrw.toBool();
		  if (!can_write_cdrw) {
		    cc = cc + 1;
		    continue;
		  }
		  //success si ricorda sempre la posizione in cui aggiungere all'array indipendentemente dal cc del ciclo while
		  success = success + 1;
		  //aggiungo id nell'array
		  id_device.insert(success, cdrom);		  
		  /*ottengo il nome del vendor del cdrom*/
		  msg = device.call("GetProperty", "storage.vendor");
		  QVariant var = msg.arguments()[0]; 
		  QString vendor = var.toStringList()[0];
		  qDebug() << "VENDOR" << vendor;
		  /*ottengo il nome del modello del cdrom*/
		  msg = device.call("GetProperty", "storage.model");
		  QVariant var2 = msg.arguments()[0]; 
		  QString model = var2.toStringList()[0];
		  qDebug() << "MODEL" << model;

		  /*ottengo il path del dispositivo cdrom*/
		  msg = device.call("GetProperty", "block.device");
		  QVariant var3 = msg.arguments()[0]; 
		  device_path.insert(success, var3.toStringList()[0]);
		  qDebug() << "PATH" << device_path;
		  
		  msg = device.call("GetProperty", "storage.firmware_version");
		  QVariant firmwarev = msg.arguments()[0];
		  QString firmware = firmwarev.toString();
		   /*aggiungo il nome del device nella combobox*/
		  QString final_device = vendor.append( "\t" + model + " " + firmware );
		  devices_combo->insertItem(success,icon_optical_drive, " " + final_device );
		  
		  cc = cc + 1;
		  }

media_available();
//if < 1 significa che sebbene ci sono 1 o piu periferiche cdrom, nessuna e' capace di scrivere su cd-rw
if (devices_combo->count() < 1) {
disable_buttons();
QMessageBox::warning(this, "AcetoneISO::warning", tr("No CD/DVD device found capable of writing to DVD-RW discs.\nIf you think this is a bug please report it."));
return; 
}
else { //c'e' almeno 1 periferica cdrom in grado di scrivere su dischi cd-rw
loaded_success = true;
//crea un timer cosi aggiorna automaticamente la label_info
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(media_available()));
timer->start(2000);
connect( devices_combo, SIGNAL(currentIndexChanged(int) ), this , SLOT( combo_changed(int) ) ); 
}

}


void erasedvd::media_available() {
//qDebug() << "media_available()";
//se sto masterizzando deve uscire da qui
if (is_erasing) {
start->setEnabled(false);
label_info->setText(tr("The")+ " " + discotipo + " " + tr("is getting blanked..."));
return;
}

//if < 1 it means there are no devices capaci di scrivere su dischi cd-rw
if (devices_combo->count() < 1) {
return; 
}

//se non e' visibile deve disconnettere le connessioni
if (loaded_success) {
  if (!devices_combo->isVisible()) {
    disconnect(devices_combo, 0, 0, 0);
    disconnect(timer, 0, 0, 0);
    loaded_success = false;
  }
}
//scopro se ho un cd dentro il device
/*creo la connessione dbus a hal*/
QDBusConnection conn = QDBusConnection::systemBus();
QDBusMessage msg;
//prendo indice corrente della combobox cosi so chi l'id della periferica selezionata
int current_index = devices_combo->currentIndex();
QString id = id_device[current_index];

//mi connetto alla periferica corrente selezionata in combobox
QDBusInterface device("org.freedesktop.Hal", id, "org.freedesktop.Hal.Device", conn);
//vedo se e' inserito un cd 
msg = device.call("GetProperty", "storage.removable.media_available");
QVariant var5 = msg.arguments()[0];
bool opticDev = var5.toBool();
QString plusminus = QString::fromUtf8("\xC2\xB1"); //0xC2 0xB1
if (!opticDev) {
  label_info->setText(tr("Insert a DVD") + plusminus + "RW " + tr("disc.")  );
  start->setEnabled(false);
  //devices_combo->setItemText(cc,tr("No Media Inserted, insert a media and click on refresh button."));
  return;
}

//vedo il tipo di cd inserito
QDBusMessage fbp;
QDBusInterface newh("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager", conn);
fbp = newh.call("FindDeviceStringMatch", "info.parent", id);
QList<QVariant> var6 = fbp.arguments();
QString disc_type = var6.at(0).toString();
QDBusInterface device_type("org.freedesktop.Hal", disc_type, "org.freedesktop.Hal.Device", conn);
msg = device_type.call("GetProperty", "volume.disc.type");
QString var7 = msg.arguments()[0].toString();
QString disc_is = var7;
//ferma in caso che il disco inserito non sia un disco DVD±RW
if ( (disc_is != "dvd_rw") and (disc_is != "dvd_plus_rw") ) {
label_info->setText(tr("The disc isn't a DVD") + plusminus + "RW. " +tr("Please insert a DVD") + plusminus + "RW " + tr("disc."));
start->setEnabled(false); 
return;
}

if (disc_is == "dvd_rw") {
discotipo = "DVD-RW";  
}
else {
discotipo = "DVD+RW";
msg = device.call("GetProperty", "storage.cdrom.dvdplusrw");
QVariant vcan_write_cdrw = msg.arguments()[0];
bool discobool = vcan_write_cdrw.toBool();
  if (!discobool) {
    label_info->setText( tr("You inserted a DVD+RW disc, however your CD/DVD device is uncapable of writing to DVD+RW discs.") ); 
    start->setEnabled(false); 
    return;
  }
}


label_info->setText(discotipo + " " + tr("succesfully found in device."));
start->setEnabled(true);

}

//gestisce quando l'utente cambia periferica nella combobox
void erasedvd::combo_changed(int n) {
int a;
a = n;
media_available(); 
}


void erasedvd::start_erase() {
  
QString type = discotipo;
QMessageBox msgBox;
msgBox.setText(tr("You decided to blank the") + " " + discotipo + tr("\nAre you sure?"));
 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
 switch (msgBox.exec()) {
 case QMessageBox::Yes:
    {
      //continue below
     }
     break;
 case QMessageBox::No:
 {
  return; //exit
 }
     break;
 default:
     // should never be reached
     break;
 }

QString erase_type;
if (radioButton->isChecked()) {
erase_type = "complete";
}
else {
erase_type = "fast";
}


disable_buttons();
QString dev = device_path.at(devices_combo->currentIndex());

//dev = dev.prepend("dev=");
textBrowser->clear(); 
erase = new QProcess();
erase->setReadChannel(QProcess::StandardOutput);
erase->setProcessChannelMode(QProcess::MergedChannels);	
//connection to update the display
connect(erase, SIGNAL(readyReadStandardOutput()), SLOT(updateEraseDisplay() )); 
connect(erase, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(printOutErase(int, QProcess::ExitStatus)));
QDir bin("/usr/bin");
QDir::setCurrent( bin.absolutePath() );
is_erasing = true;

if (erase_type == "fast") {
erase->start("dvd+rw-format",QStringList()  << dev );
}
else {
erase->start("dvd+rw-format",QStringList()  << "-force" << dev );
}




}

//fa loutput nel display del processo wodim
void erasedvd::updateEraseDisplay() {
erase_output = erase->readAllStandardOutput(); 
//questo evita che prende un output vuoto
if (erase_output.size() > 2 ) {
eraseoutput_real = erase_output; //questo si ricorda sempre dell'ultimo output NON vuoto
textBrowser->setPlainText(erase_output);
}
else {
textBrowser->setPlainText(eraseoutput_real); 
}

}

//gestisce quando il processo dvd+rw-format finisce
void erasedvd::printOutErase(int, QProcess::ExitStatus) {
enable_buttons();
is_erasing = false;

//controllo se il dvd e' gia formattato o se cmq non necessita di una formattazione secondo il sofware dvd+r-format
QByteArray str = "already";
bool contains = eraseoutput_real.contains(str); //prendo l'ultimo output non vuoto
eraseoutput_real = "";
if (contains) {
 QMessageBox::information(this, "AcetoneISO",tr("The") + " " + discotipo + " " + tr("is already formatted.\nThere is no need to format it because you can simply overwrite the media.\nIf you really want to blank it, choose Blank the Entire disc in the below window but we highly discourage you from doing so.\nOverwriting the") + " " + discotipo + " " + tr("is the simplest and safest thing to do."));
return;
}


if (eject->isChecked()) {
QString dev = device_path.at(devices_combo->currentIndex());
EJect.start("eject",QStringList()  << dev );
}

int valore_uscita = erase->exitCode();
if(valore_uscita == 0) {
  if (playcheckbox->isChecked()) {
    play_success();
  }
  QMessageBox::information(this, "AcetoneISO",tr("Process Succesfully Finished!"));
  textBrowser->clear();
}
else {
  if (playcheckbox->isChecked()) {
    play_error();
  }
  QMessageBox::critical(this, "AcetoneISO","Process Error Code: " + QString::number(valore_uscita) );
}
}

//abilita i bottoni
void erasedvd::enable_buttons() { 
start->setEnabled(true);
devices_combo->setEnabled(true);
eject->setEnabled(true);
radioButton->setEnabled(true);
radioButton_2->setEnabled(true);
playcheckbox->setEnabled(true);
}
//disabilita i bottoni
void erasedvd::disable_buttons() {
start->setEnabled(false);
devices_combo->setEnabled(false);
eject->setEnabled(false);
radioButton->setEnabled(false);
radioButton_2->setEnabled(false); 
playcheckbox->setEnabled(false);
}


void erasedvd::play_success() {
Phonon::MediaObject *mediaObject;	
Phonon::AudioOutput *audioOutput; 
Phonon::Path path;
mediaObject = new Phonon::MediaObject();
audioOutput = new Phonon::AudioOutput(Phonon::NoCategory);
path = Phonon::createPath(mediaObject, audioOutput);
QString file(":/audio/success.ogg");
mediaObject->setCurrentSource(Phonon::MediaSource(file));
mediaObject->play();
}

void erasedvd::play_error() {
Phonon::MediaObject *mediaObject;	
Phonon::AudioOutput *audioOutput; 
Phonon::Path path;
mediaObject = new Phonon::MediaObject();
audioOutput = new Phonon::AudioOutput(Phonon::NoCategory);
path = Phonon::createPath(mediaObject, audioOutput);
QString file(":/audio/error.ogg");
mediaObject->setCurrentSource(Phonon::MediaSource(file));
mediaObject->play();
}


