/*
 * 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 <dialogs.h>
#include <networks.h>

#include <frmlan.h>
#include <frmwlan.h>
#include <frmselif.h>

#include <qtimer.h>

#define	REFRESH_INTERVAL	2000

#include <pppconnectiondata.h>
#include <pppconnectionmanager.h>
#include <executor.h>
#include <pppsettings.h>

void  modNetworks::wiConnect() {
	QListViewItem  *item = lstAvailableWireless->selectedItem();
	if(item != NULL  &&  selWiIf != NULL) {
		bool  created;
		WLAN  *wlan = getWLAN(item,  created);

		if(created) {
			if(!confWLAN(wlan->getSSID(),  wlan)) {
				/* Don't connect if the user pressed
				 * cancel at the configuration dialog
				 */
				delete  wlan;
				return;
			}

			Networks::addWLAN(wlan->getBSSID(),  wlan);
			setConfiguredFlag(item);
			/* changed() */
		}

		if(selWiIf->connectTo(*wlan)) {
			if(wlan->usesDHCP())
				Dialogs::infoDlg(tr("The network device is being configured using DHCP.\n"
										  "It could take a few seconds until you're connected."));
		} else
			Dialogs::warnDlg(tr("Connection failed."));
	}
}

void  modNetworks::wiDeviceSelect(int  idx) {
	selWiIf = wiIfs.at(idx);
	resetAvailWireless();
}

void  modNetworks::resetAvailWireless() {
	displayWirelessInfoText(tr("Click 'Refresh' to scan for wireless networks."));
}

void  modNetworks::setConfiguredFlag(QListViewItem  *item) {
	if(item != NULL) {
		item->setPixmap(1, DBSDGlobal::getIconLoader()->loadIcon("configure",
																					DBSDIconLoader::Size22x22));
	}
}

void  modNetworks::readWiInterfaces() {
	wiIfs.clear();
	cmbWirelessDevice->clear();

	wiIfs = WirelessInterface::getWirelessIfs();

	WirelessInterface  *wi = wiIfs.first();
	selWiIf = wi;
	while(wi != NULL) {
		cmbWirelessDevice->insertItem(wi->getDesc());

		wi = wiIfs.next();
	}

	if(cmbWirelessDevice->count() < 2) {
		cmbWirelessDevice->setEnabled(false);

		if(cmbWirelessDevice->count() == 0) {
			cmbWirelessDevice->insertItem(tr("No wireless network devices installed."));
			displayWirelessInfoText(tr("No wireless network devices installed."));
			btnRefreshWireless->setEnabled(false);
		} else
			resetAvailWireless();
	}
}

void  modNetworks::readWireless() {
	if(selWiIf != NULL) {
		lstAvailableWireless->clear();

		QPtrList<AccessPoint>  aps = selWiIf->getAPs();
		AccessPoint  *ap = aps.first();

		if(ap == NULL)
			displayWirelessInfoText(tr("No wireless networks found."));
		else do {
			lstAvailableWireless->setSelectionMode(QListView::Single);

			QListViewItem  *item = new  QListViewItem(lstAvailableWireless);
			accesspoints.insert(item, ap);

			if(ap->getCapInfo() & AccessPoint::WEP)
				item->setPixmap(0, DBSDGlobal::getIconLoader()->loadIcon("encrypted",
																							DBSDIconLoader::Size22x22));

			if(Networks::getWLAN(ap->getBSSID()) != NULL)
				setConfiguredFlag(item);

			int signal = ap->getSignal();
			item->setText(2, ' ' + QString::number(signal) + " dBm");

			if(signal < 80)
				item->setPixmap(2, DBSDGlobal::getIconLoader()->loadIcon("ledorange",
																							DBSDIconLoader::Size16x16));
			else
				item->setPixmap(2, DBSDGlobal::getIconLoader()->loadIcon("ledgreen",
																							DBSDIconLoader::Size16x16));


			/* Use the stored SSID if already configured */
			WLAN  *wlan = Networks::getWLAN(ap->getBSSID());
			item->setText(3, (wlan == NULL) ? ap->getSSID() : wlan->getSSID());

			/* Hidden */
			item->setText(4, ap->getBSSID());

			ap = aps.next();
		} while(ap != NULL);

		lstAvailableWireless->setSelected(lstAvailableWireless->firstChild(), true);
	}
}

