#include "MountieDialog.h"
#include "MountieMemory.h"
#include "BlkDevInfo.h"
#include "PrognameDialog.h"
#include <QtDebug>

MountieDialog::MountieDialog(QWidget * parent)
 : QDialog(parent),
   dir_browser(NULL),
   context_menu(NULL),
   eject_action(NULL),
   proc(this),
   pd(NULL),
   movie(NULL),
   pix(NULL) {

   BlkDevInfo::ReadMountInfo();
   ui.setupUi(this);
   unmounted.setupUi(ui.tab);
   mounted.setupUi(ui.tab_2);
   BlkDevInfo::PopulateUnmounted(unmounted.tableWidget);
   BlkDevInfo::PopulateMounted(mounted.tableWidget);
   MountieMemory::Get()->GetHistory(unmounted.comboBox);
   FiddleOKButton();

   connect(unmounted.pushButton, SIGNAL(clicked(bool)),
           this, SLOT(BrowseButtonPushed()));
   connect(unmounted.comboBox, SIGNAL(editTextChanged(const QString &)),
           this, SLOT(MountpointChanged(const QString &)));
   connect(unmounted.tableWidget, SIGNAL(itemSelectionChanged()),
           this, SLOT(SelectionChanged()));
   connect(mounted.tableWidget, SIGNAL(itemSelectionChanged()),
           this, SLOT(SelectionChanged()));
   connect(ui.tabWidget, SIGNAL(currentChanged(int)),
           this, SLOT(TabChanged(int)));
   connect(ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked(bool)),
           this, SLOT(DoIt()));
   connect(&proc, SIGNAL(readyReadStandardOutput()),
           this, SLOT(ReadStdout()));
   connect(&proc, SIGNAL(readyReadStandardError()),
           this, SLOT(ReadStderr()));
   connect(&proc, SIGNAL(finished(int, QProcess::ExitStatus)),
           this, SLOT(CommandDone(int, QProcess::ExitStatus)));
   connect(this, SIGNAL(finished(int)), this, SLOT(SaveFinalSize()));

   CreateMenu();
}

MountieDialog::~MountieDialog() {
}

void
MountieDialog::BrowseButtonPushed() {
   if (dir_browser == NULL) {
      dir_browser = new QFileDialog(this, "Select Mountpoint", "/");
      dir_browser->setFileMode(QFileDialog::DirectoryOnly);
      dir_browser->setAcceptMode(QFileDialog::AcceptOpen);
      dir_browser->setViewMode(QFileDialog::List);
      dir_browser->setReadOnly(false);
   }
   if (dir_browser->exec() == QDialog::Accepted) {
      QStringList dirnames = dir_browser->selectedFiles();
      unmounted.comboBox->insertItem(0, dirnames[0]);
      unmounted.comboBox->setCurrentIndex(0);
   }
}

void
MountieDialog::MountpointChanged(const QString & /* text */) {
   FiddleOKButton();
}

void
MountieDialog::SelectionChanged() {
   FiddleOKButton();
}

void
MountieDialog::TabChanged(int /* index */) {
   FiddleOKButton();
}

void
MountieDialog::DoIt() {
   QString command;
   QStringList args;
   switch (ui.tabWidget->indexOf(ui.tabWidget->currentWidget())) {
   case 0:
      {
         QList<QTableWidgetItem *> list = unmounted.tableWidget->selectedItems();
         if (! list.isEmpty()) {
            QString devname;
            QString mountpoint = unmounted.comboBox->currentText();
            for (int i = 0; i < list.size(); ++i) {
               QTableWidgetItem * item = list.at(i);
               if (item->column() == 0) {
                  devname = item->text();
                  break;
               }
            }
            if ((devname.size() > 0) && (mountpoint.size() > 0)) {
               command = "mount";
               args << devname;
               args << mountpoint;
               MountieMemory::Get()->SaveHistory(mountpoint);
            }
         }
      }
      break;
   case 1:
      {
         QList<QTableWidgetItem *> list = mounted.tableWidget->selectedItems();
         if (! list.isEmpty()) {
            for (int i = 0; i < list.size(); ++i) {
               QTableWidgetItem * item = list.at(i);
               if (item->column() == 1) {
                  QString mountpoint = item->text();
                  if (mountpoint.size() > 0) {
                     command = "umount";
                     args << mountpoint;
                     MountieMemory::Get()->SaveHistory(mountpoint);
                  }
               }
            }
         }
      }
      break;
   }
   if (command.size() > 0) {
      RunOneCommand(command, args);
   }
}

