/* conduitConfigDialog.cpp                KPilot
**
** Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
**
** This file defines a .ui-based configuration dialog for conduits.
*/

/*
** This program 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 2 of the License, or
** (at your option) any later version.
**
** This program 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 this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
** MA 02110-1301, USA.
*/

/*
** Bug reports and questions can be sent to kde-pim@kde.org
*/

#include "options.h"

#include <tqlayout.h>
#include <tqgroupbox.h>
#include <tqlabel.h>
#include <tqvbox.h>
#include <tqtimer.h>
#include <tqptrlist.h>
#include <tqmap.h>
#include <tqvaluelist.h>

#include <tdemessagebox.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <tdeconfigskeleton.h>
#include <tdeapplication.h>
#include <kprogress.h>

#include "kpilotConfig.h"
#include "pilotUser.h"
#include "pilotSysInfo.h"
#include "options.h"
#include "kpilotdevicelink.h"

#include "kpilotProbeDialog.moc"
#include "pilotDaemonDCOP_stub.h"

/*
We can't connect to /dev/ttyUSB0 and /dev/ttyUSB1 at the same time, because that
will lock up kpilot completely. In particular, it gets a connection on /dev/ttyUSB0,
which it processes, and while processing, a connection on USB1 is also detected.
However, when kpilot gets 'round to process it, the link is already closed, and
pi_connect hangs forever.

Now, I split up the list of devices to probe into three list, one holding /dev/pilot,
the second holding all /dev/xxx0 and /dev/xxx2 (e.g. /dev/ttyUSB0 and /dev/ttyUSB2),
and finally a third holding the remaining /dev/xxx1 and /dev/xxx3 devices. Each of
these three sets of devices is activated for a few seconds, and then the next set is
probed. This way, I ensure that kpilot never listens on /dev/ttyUSB0 and /dev/ttyUSB1
at the same time.

Now the first detection works fine. However, it seems the Linux kernel has another
problem with /dev/ttyUSB0. I have a Clie, which uses ttyUSB0, and as soon as the
wizard tries to listen on ttyUSB1 (after it detected the handheld on ttyUSB0 already),
the kernel writes a warning message to the syslog:
visor ttyUSB1: Device lied about number of ports, please use a lower one.

If I continue autodetection once again afterwards, the visor module kind of crashes.
lsmod shows an impossible usage count for the module:

reinhold@einstein:/kde/builddir$ lsmod
Module                  Size  Used by
visor                  17164  4294967294
usbserial              30704  1 visor

After that, the kernel doesn't detect the device ever again (until the computer is rebooted),
and the module can't be unloaded.
*/