void  modNetworks::readNetworks() {
	lstNetworks->clear();

	QMap<QString, LAN *>  lanList = Networks::allLANs();
	QMapIterator<QString, LAN *>  it;
	for(it = lanList.begin();  it != lanList.end();  ++it) {
		QListViewItem  *item = new QListViewItem(lstNetworks, it.key());
		item->setPixmap(0,  DBSDGlobal::getIconLoader()->loadIcon("network", DBSDIconLoader::Size22x22));
	}

	lstNetworks->setSelected(lstNetworks->firstChild(), true);
}

bool  modNetworks::editLAN(QString  name,  LAN  *lan) {
	frmLAN  flan(this, NULL, true);
	flan.setLAN(lan);
	flan.setNetName(name);

	int  result = flan.exec();
	/*
	if(result == QDialog::Accepted)
		changed();
	*/

	return  (result == QDialog::Accepted);
}

void  modNetworks::editLANItem() {
	QListViewItem  *item = lstNetworks->selectedItem();

	if(item != NULL) {
		editLAN(item->text(0), Networks::getLAN(item->text(0)));
	} else
		Dialogs::infoDlg(tr("No network selected."));
}

void  modNetworks::lanConnect() {
	QListViewItem  *item = lstNetworks->selectedItem();

	if(item != NULL) {
		NetInterface  *iface = NULL;
		QPtrList<NetInterface>  ifList = NetInterface::getIfs(false);

		if(ifList.count() > 1) {
			frmSelIf  selif(this, NULL, true);
			selif.setIfList(ifList);
			if(selif.exec() == QDialog::Accepted)
				iface = selif.getSelIf();
		} else {
			iface = ifList.at(0);
			if(iface == NULL)
				Dialogs::warnDlg(tr("No network devices available."));
		}

		if(iface != NULL) {
			LAN  *lan = Networks::getLAN(item->text(0));

			if(iface->connectTo(*lan)) {
				if(lan->usesDHCP())
					Dialogs::infoDlg(tr("The network device is being configured using DHCP.\n"
											  "It could take a few seconds until you're connected."));
			} else
				Dialogs::warnDlg(tr("Unable to connect to network."));
		}
	} else
		Dialogs::infoDlg(tr("No network selected."));
}

void  modNetworks::removeLANItem() {
	QListViewItem  *item = lstNetworks->selectedItem();

	if(item != NULL) {
		QString  network = item->text(0);
		if(Dialogs::question(tr("Remove '%1'?").arg(network))) {
			Networks::removeLAN(network);
			/* changed(); */
		}
	}

	readNetworks();
}

void  modNetworks::addLAN() {
	bool		valid;
	LAN		*lan = new LAN(* Networks::getLANTemplate());
	QString	name;

	do {
		bool  inputOk;
		name = Dialogs::strInput(tr("Network Name:"),  &inputOk);
		if(!inputOk) {
			delete  lan;
			return;
		}

		valid = (Networks::getLAN(name) == NULL);
		if(!valid)
			Dialogs::warnDlg(tr("A network with this name already exists.\n"
									  "Use a different name."));
	} while(!valid);

	if(editLAN(name,  lan)) {
		Networks::addLAN(name,  lan);
		readNetworks();
	}
}

bool  modNetworks::confWLAN(QString  name,  WLAN  *wlan) {
	frmWLAN  fwlan(this, NULL, true);
	fwlan.setWLAN(wlan);
	fwlan.setTitle(name);

	bool	accepted = (fwlan.exec() == QDialog::Accepted);
	/*
	if(accepted)
		changed();
	*/

	return  accepted;
}

WLAN  *modNetworks::getWLAN(QListViewItem  *item,  bool &  created) {
	WLAN  *wlan = Networks::getWLAN(item->text(4));

	created = (wlan == NULL);
	if(created) {
		AccessPoint  *ap = accesspoints[item];

		if(ap != NULL) {
			wlan = new WLAN(*Networks::getWLANTemplate());
			wlan->setAP(*ap);
		}
	}

	return  wlan;
}

