/*
 * 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 <dbsdglobal.h>

#include <chunk.h>

#include <moddisksel.h>
#include <frmmodulecontainer.h>
#include <dialogs.h>
#include <variables.h>

#include <qinputdialog.h>
#include <qptrlist.h>

/* Define SHOW_ZERO_SIZE to show slices with
 * (converted!) size = 0
 *
 * #define SHOW_ZERO_SIZE
 */



int  modDiskPart::selItem() {
	QListViewItem *litem = lstPartitions->selectedItem();
	if(litem == NULL)
		return -1;

	return litem->text(3).toInt();
}


/**
 * Reads all chunks on the disk.
 */
void modDiskPart::readChunks() {
	if(dsk == NULL) {
		QString  dskName = Variables::getValue("disk.activeDisk");

		if(dskName.isEmpty()) {
			frmModuleContainer *c = new frmModuleContainer(this);
			c->setCaption(tr("DesktopBSD"));
			c->setDBSDModule(new modDiskSel(c));
			c->exec();
			dskName = Variables::getValue("disk.activeDisk");

			delete c;
		}

		if(dskName.isEmpty()) {
			Dialogs::warnDlg(tr("No disk selected for partitioning."));
			this->setEnabled(false);
			return;
		}

		dsk = new Disk(dskName);
		if(dsk->isMounted()) {
			Dialogs::warnDlg(tr("One or more partitions from this disk are mounted, therefore\n"
										 "it's partition layout can't be altered.\n"
									  "\n"
										 "Unmount the particular partitions first or use a bootable\n"
									  "CD-ROM operating system to partition this disk if this is\n"
									  "a system partition that cannot be unmounted (e.g. / or /dev)."));

			btnNewSlice->setEnabled(false);
			btnDelSlice->setEnabled(false);
			btnNewFS->setEnabled(false);
		} else {
			/* Only display hints if not mounted */
			
			if(!dsk->saneBIOSGeom()) {
				dsk->sanitizeBIOSGeom();

				if(!Dialogs::question(tr("A logical geometry for this drive was calculated:\n"
												 "%1 Cylinders / %2 Heads / %3 Sectors\n"
												 "\n"
												 "This geometry will ONLY work if this disk uses logical\n"
												 "block addressing (LBA). Please check the settings in\n"
												 "the BIOS if you are not sure.\n"
												 "\n"
												 "Do you want to use this geometry? Click 'No' to specify\n"
												 "a geometry yourself.")
													 .arg(dsk->getBIOSCyl())
													 .arg(dsk->getBIOSHeads())
													 .arg(dsk->getBIOSSectors())))
				{
					QString newGeom = QString("%1/%2/%3").arg(dsk->getBIOSCyl())
																	 .arg(dsk->getBIOSHeads())
																	 .arg(dsk->getBIOSSectors());
					bool	done = true,
							clickedOk;
					do {
						newGeom = Dialogs::strInput(tr("Please enter a new geometry.\n"
																 "\n"
																 "- For IDE drives, look up the geometry in your BIOS setup.\n"
																 "- For SCSI drives, enter the translation mode of your controller.\n"
																 "\n"
																 "Do NOT use the \"physical geometry\" of your disk.\n"
																 "\n"
																 "Input format: Cylinders/Heads/Sectors\n"),
															 &clickedOk,
															 newGeom);

						if(clickedOk) {
							done = false;

							QStringList stringValues = QStringList::split("/", newGeom);
							if(stringValues.count() == 3) {
								unsigned long chs[3];
	
								done = false;
								
								bool conversionOk;
								for(int i = 0; conversionOk && (i < 3); ++i)
									chs[i] = stringValues[i].toULong(&conversionOk);
		
								if(conversionOk) {
									dsk->setBIOSGeometry(chs[0], chs[1], chs[2]);
									done = true;
								}
							}
	
							if(!done)
								Dialogs::warnDlg(tr("The geometry you entered is invalid."));
								
						} else
							/* User clicked 'Cancel' */
							done = Dialogs::question(tr("Use the automatically calculated geometry?"));
							
					} while(!done);
				}
			}
		}
	}

	/* Save selected Item's position and reselect it
	  * after the new list has been built.
	  */
	int oldSel = selItem();

	/* Clear ListView */
	lstPartitions->clear();
	lstPartitions->setSortColumn(3);

	/* Read all chunks and insert them into the QListView */
	int chunks = dsk->chunkCount();
	for(int i = 0; i < chunks; ++i) {
		Disk::Chunk cnk = (*dsk)[i];

#ifndef SHOW_ZERO_SIZE
		if(cnk.getSize().get(Size::MB) == 0)
			continue;
#endif

		DBSDIconLoader  *iloader = DBSDGlobal::getIconLoader();
		Disk::Chunk::FSType type = cnk.getFSType();
		QPixmap *pixm = NULL;

		if(type == Disk::Chunk::Unused)
			/* unused */
			pixm = new QPixmap(iloader->loadIcon("empty", DBSDIconLoader::LargeListItem));
		else if(type == Disk::Chunk::UFS)
			pixm = new QPixmap(iloader->loadIcon("desktopbsd", DBSDIconLoader::LargeListItem));
		else
			pixm = new QPixmap(iloader->loadIcon("binary", DBSDIconLoader::LargeListItem));

		Size::Unit unit = (cnk.getSize().get(Size::GB) < 10) ? Size::MB : Size::GB;
		QListViewItem* newIt = new QListViewItem(lstPartitions,
															  "",
															  cnk.getSize().toString(unit),
															  cnk.getDesc(),
															  QString::number(i));
		newIt->setPixmap(0, *pixm);
		if(i == oldSel)
			lstPartitions->setCurrentItem(newIt);

		delete pixm;
	}
}


