#ifndef _KPILOT_KPILOTDEVICELINKPRIVATE_H
#define _KPILOT_KPILOTDEVICELINKPRIVATE_H
/*
**
** Copyright (C) 2007 by Jason 'vanRijn' Kasper <vR@movingparts.net>
**
*/

/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser 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 <errno.h>

#include <tqstringlist.h>
#include <tqthread.h>

#include "kpilotdevicelink.h"
#include "options.h"

class TQTimer;
class TQSocketNotifier;

// singleton helper class
class DeviceMap
{
public:
	static DeviceMap *self()
	{
		if (!mThis)
			mThis = new DeviceMap();
		return mThis;
	}

	bool canBind(const TQString &device)
	{
		FUNCTIONSETUPL(5);
		DEBUGKPILOT << fname << ": device: ["
			<< device << "]" << endl;

		showList();
		return !mBoundDevices.contains(device);
	}

	void bindDevice(const TQString &device)
	{
		FUNCTIONSETUPL(5);
		DEBUGKPILOT << fname << ": device: ["
			<< device << "]" << endl;

		mBoundDevices.append(device);
		showList();
	}

	void unbindDevice(const TQString &device)
	{
		FUNCTIONSETUPL(5);
		DEBUGKPILOT << fname << ": device: ["
			<< device << "]" << endl;

		mBoundDevices.remove(device);
		showList();
	}

protected:
	DeviceMap()
	{
		mBoundDevices.clear();
	}
	~DeviceMap()
	{
	}

	TQStringList mBoundDevices;
	static DeviceMap *mThis;

private:
	void showList() const
	{
		FUNCTIONSETUPL(5);

		if ( !(mBoundDevices.count() > 0))
			return;

		DEBUGKPILOT << fname << ": Bound devices: ["
			<< ((mBoundDevices.count() > 0) ?
					mBoundDevices.join(CSL1(", ")) : CSL1("<none>"))
			<< "]" << endl;
	}
};

class Messages
{
public:
	Messages(KPilotDeviceLink *parent) :
		fDeviceLink(parent)
	{
		reset();
	}

	void reset()
	{
		messages = 0;
		messagesMask = ~messageIsError; // Never block errors
	}

	void block(unsigned int m, bool force=false)
	{
		if (force)
		{
			// Force blocking this message, even if it's an error msg.
			messages |= m;
		}
		else
		{
			messages |= (m & messagesMask);
		}
	}

	/**
	 * Some messages are only printed once and are suppressed
	 * after that. These are indicated by flag bits in
	 * messages. The following enum is a bitfield.
	 */
	enum
	{
		OpenMessage=1, ///< Trying to open device ..
		OpenFailMessage=2 ///< Failed to open device ...
	};
	int messages;
	int messagesMask;
	static const int messageIsError = 0;

	/** Determines whether message @p s which has an id of @p msgid (one of
	 *  the enum values mentioned above) should be printed, which is only if that
	 *  message has not been suppressed through messagesMask.
	 *  If return is true, this method also adds it to the messagesMask.
	 */
	bool shouldPrint(int msgid)
	{
		if (!(messages & msgid))
		{
			block(msgid);
			return true;
		}
		else
		{
			return false;
		}
	}

protected:
	KPilotDeviceLink *fDeviceLink;
};

class DeviceCommEvent : public TQEvent
{
public:
	DeviceCommEvent(DeviceCustomEvents type, TQString msg = TQString(),
			int progress = 0) :
		TQEvent( (TQEvent::Type)type ), fMessage(msg), fProgress(progress),
				fPilotSocket(-1)
	{
	}
	TQString message() const
	{
		return fMessage;
	}
	int progress()
	{
		return fProgress;
	}

	inline void setCurrentSocket(int i)
	{
		fPilotSocket = i;
	}

	inline int currentSocket()
	{
		return fPilotSocket;
	}
private:
	TQString fMessage;
	int fProgress;
	/**
	 * Pilot-link library handles for the device once it's opened.
	 */
	int fPilotSocket;
};

/** Class that handles all device communications.  We do this
 in a different thread so that we do not block the main TQt
 Event thread (similar to Swing's AWT event dispatch thread).
 */

class DeviceCommThread : public TQObject, public TQThread
{
friend class KPilotDeviceLink;

Q_OBJECT
  

public:
	DeviceCommThread(KPilotDeviceLink *d);
	virtual ~DeviceCommThread();

	virtual void run();

	void setDone(bool b)
	{
		FUNCTIONSETUP;
		fDone = b;
	}

protected:

	void close();
	
	void reset();

	/**
	 * Does the low-level opening of the device and handles the
	 * pilot-link library initialisation.
	 */
	bool open(const TQString &device = TQString());

protected slots:
	/**
	* Attempt to open the device. Called regularly to check
	* if the device exists (to handle USB-style devices).
	*/
	void openDevice();

	/**
	* Called when the device is opened *and* activity occurs on the
	* device. This indicates the beginning of a hotsync.
	*/
	void acceptDevice();
	
	/**
	* This slot fires whenever we've been trying to establish a hotsync with
	* the device for longer than a given amount of time.  When this slot is
	* fired, we will tear down the communications process and start over again.
	*/
	void workaroundUSB();

private:
	volatile bool fDone;

	KPilotDeviceLink *fHandle;
	inline KPilotDeviceLink *link()
	{
		if (fHandle)
		{
			return fHandle;
		}
		else
		{
			FUNCTIONSETUP;
			WARNINGKPILOT << "Link asked for, but either I'm "
				<< "done or I don't have a valid handle.  "
				<< "Shutting down comm thread." << endl;
			TQThread::exit();
			return 0;
		}
	}

	/**
	 * Timers and Notifiers for detecting activity on the device.
	 */
	TQTimer *fOpenTimer;
	TQSocketNotifier *fSocketNotifier;
	bool fSocketNotifierActive;

	/** Timer used to check for a badly-connected Z31/72 */
	TQTimer *fWorkaroundUSBTimer;
	
	/**
	 * Pilot-link library handles for the device once it's opened.
	 */
	int fPilotSocket;
	int fTempSocket;

	inline TQString errorMessage(int e)
	{
		switch (e)
		{
		case ENOENT:
			return i18n(" The port does not exist.");
			break;
		case ENODEV:
			return i18n(" There is no such device.");
			break;
		case EPERM:
			return i18n(" You do not have permission to open the "
				"Pilot device.");
			break;
		default:
			return i18n(" Check Pilot path and permissions.");
		}
	}
	
	/**
	* Handle cases where we can't accept or open the device,
	* and data remains available on the pilot socket.
	*/
	int fAcceptedCount;

};


#endif