void  modNetworks::confWirelessItem() {
	QListViewItem  *item = lstAvailableWireless->selectedItem();

	if(item != NULL) {
		/* If no settings for this wireless network
		 * exist, they are created, but only saved
		 * if the user pressed "Ok".
		 */

		bool  created;
		WLAN  *wlan = getWLAN(item, created);

		if(wlan != NULL)
			if(confWLAN(item->text(3), wlan))
			{
				/* Changed */
				item->setText(3, wlan->getSSID());
				setConfiguredFlag(item);

				if(created)
					Networks::addWLAN(wlan->getBSSID(),  wlan);
			}
	} else
		Dialogs::infoDlg(tr("No wireless network selected."));
}

void  modNetworks::init() {
	/* Initialize Graphics */

	DBSDIconLoader  *iloader = DBSDGlobal::getIconLoader();

	btnAdd->setIconSet(iloader->loadIcon("filenew", DBSDIconLoader::Button));
	btnEdit->setIconSet(iloader->loadIcon("edit", DBSDIconLoader::Button));
	btnRemove->setIconSet(iloader->loadIcon("editdelete", DBSDIconLoader::Button));
	btnConnect->setIconSet(iloader->loadIcon("connect_established", DBSDIconLoader::Button));
	btnRefreshStatus->setIconSet(iloader->loadIcon("reload", DBSDIconLoader::Button));
	btnRefreshWireless->setIconSet(iloader->loadIcon("reload", DBSDIconLoader::Button));
	btnEditWireless->setIconSet(iloader->loadIcon("configure", DBSDIconLoader::Button));
	btnWirelessConnect->setIconSet(iloader->loadIcon("connect_established", DBSDIconLoader::Button));
	btnPPPInfo->setPixmap(iloader->loadIcon("info", DBSDIconLoader::Size16x16));
	btnEditManually->setIconSet(iloader->loadIcon("configure", DBSDIconLoader::Button));
	btnConnectDSL->setIconSet(iloader->loadIcon("connect_established", DBSDIconLoader::Button));
	btnDisconnectDSL->setIconSet(iloader->loadIcon("connect_creating", DBSDIconLoader::Button));
	btnDeactivateInterface->setIconSet(iloader->loadIcon("fileclose", DBSDIconLoader::Button));
	pixLocked->setPixmap(DBSDGlobal::getIconLoader()->loadIcon("encrypted",
																				  DBSDIconLoader::Size16x16));
	pixConfigured->setPixmap(DBSDGlobal::getIconLoader()->loadIcon("configure",
																						DBSDIconLoader::Size16x16));
	pixPPP->setPixmap(DBSDGlobal::getIconLoader()->loadIcon("dbsd-pppconf",
																			  DBSDIconLoader::Size128x128));
}

void  modNetworks::initModule() {
	accesspoints.setAutoDelete(true);

	QTimer  *timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), SLOT(readIfStatus()));
	timer->start(REFRESH_INTERVAL, false);

	Networks::load();
	readNetworks();
	readWiInterfaces();

	lstIfStatus->setColumnWidthMode(0, QListView::Manual);
	lstIfStatus->setColumnWidth(0, 234);
	readIfStatus();

	// Load stored settings
	PPPSettings settings;
	chkCustomDNS->setChecked(settings.getCustomDNSEnabled());
	cmbConType->setCurrentItem(settings.getConnectionType());
	connectionTypeChanged(settings.getConnectionType());
	radGroupConfiguration->setButton(settings.getConfiguration());
	editPrimDNS->setText(settings.getPrimaryDNS());
	editSecDNS->setText(settings.getSecondaryDNS());
	editAccount->setText(settings.getAccountID());
	editPassword->setText(settings.getPassword());
	editModemIP->setText(settings.getModemIP());

	// Get all network devices
	cmbNetworkDevice->clear();
	pppNetworkdevices = NetInterface::getIfs(true,  false);
	pppNetworkdevices.setAutoDelete(true);

	int pos = 0;
	NetInterface *interface;
	QString ifname = settings.getNetworkDevice();

	// Insert found network devices into combobox
	for(interface = pppNetworkdevices.first(); interface; interface = pppNetworkdevices.next())
	{
			cmbNetworkDevice->insertItem(interface->getDesc());

			if(interface->getName() == settings.getNetworkDevice())
				pos = cmbNetworkDevice->count() - 1;
	}
	
	// Select previously selected devices
	cmbNetworkDevice->setCurrentItem(pos);
}

