/* KPilot
**
** Copyright (C) 2004 by Adriaan de Groot <groot@kde.org>
** Copyright (C) 2004 by Joern Ahrens <joern@kpilot.org>
**
** The code for NotepadActionThread::unpackNotePad was taken from
** Angus Ainslies read-notepad.c, which is part of pilot-link.
** NotepadActionThread::saveImage is also based on read-notepad.c.
**
** This file is part of the Notepad conduit, a conduit for KPilot that
** stores the notepad drawings to files.
*/

/*
** 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 "notepad-conduit.h"  // The Conduit action

#include "notepadconduit.h"   // The settings class
#include "options.h"
#include "pilotUser.h"
#include "pilotSerialDatabase.h"

#include <pi-notepad.h>

#include <qthread.h>
#include <qapplication.h>
#include <q3valuelist.h>
#include <qimage.h>
#include <qdir.h>
#include <q3cstring.h>
//Added by qt3to4:
#include <QEvent>

NotepadConduit::NotepadConduit(KPilotLink *d,
	const QVariantList &args) : ConduitAction(d, "Notepad", args)
{
	FUNCTIONSETUP;
	fConduitName=i18n("Notepad");
	thread = 0L;

}

NotepadConduit::~NotepadConduit()
{
	FUNCTIONSETUP;
}

/* virtual */ bool NotepadConduit::exec()
{
	FUNCTIONSETUP;

#ifdef DEBUG
	DEBUGKPILOT << "In exec() @" << (unsigned long) this;
#endif

	QDir dir(NotepadConduitSettings::outputDirectory());
	if(!dir.exists() && !dir.mkdir(dir.path())) {
		emit logError(i18n("Unable to open %1",dir.path()));
		delayDone();
		return false;
	}
	else {
		thread = new NotepadActionThread(this, deviceLink());
		thread->start();
		// tickle is disabled due to crashs during sync
		// -> PADP TX "unexpected package"
//		startTickle();
	}

	return true;
}

bool NotepadConduit::event(QEvent *e)
{
	FUNCTIONSETUP;

	if(e->type() == QEvent::User) {
#ifdef DEBUG
		DEBUGKPILOT << "Notepad thread done.";
#endif
//		stopTickle();
		delayDone();
		if(thread->getFailed())
			logError(i18np("1 notepad could not be saved", "%1 notepads could not be saved", thread->getFailed()));
		logMessage(i18np("1 notepad saved", "%1 notepads saved", thread->getSaved()));
		delete thread;
		return true;
	}
	else
		return ConduitAction::event(e);
}

//-----------------------------------------------------------------------------
// NotepadActionThread
//-----------------------------------------------------------------------------

NotepadActionThread::NotepadActionThread(QObject *parent, KPilotLink *link) :
	fParent(parent), fLink(link), notSaved(0), saved(0)
{
	FUNCTIONSETUP;
}

void NotepadActionThread::run()
{
	FUNCTIONSETUP;

	PilotDatabase *db = fLink->database( CSL1("npadDB") );

	int n = db->recordCount();

	if ( n > 0 )
	{
		Q3ValueList<recordid_t> vl = db->idList();
		Q3ValueList<recordid_t>::iterator it;
		struct NotePad a;
		for ( it = vl.begin(); it != vl.end(); ++it )
		{
			PilotRecord *pr = db->readRecordById(*it);
			if(pr)
			{
				unpack_NotePad(&a, (unsigned char*)pr->data(), pr->size());
				saveImage(&a);
				free_NotePad(&a);
			}
		}
	}
	KPILOT_DELETE(db);
	QApplication::postEvent(fParent, new QEvent(QEvent::User));
}

static void saveImageFromBITS(QImage &image, struct NotePad *n, unsigned int width)
{
	FUNCTIONSETUP;
	image.setColor(0, qRgb(0xaa, 0xc1 ,0x91));
	image.setColor(1, qRgb(0x30, 0x36, 0x29));

	int x = 0;
	int y = 0;
	int pos = 0;
	for(unsigned int i=0; i<n->body.dataLen/2; ++i)
	{
		for(int j=0; j<n->data[i].repeat; ++j)
		{
			for(int k=0; k<8; ++k)
			{
				y = pos / width;
				x = pos % width ;

				image.setPixel( x, y,
					(n->data[i].data & 1<<(7-k)) ? 1 : 0 );
				++pos;
			}
		}
	}
}

static void saveImageFromUNCOMPRESSED(QImage &image, struct NotePad *n, unsigned int width)
{
	FUNCTIONSETUP;

	image.setColor(0, qRgb(0xaa, 0xc1 ,0x91));
	image.setColor(1, qRgb(0x30, 0x36, 0x29));

	unsigned int pos = 0;
	unsigned int x,y;

	for (unsigned int i=0; i<n->body.dataLen / 2; ++i)
	{
		for (unsigned int k=0; k<8; ++k)
		{
			y = pos / width;
			x = pos % width ;

			image.setPixel( x, y,
				(n->data[i].repeat & 1<<(7-k)) ? 1 : 0 );
			++pos;
		}

		for (unsigned int k=0; k<8; ++k)
		{
			y = pos / width;
			x = pos % width ;

			image.setPixel( x, y,
				(n->data[i].data & 1<<(7-k)) ? 1 : 0 );
			++pos;
		}
	}
}

void NotepadActionThread::saveImage(struct NotePad *n)
{
	FUNCTIONSETUP;

	// Width needs adjusting, based on whether it's low res (+8)
	// or a hi-res notepad image.
	int width = n->body.width + ( n->body.width > 160 ? 16 : 8 );
	int height = n->body.height;


	QImage image(width, height, QImage::Format_RGB32 );

	switch (n->body.dataType)
	{
	case NOTEPAD_DATA_BITS :
		saveImageFromBITS( image,n,width );
		break;
	case NOTEPAD_DATA_UNCOMPRESSED :
		saveImageFromUNCOMPRESSED( image,n,width );
		break;
	case NOTEPAD_DATA_PNG :
		image.loadFromData((uchar*)(n->data), n->body.dataLen);
		break;
	default :
		// Unknown data type
		WARNINGKPILOT <<"Unknown data type:" << n->body.dataType;
		return;

		// TODO: Post a warning to the UI
	}

	QString filename(n->name);
	if(filename.isEmpty())
	{
		filename.sprintf("%4d-%02d-%02d_%02d-%02d-%02d",
			n->changeDate.year,
			n->changeDate.month,
			n->changeDate.day,
			n->changeDate.hour,
			n->changeDate.min,
			n->changeDate.sec);
	}
	QString imgname = QString("%1/%2.png").arg(NotepadConduitSettings::outputDirectory()).arg(filename);


#ifdef DEBUG
	DEBUGKPILOT << "Notepad" << imgname;
#endif
	if(!image.save(imgname, "PNG", -1))
		++notSaved;
	else
		++saved;
}