ProbeDialog::ProbeDialog(TQWidget *parent, const char *n) :
	KDialogBase(parent, n, true, i18n("Autodetecting Your Handheld"), KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::User1, KDialogBase::Cancel, true, i18n("Restart Detection")),
	mDetected(false), mUserName(), mDevice()
{
	FUNCTIONSETUP;

	TQVBox *mainWidget = makeVBoxMainWidget();

	fInfoText = new TQLabel( i18n( "KPilot is now trying to automatically detect the device of your handheld. Please press the hotsync button if you have not done so already." ), mainWidget, "fInfoText" );
	fInfoText->setAlignment( TQLabel::WordBreak );

	fStatusGroup = new TQGroupBox( i18n("Status"), mainWidget, "fStatusGroup" );
	fStatusGroup->setColumnLayout(0, TQt::Vertical );
	fStatusGroupLayout = new TQGridLayout( fStatusGroup->layout() );

	fStatus = new TQLabel( i18n("Autodetection not yet started..."), fStatusGroup, "fStatus" );
	fStatus->setAlignment( TQLabel::WordBreak );
	fStatusGroupLayout->addWidget( fStatus, 0, 0 );

	fProgress = new KProgress( 100, fStatusGroup, "fProgress" );
	fStatusGroupLayout->addWidget( fProgress, 1, 0 );



	fResultsGroup = new TQGroupBox( i18n( "Detected Values" ), mainWidget, "fResultsGroup" );
	fResultsGroup->setEnabled( false );
	fResultsGroup->setColumnLayout(0, TQt::Vertical );
	fResultsGroupLayout = new TQGridLayout( fResultsGroup->layout() );
	fResultsGroupLayout->setAlignment( TQt::AlignTop );

	fUserLabel = new TQLabel( i18n( "Handheld user:" ), fResultsGroup, "fUserLabel" );
	fUserLabel->setSizePolicy( TQSizePolicy( (TQSizePolicy::SizeType)4, (TQSizePolicy::SizeType)5, 0, 0, fUserLabel->sizePolicy().hasHeightForWidth() ) );
	fResultsGroupLayout->addWidget( fUserLabel, 0, 0 );

	fDeviceLabel = new TQLabel( i18n( "Device:" ), fResultsGroup, "fDeviceLabel" );
	fResultsGroupLayout->addWidget( fDeviceLabel, 1, 0 );

	fUser = new TQLabel( i18n("[Not yet known]"), fResultsGroup, "fUser" );
	fResultsGroupLayout->addWidget( fUser, 0, 1 );

	fDevice = new TQLabel( i18n("[Not yet known]"), fResultsGroup, "fDevice" );
	fResultsGroupLayout->addWidget( fDevice, 1, 1 );


	resize( TQSize(459, 298).expandedTo(minimumSizeHint()) );
	clearWState( WState_Polished );
	enableButtonOK(false);

	mDevicesToProbe[0] << "/dev/pilot";
	mDevicesToProbe[1] <<"/dev/ttyS0"<<"/dev/ttyS2"
	                <<"/dev/tts/0"<<"/dev/tts/2"
	                <<"/dev/ttyUSB0"<<"/dev/ttyUSB2"
	                <<"/dev/usb/tts/0"<<"/dev/usb/tts/2"
	                <<"/dev/cuaa0"<<"/dev/cuaa2"
			<<"/dev/cuad0"<<"/dev/cuad2"
	                <<"/dev/ucom0"<<"/dev/ucom2";
	mDevicesToProbe[2] <<"/dev/ttyS1"<<"/dev/ttyS3"
	                <<"/dev/tts/1"<<"/dev/tts/3"
	                <<"/dev/ttyUSB1"<<"/dev/ttyUSB3"
	                <<"/dev/usb/tts/1"<<"/dev/usb/tts/3"
	                <<"/dev/cuaa1"<<"/dev/cuaa3"
			<<"/dev/cuad1"<<"/dev/cuad3"
	                <<"/dev/ucom1"<<"/dev/ucom3";

	fProcessEventsTimer = new TQTimer( this );
	fTimeoutTimer = new TQTimer( this );
	fProgressTimer = new TQTimer( this );
	fRotateLinksTimer = new TQTimer( this );
	connect( fProcessEventsTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(processEvents()) );
	connect( fTimeoutTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(timeout()) );
	connect( fProgressTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT( progress()) );
	connect( fRotateLinksTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT( detect()) );
	connect( this, TQ_SIGNAL(finished()), this, TQ_SLOT(disconnectDevices()) );
}

ProbeDialog::~ProbeDialog()
{
	FUNCTIONSETUP;
}

void ProbeDialog::processEvents()
{
	FUNCTIONSETUP;
	tdeApp->processEvents();
}

void ProbeDialog::progress()
{
	fProgress->advance(1);
}

int ProbeDialog::exec()
{
	mDetected = false;
	mUserName = TQString();
	mDevice = TQString();
	TQTimer::singleShot( 0, this, TQ_SLOT( startDetection() ) );
	return KDialogBase::exec();
}

void ProbeDialog::startDetection()
{
	FUNCTIONSETUP;

	disconnectDevices();
	fProgress->setProgress(0);
	fStatus->setText( i18n("Starting detection...") );
	TQTimer::singleShot(0, this, TQ_SLOT(processEvents()) );
	processEvents();
	PilotDaemonDCOP_stub *daemonStub = new PilotDaemonDCOP_stub("kpilotDaemon", "KPilotDaemonIface");
	if (daemonStub) {
		daemonStub->stopListening();
	}
	KPILOT_DELETE(daemonStub);
	processEvents();
	if (!fTimeoutTimer->start( 30000, true ) )
	{
		WARNINGKPILOT << "Could not start fTimeoutTimer" << endl;
	}
	if (!fProcessEventsTimer->start( 100, false ) )
	{
		WARNINGKPILOT << "Could not start fProcessEventsTimer" << endl;
	}
	if (!fProgressTimer->start( 300, false) )
	{
		WARNINGKPILOT << "Could not start Progress timer" << endl;
	}

	KPilotDeviceLink*link;
	for (int i=0; i<3; i++)
	{
		TQStringList::iterator end(mDevicesToProbe[i].end());
		for (TQStringList::iterator it=mDevicesToProbe[i].begin(); it!=end; ++it)
		{
			link = new KPilotDeviceLink();
			link->setDevice((*it));
#ifdef DEBUG
			DEBUGKPILOT<<"new kpilotDeviceLink for "<<(*it)<<endl;
#endif
			mDeviceLinks[i].append( link );
			connect( link, TQ_SIGNAL(deviceReady(KPilotDeviceLink*)), this, TQ_SLOT(connection(KPilotDeviceLink*)) );
			processEvents();
		}
	}
	fStatus->setText( i18n("Waiting for handheld to connect...") );
	mProbeDevicesIndex=0;

	detect();
	if (!fRotateLinksTimer->start( 3000, false) )
	{
		WARNINGKPILOT << "Could not start Device link rotation timer" << endl;
	}
}


