/* 
 * This file is part of DesktopBSD, see the README file.
 *
 * Author: Peter Hofer <ph@desktopbsd.net>
 *         (C) 2004, 2005
 *
 * Copyright: BSD, see the COPYING file included in this distribution
 *
 */

#include <mountertray.h>
#include <graphics.h>

#include <qptrlist.h>
#include <kpopupmenu.h>
#include <executor.h>
#include <mountersettings.h>

#include <kglobal.h>
#include <qprocess.h>
#include <qdragobject.h>
#include <kiconloader.h>
#include <kapplication.h>
#include <kmessagebox.h>
#include <kpassivepopup.h>
#include <qtimer.h>



MounterTray::MounterTray() {
   connect(contextMenu(), SIGNAL(activated(int)), SLOT(mountUnmount(int)));
   setPixmap(getIcon());
}

QPixmap MounterTray::getIcon() {
	return loadIcon("hdd_mount");
}


void MounterTray::mousePressEvent(QMouseEvent *e)
{
	showMenu(e->globalPos());
}


void MounterTray::showMenu()
{
	showMenu(mapToGlobal(QPoint(width() / 2, height() / 2)));
}


/** Show the popup menu at the specified position. */
void MounterTray::showMenu(const QPoint & pos)
{
	contextMenuAboutToShow(contextMenu());
   contextMenu()->popup(pos);
}


void  MounterTray::browseDevs() {
   if(!QProcess(QStringList::split(' ', "/usr/local/bin/kfmclient exec file:/media")).start())
   	KMessageBox::error(NULL,  tr("Cannot launch konqueror!"));
}



void  MounterTray::clearDevs() {
   QMapIterator<QString, MntDevice *>  devIt = devList.begin();
   while(devIt != devList.end()) {
      MntDevice  *mdev = devIt.data();
      devList.erase(devIt++);
      delete  mdev;
   }
}



void  MounterTray::refreshAll() {
   /* Removes all devices which causes a new scan
    * of all partitions when calling menuPrepare() below.
    */
   clearDevs();
  
   showMenu();
}



void  MounterTray::contextMenuAboutToShow(KPopupMenu  *menu) {
   menu->clear();
   itemDict.clear();
   
   QMap<QString, MntDevice *>  devs;
   QStringList  lst = MntDevice::getAllDeviceNames();
   
   QValueListConstIterator<QString>  it = lst.begin();
   while(it != lst.end()) {
      MntDevice  *mdev = devList[*it];
      
      if(mdev == NULL)
			/* MntDevice doesn't already exist, create */
         mdev = new MntDevice(*it);
      else
         devList.erase(*it);

		/* Don't use ATAPICAM cd* devices; they appear
		 * as acd* devices in this list.
		 * 
		 * ATAPICAM is avoided because it seems to not work
		 * properly when mounting DVD-ROMs.
		 */
		if(mdev->isATAPICAMDevice()) {
			++it;
			continue;
		}
      
      devs[*it] = mdev;
      mntInsert(mdev,  0,  menu);
      
      /* Insert a separator after every disk/cdrom/etc. */
      menu->insertSeparator();
      
      ++it;
   }
   
   /* Devices that are no more attached to the
    * system (e.g. removed USB sticks) are deleted
    * from the map now.
    */
   clearDevs();
   
   devList = devs;
   
   menu->insertItem(getIcon("reload", KIcon::SizeSmall), tr("Refresh All"),
                    this,  SLOT(refreshAll()));
   
   menu->insertItem(getIcon("folder", KIcon::SizeSmall), tr("Mounted Devices"),
                    this,  SLOT(browseDevs()));
}



void  MounterTray::mntInsert(MntDevice  *mdev,  uint  indent,  KPopupMenu  *menu) {
   int      itemId;
   QPixmap  pixmap;
   bool     mounted = mdev->isMounted();
   
   if(indent == 0) {
      switch(mdev->getDeviceType()) {
      case  Device::ATAPI_CD:
      case  Device::SCSI_CD:
         pixmap = getIcon(mounted ? "cdrom_mount" : "cdrom_unmount",
                          KIcon::SizeMedium);
         break;
         
      case  Device::Floppy:
         pixmap = getIcon(mounted ? "3floppy_mount" : "3floppy_unmount",
                          KIcon::SizeMedium);
         break;
         
      default:
         pixmap = getIcon("hdd_unmount", KIcon::SizeMedium);
         break;
      }
   } else {
      if(mdev->subdevCount() > 0)
         pixmap = getStoredPixmap("slice");
      else {
         if(mounted)
            pixmap = getStoredPixmap("mounted");
         else
            pixmap = getStoredPixmap("unmounted");
      }
   }
   
   Size     size = mdev->getSize();
   QString  sizeStr;
   if(size.get() > 0) {
   	sizeStr += " - ";
	if(size.get(Size::GB) > 0)
		sizeStr += size.toString(Size::GB);
	else
		sizeStr += size.toString(Size::MB);
   }
   
   itemId = menu->insertItem(pixmap,  QString().fill(' ', indent * 3) + mdev->getDesc() + sizeStr);
   itemDict.insert(itemId, mdev);
   
   indent++;
   for(uint i = 0;  i < mdev->subdevCount();  ++i)
      mntInsert((*mdev)[i],  indent,  menu);
}