void
MountieDialog::ReadStdout() {
   stdout_accum.append(proc.readAllStandardOutput());
}

void
MountieDialog::ReadStderr() {
   stderr_accum.append(proc.readAllStandardError());
}

void
MountieDialog::CommandDone(int exitCode, QProcess::ExitStatus exitStatus) {
   if (movie != NULL) {
      movie->stop();
      ui.dudley->setMovie(NULL);
   }
   if (pix == NULL) {
      pix = new QPixmap(":/new/prefix1/dudley-50x50.png");
   }
   ui.dudley->setPixmap(*pix);
   ui.message->setText("");
   ReadStdout();
   ReadStderr();
   QString se = stderr_accum.trimmed();
   QString so = stdout_accum.trimmed();
   if (so.size() > 0) {
      so.append("\n");
   }
   so.append(se);
   if ((exitCode == 0) &&
       (exitStatus == QProcess::NormalExit) &&
       (so.size() == 0)) {

      // Everything worked, no feedback to provide, just close up shop.
      // (Unless, of course, the stay open option is set, in which case
      // refresh the info to reflect command just run).

      MountieMemory::Get()->SaveSize(size());
      if (MountieMemory::Get()->GetStayOpen()) {
         RefreshTables();
         FiddleOKButton();
      } else {
         accept();
      }
   } else {
      RefreshTables();
      FiddleOKButton();
      if (so.size() == 0) {

         // No error info from the command, but it didn't finish
         // properly, just report the abbynormal termination code.

         so = QString("exit code: %1, exit status: %2")
              .arg(exitCode)
              .arg((exitStatus == QProcess::NormalExit) ? "Normal" : "Crashed");
      }
      QMessageBox::critical(this, "Error", so);
   }
}

void
MountieDialog::RefreshTables() {
   BlkDevInfo::ReadMountInfo();
   BlkDevInfo::PopulateUnmounted(unmounted.tableWidget);
   BlkDevInfo::PopulateMounted(mounted.tableWidget);
   FiddleOKButton();
}

void
MountieDialog::SetStayOpen(bool checked) {
   MountieMemory::Get()->SaveStayOpen(checked);
}

void
MountieDialog::SetKeepHistory(bool checked) {
   MountieMemory::Get()->SaveKeepHistory(checked);
   MountieMemory::Get()->GetHistory(unmounted.comboBox);
}

void
MountieDialog::SetMountVia() {
   if (pd == NULL) {
      pd = new PrognameDialog(this);
   }
   pd->exec();
}

void
MountieDialog::SaveFinalSize() {
   MountieMemory::Get()->SaveSize(size());
}

void
MountieDialog::DoEject() {
   QString command = "eject";
   QStringList args;
   args << "-T";
   QAction *action = qobject_cast<QAction *>(sender());
   if (action != NULL) {
      QString devname = action->data().toString();
      if (devname.size() > 0) {
         // Looks like we found the device name we want to eject.
         args << devname;
      }
   }
   RunOneCommand(command, args);
}

void
MountieDialog::contextMenuEvent(QContextMenuEvent *event) {
   CreateMenu();
   FiddleEjectAction();
   context_menu->exec(event->globalPos());
}

void
MountieDialog::FiddleOKButton() {
   QString ok_text = "OK";
   bool ok_enabled = false;
   switch (ui.tabWidget->indexOf(ui.tabWidget->currentWidget())) {
   case 0:
      {

         // We are on the Unmounted tab, so button action is "Mount"

         ok_text = "Mount";
         QList<QTableWidgetItem *> list = unmounted.tableWidget->selectedItems();
         if (! list.isEmpty()) {
            QDir dirname(unmounted.comboBox->currentText());
            if (dirname.exists() && (dirname.count() <= 2)) {

               // We have selected an item and the mountpoint is an
               // existing empty directory. Looks like all is well.

               ok_enabled = true;
            }
         }
      }
      break;
   case 1:
      {

         // We are on the Mounted tab, so button action is "Unmount"

         ok_text = "Unmount";
         QList<QTableWidgetItem *> list = mounted.tableWidget->selectedItems();
         if (! list.isEmpty()) {

            // There is one or more mounted files selected, enable button.

            ok_enabled = true;
         }
      }
      break;
   }
   QPushButton * ok_btn = ui.buttonBox->button(QDialogButtonBox::Apply);
   ok_btn->setText(ok_text);
   if (proc.state() != QProcess::NotRunning) {
      // Don't enable button is process is currently active.
      ok_enabled = false;
   }
   ok_btn->setEnabled(ok_enabled);
}