void  modNetworks::destroy() {
	if(wasInitialized())
	{
		Networks::save();

		PPPSettings settings;
		settings.setConfiguration(radGroupConfiguration->selectedId());
		settings.setConnectionType(cmbConType->currentItem());
		settings.setCustomDNSEnabled(chkCustomDNS->isChecked());
		settings.setPrimaryDNS(editPrimDNS->text());
		settings.setSecondaryDNS(editSecDNS->text());
		settings.setAccountID(editAccount->text());
		settings.setPassword(editPassword->text());
		settings.setModemIP(editModemIP->text());
	
		NetInterface *interface = pppNetworkdevices.at(cmbNetworkDevice->currentItem());
		settings.setNetworkDevice(interface->getName());
	}
}


int	modNetworks::getSupportedOperations() {
	return	Apply;
}


/** Reads the interface status of all network devices */
void	modNetworks::readIfStatus() {
	DBSDIconLoader  *iloader = DBSDGlobal::getIconLoader();

	int selectedItem = (lstIfStatus->selectedItem() != NULL) ? 0 : -1;
	if(selectedItem != -1) {
		QListViewItemIterator it(lstIfStatus);
		while(*it != NULL && !(*it)->isSelected()) {
			++selectedItem;
			++it;
		}
	}
	
	lstIfStatus->clear();

	interfaces = NetInterface::getIfs(true,  false);
	interfaces.setAutoDelete(true);

	NetInterface  *iface = interfaces.first();
	for(int n = 0; iface != NULL; ++n) {
		QListViewItem  *item = new QListViewItem(lstIfStatus, iface->getDesc());
		item->setPixmap(0,  iloader->loadIcon("kcmpci",  DBSDIconLoader::LargeListItem));

		QString  statusstr = tr("(Error)");
		NetInterface::Status  status = iface->getStatus();
		if(status == NetInterface::NoCarrier)
			statusstr = tr("No Carrier");
		else if(status == NetInterface::Active  ||  status == NetInterface::Associated) {
			statusstr = (status == NetInterface::Active) ? tr("Active") : tr("Associated", "Wireless device associated to an access point");
			item->setText(2, iface->getIP().toString());
			item->setText(3, iface->getNetmask().toString());

			/* Display wireless settings if this is an 802.11 device */
			if(status == NetInterface::Associated) {
				WirelessInterface  *wiIf = (WirelessInterface *) iface;

				WEPMode  wmode  = wiIf->getWEPMode();
				QString  wepstr = tr("(Error)");;
				if(wmode == WEPOn)
					wepstr = tr("On");
				else if(wmode == WEPOff)
					wepstr = tr("Off");
				else if(wmode == WEPMixed)
					wepstr = tr("Mixed");

				item->setText(4, tr("SSID: '%1', WEP Mode: %2").arg(wiIf->getSSID())
																			  .arg(wepstr));
			}
		}

		item->setText(1,  statusstr);
		
		/* Use the (hidden) column 5 to store the index in the QPtrList */
		item->setText(5, QString::number(n));

		iface = interfaces.next();
	}

	if(lstIfStatus->childCount() == 0) {
		QListViewItem  *item = new QListViewItem(lstIfStatus, tr("No network interfaces found."));
		item->setPixmap(0,  iloader->loadIcon("messagebox_info",  DBSDIconLoader::LargeListItem));
	} else if(selectedItem >= 0) {
		QListViewItemIterator it(lstIfStatus);
		for(int n = 0; *it != NULL && n < selectedItem; ++it, ++n);
		
		if(*it != NULL)
			lstIfStatus->setSelected(*it, true);
	}
}


void modNetworks::displayWirelessInfoText(const QString & text) {
	lstAvailableWireless->clear();

	QListViewItem  *item = new QListViewItem(lstAvailableWireless);
	item->setPixmap(2,  DBSDGlobal::getIconLoader()->loadIcon("messagebox_info", DBSDIconLoader::LargeListItem));
	item->setText(3, text);

	lstAvailableWireless->setSelectionMode(QListView::NoSelection);
	wlanSelected(NULL);
}