void  MounterTray::mountUnmount(int  menuId) {
   MntDevice  *mdev = itemDict[menuId];
   
   if(mdev != NULL  &&  mdev->subdevCount() == 0) {
      bool  success;
      if(!mdev->isMounted()) {
			int flags = 0;
			
         /* Mount */
			if(mdev->getFSType() == MntDevice::FAT && mdev->getSize() >= Size(127, Size::GB))
			{
				// Enable experimental support for large FAT file systems.
				// See: src/sys/fs/msdosfs/msdosfs_vfsops.c
				flags |= MntDevice::Large;
			}
         
         success = mdev->mount(flags);
         if(!success)
            /** @todo Insert better error indicating in MntDevice */
            KMessageBox::error(NULL,   tr("<html>"
														"<p>Mounting the file system failed.</p>"
														
														"<p>Possible reasons:<br>"
														"- No media inserted (CD, DVD, Floppy Disk)<br>"
														"- The file system's type is not supported.<br>"
														"- The file system is corrupted.<br>"
														"- Partition table contains wrong data."
														"</p>"
										
														"</html>"));
			else if(flags & MntDevice::Large)
				KMessageBox::information(NULL,
												 tr("<p>This partition was mounted with experimental support "
													 "for large FAT file systems (more than 128 GB).</p>"
													 "<p>On file systems with many files, this can cause "
													 "significantly increased system memory usage.</p>"));
         else if(mdev->getFSType() == MntDevice::NTFS)
            KMessageBox::information(NULL,
                                     tr("This partition's file system is NTFS.\n\n"
                                        "Writing NTFS is not supported, so it was mounted read-only."));
			else
				KPassivePopup::message(tr("Mounted successful onto: <i>%1</i>.").arg(mdev->mountedOn()),
											  QString::null, *pixmap(), this);
      } else {
         /* Unmount */
         QString mpoint = mdev->mountedOn();
	 
         success = mdev->unmount(false);
         
         if(success)
            KPassivePopup::message(tr("<i>%1</i> was unmounted successfully.").arg(mpoint),
            							  QString::null,
            							  *pixmap(),
            							  this);
         else {
            int  forcibly = 
                  KMessageBox::warningYesNo(NULL,	tr("<html>"
																		"<p>Unable to unmount the file system.</p>"
																		
																		"<p>Possible reasons:<br>"
																		"- There are still files in use from this file system.<br>"
																		"- The device was removed.<br>"
                                                      "- The device was mounted by another user.<br>"
																		"</p>"
															
																		"<p>You can unmount the file system forcibly, but<br>"
																		"DO NOT do that if the device was already removed.</p>"

																		"<p>Do you want to unmount forcibly?<br>"
																		"<b>ATTENTION:  You will LOSE DATA!</b></p>"
																		"</html>"));
            
            if(forcibly == KMessageBox::Yes) {
               if(mdev->unmount(true))
                  KPassivePopup::message(tr("Forced unmount successful."), QString::null, *pixmap(), this);
               else
                  KMessageBox::error(NULL,  tr("<html>"
                                               "<p>Forced unmount failed.</p>"

                                               "<p>Possible reasons:<br>"
                                               "- You tried to unmount a system partition (e.g. '/', '/dev')<br>"
															  "- You don't have sufficient permissions"
                                               "</p>"

															  "</html>"));
            }
         }
      }
   }
}



QPixmap  MounterTray::getIcon(const QString&  name,  int  size) {
   return  KGlobal::iconLoader()->loadIcon(name,  KIcon::Desktop,  size);
}


void MounterTray::showEvent (QShowEvent *) {
	MounterSettings settings;

	if (settings.isIntroductionEnabled()) {
		QTimer::singleShot( 5 * 1000, this, SLOT(showIntroductionPopup()) );
	}
}


void MounterTray::showIntroductionPopup() {
	KApplication *a = KApplication::kApplication();
	KPassivePopup *popup = KPassivePopup::message(a->caption(),
										tr("<i>Click this icon to access data on<br>"
				   						"other partitions and external drives.</i>"),
										getIcon(), this, 0, 0);

	connect(popup, SIGNAL(clicked()), this, SLOT(popupClicked()));
}

void MounterTray::popupClicked() {
	MounterSettings settings;
	settings.setIntroductionEnabled(false);
	showMenu();
}