void
MountieDialog::RunOneCommand(QString command, QStringList & args) {
   QString progname = MountieMemory::Get()->GetProgname();
   if (progname.size() > 0) {
      args.prepend(command);
      command = progname;
   }
   QString msg("Running: ");
   msg.append(command);
   for (int i = 0; i < args.size(); ++i) {
      msg.append(" ");
      msg.append(args.at(i));
   }
   ui.message->setText(msg);
   stderr_accum = QString();
   stdout_accum = QString();
   if (movie == NULL) {
      movie = new QMovie(":/new/prefix1/spinning-dudley.gif");
   }
   ui.dudley->setMovie(movie);
   movie->start();
   proc.start(command, args, QIODevice::ReadOnly);
   FiddleOKButton();
}

void
MountieDialog::FiddleEjectAction() {
   if ((eject_action != NULL) &&
       (prev_ejectable != BlkDevInfo::GetEjectableDevices())) {

      // Someone plugged in a USB cdrom or something. Need to
      // flush the previous eject menu item and rebuild

      context_menu->removeAction(eject_action);

      // Will this delete submenu as well, or do I need to traverse?

      delete eject_action;
      eject_action = NULL;
   }
   if (eject_action == NULL) {

      // Remember list of ejectable devices so we can see if it
      // changed since we last popped up menu...

      prev_ejectable = BlkDevInfo::GetEjectableDevices();
      if (prev_ejectable.count() <= 1) {
         eject_action = new QAction("Eject", this);
         connect(eject_action, SIGNAL(triggered(bool)),
                 this, SLOT(DoEject()));
      } else {
         eject_action = new QAction("Eject", this);
         QMenu * eject_menu = new QMenu(this);
         for (int i = 0; i < prev_ejectable.count(); ++i) {
            QString devname = prev_ejectable.at(i);
            QString label = "Eject ";
            label.append(BlkDevInfo::GetDeviceDescription(devname));
            QAction * e = new QAction(label, this);
            e->setData(devname);
            eject_menu->addAction(e);
            connect(e, SIGNAL(triggered(bool)),
                 this, SLOT(DoEject()));
         }
         eject_action->setMenu(eject_menu);
      }
      eject_action->setIcon(QIcon(":/new/prefix1/eject.png"));
      context_menu->insertAction(mount_via_action, eject_action);
   }
}

void
MountieDialog::CreateMenu() {
   if (context_menu == NULL) {
      refresh_action = new QAction("&Refresh", this);
      refresh_action->setIcon(QIcon(":/new/prefix1/refresh.png"));
      refresh_action->setShortcut(tr("Ctrl+R"));
      addAction(refresh_action);
      connect(refresh_action, SIGNAL(triggered(bool)),
              this, SLOT(RefreshTables()));

      stay_open_action = new QAction("Stay &Open", this);
      stay_open_action->setCheckable(true);
      stay_open_action->setChecked(MountieMemory::Get()->GetStayOpen());
      stay_open_action->setShortcut(tr("Ctrl+O"));
      addAction(stay_open_action);
      connect(stay_open_action, SIGNAL(triggered(bool)),
              this, SLOT(SetStayOpen(bool)));

      keep_history_action = new QAction("Keep &History", this);
      keep_history_action->setCheckable(true);
      keep_history_action->setChecked(MountieMemory::Get()->GetKeepHistory());
      keep_history_action->setShortcut(tr("Ctrl+H"));
      addAction(keep_history_action);
      connect(keep_history_action, SIGNAL(triggered(bool)),
              this, SLOT(SetKeepHistory(bool)));

      mount_via_action = new QAction("Mount/Unmount &Via...", this);
      mount_via_action->setShortcut(tr("Ctrl+V"));
      addAction(mount_via_action);
      connect(mount_via_action, SIGNAL(triggered(bool)),
              this, SLOT(SetMountVia()));

      exit_action = new QAction("&Exit", this);
      exit_action->setIcon(QIcon(":/new/prefix1/exit.png"));
      exit_action->setShortcut(tr("Ctrl+X"));
      addAction(exit_action);
      connect(exit_action, SIGNAL(triggered(bool)), qApp, SLOT(quit()));

      context_menu = new QMenu(this);
      context_menu->addAction(refresh_action);
      context_menu->addAction(stay_open_action);
      context_menu->addAction(keep_history_action);
      context_menu->addAction(mount_via_action);
      context_menu->addSeparator();
      context_menu->addAction(exit_action);
   }
}