void modNetworks::wlanSelected(QListViewItem *litem) {
	bool b = lstAvailableWireless->selectionMode() != QListView::NoSelection && litem != NULL;

	btnEditWireless->setEnabled(b);
	btnWirelessConnect->setEnabled(b);
}


void modNetworks::lanSelected(QListViewItem *litem) {
	btnEdit->setEnabled(litem != NULL);
	btnRemove->setEnabled(litem != NULL);
	btnConnect->setEnabled(litem != NULL);
}


void modNetworks::deactivateNIC() {
	QListViewItem *item = lstIfStatus->selectedItem();
	if(item != NULL) {
		bool conversionOk;
		uint index = item->text(5).toUInt(&conversionOk);
		NetInterface *ni = interfaces.at(index);
		
		if(conversionOk && ni != NULL)
			ni->remove();
		else
			Dialogs::warnDlg(tr("Error: Interface not found in the list."));
	} else
		Dialogs::infoDlg(tr("No network interface selected."));
}


void modNetworks::nicSelected(QListViewItem *item) {
	btnDeactivateInterface->setEnabled(item != NULL);
}

void modNetworks::pppConnect()
{
	if (radBtnFollowingConfiguration->isChecked())
	{
		QString primdns;
		QString secdns;
	
		if(chkCustomDNS->isChecked())
		{
			primdns = editPrimDNS->text();
			secdns  = editSecDNS->text();
		}
		else
			primdns = "default";
	
		PPPConnectionData::ConnectionType contype;
	
		if(cmbConType->currentText() == "PPTP")
			contype = PPPConnectionData::PPTP;
		else
			contype = PPPConnectionData::PPPoE;
				
		// Get selected network device
		NetInterface *interface = pppNetworkdevices.at(cmbNetworkDevice->currentItem());
		if(interface == NULL)
		{
			Dialogs::warnDlg(tr("Internal error: Network device not found."));
			return;
		}
	
		PPPConnectionData condata(editAccount->text(), editPassword->text(),
									editModemIP->text(), primdns, secdns, 
									contype, interface->getName());
	
		int condataStatus = condata.checkData();
	
		if(condataStatus == 0 || (condataStatus == -4 && primdns == "default") ||
										(condataStatus == -5 && secdns.isEmpty()))
		{
			conmanager.setConnectionData(condata);
	
			// Create/prepare mpd.conf and mpd.links
			if(conmanager.createConfigurationFiles() != 0)
			{
				Dialogs::warnDlg(tr("Couldn't create configuration files."));
				return;
			}
			
			if(primdns != "default")
				conmanager.setNameserver();
		}
		else
		{
			QString errMessage;
	
			switch(condataStatus)
			{
				case -1: errMessage = tr("No valid account ID.");
							break;
				case -2: errMessage = tr("No valid password given.");
							break;
				case -3: errMessage = tr("Invalid modem IP address.");
							break;
				case -4: errMessage = tr("Invalid primary DNS specified.");
							break;
				case -5: errMessage = tr("Invalid secondary DNS given.");
							break;
			}
			
			Dialogs::warnDlg(errMessage);
	
			return;
		}
	}

	if(conmanager.openConnection() != 0)
		Dialogs::warnDlg(tr("Can't start MPD (is net/mpd installed?)."));
}


void modNetworks::pppDisconnect()
{
	conmanager.closeConnection();
}


void modNetworks::editManually()
{
	Executor::exec("/usr/local/bin/kate -s 'MPD Configuration' /usr/local/etc/mpd/mpd.conf /usr/local/etc/mpd/mpd.links");
}

void modNetworks::connectionTypeChanged(int pos)
{
	editModemIP->setEnabled(pos == 0);
	cmbNetworkDevice->setEnabled(pos != 0);
}


void modNetworks::showPPPInfo()
{
	QString infomessage = tr("PPTP and PPPoE are network protocols for establishing\n"
									 "connections to your provider.\n"
									 "\n"
									 "PPPoE is the most common protocol and used by providers\n"
									 "in Germany and Switzerland, while PPTP is mostly used in Austria,\n"
									 "Italy, Belgium and the Netherlands.\n"
									 "\n"
									 "Contact your provider if you are not sure which protocol to use.");

	Dialogs::infoDlg(infomessage);
}