void modDiskPart::initModule() {
	readChunks();

	/* Make every column 50 % larger */
	for(int i = 0; i < lstPartitions->columns(); ++i)
		lstPartitions->setColumnWidth(i, int(lstPartitions->columnWidth(i) * 1.5));
}


void modDiskPart::createSlice() {
	int	i = selItem();
	if(i < 0) {
		Dialogs::infoDlg(tr("Select a slice in which you want to\n"
								  "create a partition first."));
		return;
	}

	Disk::Chunk cnk = (*dsk)[i];
	if(cnk.inUse()) {
		Dialogs::warnDlg(tr("The selected slice already contains a partition.\n"
								  "\n"
								  "Select an empty slice or delete one to create\n"
								  "a new partition in it."));
	} else {
		if(dsk->usedChunkCount() == 4) {
			Dialogs::warnDlg(tr("You cannot have more than 4 partitions per disk."));
			return;
		}

		bool  ok;
		Size  size;
		if(Dialogs::question(tr("Use all the space available in this slice?"))) {
			size = cnk.getSize();
		} else {
			int iSize = Dialogs::intInput(tr("Size of new slice (in MB, 1 GB = 1024 MB):"),
													&ok,
													cnk.getSize().get(Size::MB),
													0,
													cnk.getSize().get(Size::MB));
			if(!ok)
				return;

			size.set(iSize, Size::MB);
		}

		QStringList  lst;
		lst << tr("DesktopBSD (UFS2)") << tr("Windows, MS-DOS (FAT)");

		QString sel = QInputDialog::getItem(tr("DesktopBSD"), tr("Which file system do you want to use?"),
														lst, 0, false, &ok, this);

		if(!ok)
			return;

		FSDevice::FSType type = (sel == lst.first()) ? FSDevice::UFS : FSDevice::FAT;
		ok = cnk.create(type, size);

		if(ok) {
			readChunks();
			emit changed();
		} else
			Dialogs::warnDlg(tr("Error: Cannot create this partition."));
	}
}


void modDiskPart::delSlice(){
	int i = selItem();
	if(i < 0) {
		Dialogs::infoDlg(tr("Select the partition you want to delete first."));
		return;
	}

	Disk::Chunk cnk = (*dsk)[i];
	if(!cnk.inUse()) {
		Dialogs::warnDlg(tr("This slice doesn't contain a partition\n"
								  "that could be deleted."));
		return;
	}

	cnk.free();
	readChunks();
	emit changed();
}


void modDiskPart::reset()
{
	if(Dialogs::question(tr("Undo all changes you made?"))) {
		dsk->undoChanges();
		readChunks();
		emit changed(false);
	}
}


void modDiskPart::apply() {
	if(!Dialogs::question(tr("This can NOT be UNDONE.\n" \
									 "\n" \
									 "Are you sure you want to proceed?")))
		return;

	if(dsk->writeChanges()) {
		emit changed(false);
		Dialogs::infoDlg(tr("Changes applied successfully."));
	} else {
		Dialogs::warnDlg(tr("Unable to write to the disk.\n"
								  "\n"
								  "The disk is probably mounted or was removed\n"
								  "from the computer when partitioning."));
	}
}




void modDiskPart::destroy() {
	if(wasInitialized())
		delete  dsk;
}


void modDiskPart::init() {
	/* Initialize Graphics */
	dsk = NULL;

	DBSDIconLoader  *iloader = DBSDGlobal::getIconLoader();
	btnNewSlice->setIconSet(iloader->loadIcon("filenew", DBSDIconLoader::Button));
	btnDelSlice->setIconSet(iloader->loadIcon("editdelete", DBSDIconLoader::Button));
	btnNewFS->setIconSet(iloader->loadIcon("filesaveas", DBSDIconLoader::Button));
	pixHint->setPixmap(iloader->loadIcon("info", DBSDIconLoader::HintIcon));
}


int modDiskPart::getSupportedOperations() {
	return  (Apply | Reset);
}




bool modDiskPart::newFS() {
	int i = selItem();
	if(i < 0) {
		Dialogs::infoDlg(tr("No partition selected."));
		return false;
	}

	Disk::Chunk cnk = (*dsk)[i];
	if(!cnk.inUse()) {
		Dialogs::warnDlg(tr("You must create a partition in this slice first.\n"
								  "Use 'New Slice' for that.\n"));
		return false;
	}

	int fs = cnk.getFSType();
	if(fs != FSDevice::UFS && fs != FSDevice::FAT) {
		Dialogs::warnDlg(tr("Formatting this type of file system is not supported.\n"
								  "\n"
								  "This software can only create new FAT and UFS file systems."));
		return false;
	}

	if(Dialogs::question(tr("To create the new partition in this slice, all\n"
									"changes must be saved. This can NOT be UNDONE.\n"
									"Additionally, all data in this partition will be\n"
									"LOST after formatting it.\n"
									"\n"
									"Do you want to proceed?")))
	{
		if(cnk.newFS())
			Dialogs::infoDlg(tr("The new file system was created."));
		else
			Dialogs::warnDlg(tr("Creating the new file-system failed.\n"
									  "\n"
									  "The disk is probably mounted or was removed\n"
									  "from the computer when partitioning."));

		readChunks();
		emit changed(false);
	}

	return false;
}