void ProbeDialog::detect(int i)
{
	FUNCTIONSETUP;

	mProbeDevicesIndex = i;
	PilotLinkList::iterator end(mDeviceLinks[mProbeDevicesIndex].end());

	for (PilotLinkList::iterator it=mDeviceLinks[mProbeDevicesIndex].begin(); it!=end; ++it)
	{
		if (*it) (*it)->reset();
	}
}

void ProbeDialog::detect()
{
	detect( (mProbeDevicesIndex+1)%3 );
}

void ProbeDialog::timeout()
{
	disconnectDevices();
	if (!mDetected) {
		fStatus->setText( i18n("Timeout reached, could not detect a handheld.") );
		KMessageBox::information ( this, i18n("<qt>A handheld could not be detected. Possible check the following things:</p>"
			"<ul><li> Have you pressed the hotsync button on the handheld?\n"
			"<li> Make sure the device sits in the cradle correctly.\n"
			"<li> Make sure the cradle is correctly plugged in to the computer.\n"
			"<li> Have you checked that your device is actually supported by kpilot (see http://www.kpilot.org).\n"
			"</ul>"
			), i18n("Automatic Detection Failed"), "AutoDetectionFailed");
	}
}

void ProbeDialog::connection( KPilotDeviceLink*lnk)
{
	FUNCTIONSETUP;

	mActiveLink = lnk;
	if ( !mActiveLink ) return;
	const KPilotUser &usr( mActiveLink->getPilotUser() );

	mUserName = usr.name();
	mDevice = mActiveLink->pilotPath();

	fStatus->setText( i18n("Found a connected device on %1").arg(mDevice) );
	fUser->setText( mUserName );
	fDevice->setText( mDevice );
	mDetected = true;

	fResultsGroup->setEnabled( true );
	enableButtonOK(true);

	TQTimer::singleShot(0, this, TQ_SLOT(retrieveDBList()));
}

void ProbeDialog::retrieveDBList()
{
	KPilotLink::DBInfoList dbs = mActiveLink->getDBList();
	mDBs.clear();
	char buff[7];
	buff[0] = '[';

	for ( KPilotLink::DBInfoList::ConstIterator i = dbs.begin();
		i != dbs.end(); ++i )
	{
		set_long( &buff[1], (*i).creator );
		buff[5] = ']';
		buff[6] = '\0';
		TQString cr( buff );
		mDBs << cr;
		mDBs << TQString( (*i).name );
	}
	mDBs.sort();

	TQString old( TQString::null );
	TQStringList::Iterator itr = mDBs.begin();
	while ( itr != mDBs.end() ) {
		if ( old == *itr ) {
			itr = mDBs.remove( itr );
		} else {
			old = *itr;
			++itr;
		}
	}

	// End sync gracefully, but don't change settings on the handheld.
	mActiveLink->endSync( KPilotLink::NoUpdate );

	TQTimer::singleShot(0, this, TQ_SLOT(disconnectDevices()));
}
void ProbeDialog::disconnectDevices()
{
	FUNCTIONSETUP;

	if (!mDetected) fStatus->setText( i18n("Disconnected from all devices") );
	fProcessEventsTimer->stop( );
	fTimeoutTimer->stop();
	fProgressTimer->stop();
	fRotateLinksTimer->stop();
	fProgress->setProgress(fProgress->totalSteps());
	for (int i=0; i<3; ++i)
	{
		PilotLinkList::iterator end(mDeviceLinks[i].end());
		for (PilotLinkList::iterator it=mDeviceLinks[i].begin(); it!=end; ++it)
		{
			(*it)->close();
			KPILOT_DELETE(*it);
		}
		mDeviceLinks[i].clear();
	}


	PilotDaemonDCOP_stub *daemonStub = new PilotDaemonDCOP_stub("kpilotDaemon", "KPilotDaemonIface");
	if (daemonStub)
	{
		daemonStub->startListening();
	}
	KPILOT_DELETE(daemonStub);
}

