/* KPilot
**
** Copyright (C) 1998-2001 by Dan Pilone
** Copyright (C) 2001 by David Bishop (XML stuff)
** Copyright (C) 2004 by Adriaan de Groot
**
** This is the memo-viewing widget (internal conduit) used by KPilot.
*/

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

#include <pi-macros.h>
#include <pi-dlp.h>

#include <tqdir.h>
#include <tqptrlist.h>
#include <tqlistbox.h>
#include <tqfile.h>
#include <tqpushbutton.h>
#include <tqlayout.h>
#include <tqdom.h>
#include <tqtextstream.h>
#include <tqwhatsthis.h>
#include <tqlabel.h>
#include <tqdatetime.h>
#include <tqptrlist.h>

#include <tdeapplication.h>
#include <tdemessagebox.h>
#include <tdefiledialog.h>
#include <tdeversion.h>
#include <ktextedit.h>

#include "kpilot.h"
#include "kpilotConfig.h"
#include "listItems.h"
#include "pilotLocalDatabase.h"
#include "pilotMemo.h"

#include "memoWidget.moc"


class MemoWidget::Private
{
public:
	Private() : fMemoAppInfo(0L) { } ;
	~Private() { KPILOT_DELETE(fMemoAppInfo); } ;

	PilotMemoInfo	*fMemoAppInfo;
	TQPtrList<PilotMemo>	fMemoList;
} ;


MemoWidget::MemoWidget(TQWidget * parent,
	const TQString & path) :
	PilotComponent(parent, "component_memo", path),
	fTextWidget(0L),
	d(new Private()),
	lastSelectedMemo(-1)
{
	FUNCTIONSETUP;

	setGeometry(0, 0,
		parent->geometry().width(), parent->geometry().height());
	setupWidget();
	d->fMemoList.setAutoDelete(true);
	slotUpdateButtons();
}

MemoWidget::~MemoWidget()
{
	FUNCTIONSETUP;
	saveChangedMemo();
	KPILOT_DELETE(d);
}


// void MemoWidget::initializeMemos(PilotDatabase *memoDB)
//
// Reads all the memos from the local database and places them
// in the selection screen.
//

void MemoWidget::initializeMemos(PilotDatabase * memoDB)
{
	FUNCTIONSETUP;


	// ShowSecrets tells us to also list memos with an attribute of "Secret"
	// or "Private"
	//
	bool showSecrets = KPilotSettings::showSecrets();

	d->fMemoList.clear();


	int currentRecord = 0;
	PilotRecord *pilotRec;
	PilotMemo *memo;

	while ((pilotRec = memoDB->readRecordByIndex(currentRecord)) != NULL)
	{
		if (!pilotRec->isDeleted())
		{
			if ((!pilotRec->isSecret()) || showSecrets)
			{
				memo = new PilotMemo(pilotRec);
				d->fMemoList.append(memo);

#ifdef DEBUG
				DEBUGKPILOT << fname <<
					": Added memo "
					<< currentRecord << endl;
#endif
			}
			else
			{
#ifdef DEBUG
				DEBUGKPILOT << fname <<
					": Skipped secret record " <<
					currentRecord << endl;
#endif
			}
		}
		else
		{
#ifdef DEBUG
			DEBUGKPILOT << fname <<
				": Skipped deleted record " <<
				currentRecord << endl;
#endif
		}

		delete pilotRec;

		currentRecord++;
	}
}


void MemoWidget::showComponent()
{
	FUNCTIONSETUP;
	if (!shown) return;

	// Get the local database - assume the call may fail and return
	// NULL, or the database object may be returned but unopened.
	//
	//
	PilotLocalDatabase *memoDB =
		new PilotLocalDatabase(dbPath(), CSL1("MemoDB"));
	if (memoDB == NULL || !memoDB->isOpen())
	{
		WARNINGKPILOT << "Can't open local database MemoDB\n";

		populateCategories(fCatList, 0L);
		updateWidget();
		return;
	}

	KPILOT_DELETE(d->fMemoAppInfo);
	d->fMemoAppInfo = new PilotMemoInfo(memoDB);

	d->fMemoAppInfo->dump();
	populateCategories(fCatList, d->fMemoAppInfo->categoryInfo());
	initializeMemos(memoDB);

	KPILOT_DELETE( memoDB );

	updateWidget();
}

void MemoWidget::hideComponent()
{
	FUNCTIONSETUP;
	saveChangedMemo();
	fCatList->clear();
	fTextWidget->clear();
	d->fMemoList.clear();
	fListBox->clear();
	lastSelectedMemo = -1;
}

void MemoWidget::postHotSync()
{
	FUNCTIONSETUP;
	d->fMemoList.clear();
	showComponent();
}


// void MemoWidget::setupWidget()
//
// Setup all the GUI components by allocating them.
//
//
void MemoWidget::setupWidget()
{
	FUNCTIONSETUP;

	TQLabel *label = NULL;
	TQPushButton *button = NULL;
	TQGridLayout *grid = new TQGridLayout(this, 5, 4, SPACING);
	TQString wt;

	fCatList = new TQComboBox(this);
	grid->addWidget(fCatList, 0, 1);
	connect(fCatList, TQT_SIGNAL(activated(int)),
		this, TQT_SLOT(slotSetCategory(int)));
	TQWhatsThis::add(fCatList,
		i18n("Select the category of addresses\n"
			"to display here."));

	(void) i18n("Memos:");
	label = new TQLabel(i18n("Category:"), this);
	label->setBuddy(fCatList);
	grid->addWidget(label, 0, 0);

	fListBox = new TQListBox(this);
	grid->addMultiCellWidget(fListBox, 1, 1, 0, 1);
	connect(fListBox, TQT_SIGNAL(highlighted(int)),
		this, TQT_SLOT(slotShowMemo(int)));
	connect(fListBox, TQT_SIGNAL(selectionChanged()),
		this,TQT_SLOT(slotUpdateButtons()));
	TQWhatsThis::add(fListBox,
		i18n("This list displays all the memos\n"
			"in the selected category. Click on\n"
			"one to display it to the right."));

	label = new TQLabel(i18n("Memo text:"), this);
	grid->addWidget(label, 0, 2);

	fTextWidget = new KTextEdit(this, "textArea");
	fTextWidget->setWordWrap(KTextEdit::WidgetWidth);
	fTextWidget->setTextFormat(TQt::PlainText);
	grid->addMultiCellWidget(fTextWidget, 1, 4, 2, 2);
	TQWhatsThis::add(fTextWidget,
		i18n("The text of the selected memo appears here."));
	fTextWidget->setReadOnly(!KPilotSettings::internalEditors());

	button = new TQPushButton(i18n("Import Memo..."), this);
	grid->addWidget(button, 2, 0);
	connect(button, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotImportMemo()));
	wt = KPilotSettings::internalEditors() ?
		i18n	("Read a text file and add it to the Pilot's memo database.") :
		i18n("<qt><i>Import is disabled by the 'internal editors' setting.</i></qt>");
	TQWhatsThis::add(button,wt);

	fExportButton = new TQPushButton(i18n("Export Memo..."), this);
	grid->addWidget(fExportButton, 2, 1);
	connect(fExportButton, TQT_SIGNAL(clicked()), this,
		TQT_SLOT(slotExportMemo()));
	TQWhatsThis::add(fExportButton,
		i18n("Write the selected memo to a file."));

	fDeleteButton = new TQPushButton(i18n("Delete Memo"), this);
	grid->addWidget(fDeleteButton, 3, 1);
	connect(fDeleteButton, TQT_SIGNAL(clicked()), this,
		TQT_SLOT(slotDeleteMemo()));
	wt = KPilotSettings::internalEditors() ?
		i18n("Delete the selected memo.") :
		i18n("<qt><i>Deleting is disabled by the 'internal editors' setting.</i></qt>") ;
	TQWhatsThis::add(fDeleteButton, wt);

	button = new TQPushButton(i18n("Add Memo"), this);
	grid->addWidget(button, 3, 0);
	connect(button, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotAddMemo()));
	TQWhatsThis::add(button,i18n("Add a new memo to the database."));
}

void MemoWidget::slotUpdateButtons()
{
	FUNCTIONSETUP;

	bool highlight = false;

	if ((fListBox->currentItem() != -1) &&
		(fListBox->isSelected(fListBox->currentItem())))
			highlight=true;

#ifdef DEBUG
	DEBUGKPILOT << fname << ": Selected items " << highlight << endl;
#endif

	if (fExportButton)
	{
		fExportButton->setEnabled(highlight);
	}

	//  The remaining buttons are relevant only if the
	// internal editors are editable.
	highlight &= KPilotSettings::internalEditors() ;
	if (fDeleteButton)
	{
		fDeleteButton->setEnabled(highlight);
	}
}

void MemoWidget::slotSetCategory(int)
{
	FUNCTIONSETUP;
	updateWidget();
}

void MemoWidget::slotDeleteMemo()
{
	FUNCTIONSETUP;
	if (!shown) return;

	int item = fListBox->currentItem();

	if (item == -1)
	{
#ifdef DEBUG
		DEBUGKPILOT << fname << ": No current item selected\n";
#endif
		return;
	}
	if (KMessageBox::questionYesNo(this,
			i18n("Delete currently selected memo?"),
			i18n("Delete Memo?"), KStdGuiItem::del(), KStdGuiItem::cancel()) != KMessageBox::Yes)
	{
#ifdef DEBUG
		DEBUGKPILOT << fname <<
			": User decided not to delete memo.\n";
#endif
		return;
	}

	PilotListItem *p = (PilotListItem *) fListBox->item(item);
	PilotMemo *selectedMemo = (PilotMemo *) p->rec();

	if (selectedMemo->id() == 0x0)
	{
#ifdef DEBUG
		DEBUGKPILOT << fname << ": Searching for record to delete (it's fresh)" << endl;
#endif
		PilotLocalDatabase *memoDB = new PilotLocalDatabase(dbPath(), CSL1("MemoDB"));
		if (!memoDB || (!memoDB->isOpen()))
		{
			// Err.. peculiar.
			WARNINGKPILOT << "Can't open MemoDB" << endl;
			KMessageBox::sorry(this,
				i18n("Cannot open MemoDB to delete record."),
				i18n("Cannot Delete Memo"));
			return;
		}
		memoDB->resetDBIndex();
#ifdef DEBUG
		DEBUGKPILOT << fname << ": Searching for new record." << endl;
#endif
		const PilotRecord *r = 0L;
		while ((r = memoDB->findNextNewRecord()))
		{
#ifdef DEBUG
			DEBUGKPILOT << fname << ": got record " << (void *) r << endl;
#endif
			PilotMemo m(r);
			if (m.text() == selectedMemo->text())
			{
#ifdef DEBUG
				DEBUGKPILOT << fname << ": I think I found the memo." << endl;
#endif
				(const_cast<PilotRecord *>(r))->setDeleted(true);
				break;
			}
		}
		delete memoDB;
	}
	else
	{
		selectedMemo->setDeleted(true);
		writeMemo(selectedMemo);
	}
	d->fMemoList.remove(selectedMemo);
	delete p;
}


void MemoWidget::updateWidget()
{
	FUNCTIONSETUP;
	if (!shown || !d->fMemoAppInfo ) return;

	if (fCatList->currentItem() == -1)
	{
#ifdef DEBUG
		DEBUGKPILOT << fname << ": No category selected.\n";
#endif
		return;
	}

	int listIndex = 0;
	int currentCatID = findSelectedCategory(fCatList,
		d->fMemoAppInfo->categoryInfo(), false);


	fListBox->clear();
	d->fMemoList.first();


	// Iterate through all the memos and insert each memo
	// only if the category of the memo matches the selected category
	// (using -1 to mean "All")
	//
	//
	while (d->fMemoList.current())
	{
		PilotMemo *curr = d->fMemoList.current();
		if ((curr->category() == currentCatID) ||
			(currentCatID == -1))
		{
			PilotListItem *p =
				new PilotListItem(curr->shortTitle(),
				listIndex,
				curr);

			// List will delete the title of the memo,
			// so there's no memory leak here.
			//
			//
			fListBox->insertItem(p);

#ifdef DEBUG
			DEBUGKPILOT << fname << ": Added memo "
				<< curr->getTitle() << endl;
#endif
		}
		else
		{
#ifdef DEBUG
			DEBUGKPILOT << fname << ": Skipped memo "
				<< curr->getTitle() << endl;
#endif
		}

		listIndex++;
		d->fMemoList.next();
	}

	fTextWidget->clear();

	slotUpdateButtons();

	lastSelectedMemo=-1;
}

void MemoWidget::showMemo(const PilotMemo *m)
{
	FUNCTIONSETUP;

	int index = fListBox->count();
	for (int x = 0; x < index; x++)
	{
		PilotMemo *p = (PilotMemo *) ((PilotListItem *)fListBox->item(x))->rec();
#ifdef DEBUG
		DEBUGKPILOT << fname << ": Memo @" << (void *) p <<endl;
		DEBUGKPILOT << fname << "       :" << fListBox->item(x)->text() << endl;
#endif
		if (m==p)
		{
			fListBox->setSelected(x,true);
			slotShowMemo(x);
			break;
		}
	}

}

void MemoWidget::slotShowMemo(int which)
{
	FUNCTIONSETUP;
	if ( which == -1 ) return;
	if (!shown) return;

	slotUpdateButtons();
	if ( !fListBox->isSelected(which) )
	{
		// Handle unselecting a memo. This is easy.
		fTextWidget->blockSignals(true);
		fTextWidget->clear();
		fTextWidget->blockSignals(false);
		return;
	}


#ifdef DEBUG
	DEBUGKPILOT << fname << ": Displaying memo " << which << endl;
#endif
	fTextWidget->blockSignals(true);
	PilotListItem *p = (PilotListItem *) fListBox->item(which);
	PilotMemo *theMemo = (PilotMemo *) p->rec();
	fTextWidget->setText(theMemo->text());
	fTextWidget->blockSignals(false);
}


void MemoWidget::writeMemo(PilotMemo * which)
{
	FUNCTIONSETUP;
	if (!shown) return;
	PilotRecord *pilotRec = which->pack();
	PilotDatabase *memoDB = new PilotLocalDatabase(dbPath(), CSL1("MemoDB"));
	memoDB->writeRecord(pilotRec);
	markDBDirty(CSL1("MemoDB"));
	KPILOT_DELETE( memoDB );
	KPILOT_DELETE( pilotRec );
}

void MemoWidget::saveChangedMemo()
{
	FUNCTIONSETUP;
	if (!shown) return;

	if (-1 == lastSelectedMemo) return;
	if (!fTextWidget->isModified()) return;

#ifdef DEBUG
	DEBUGKPILOT << fname
		<< ": Saving changed memo " << lastSelectedMemo << endl;
#endif

	PilotListItem *p =
		(PilotListItem *) fListBox->item(lastSelectedMemo);
	PilotMemo *currentMemo = (PilotMemo *) p->rec();

// TODO: overload setText in PilotMemo
	currentMemo->setText(Pilot::toPilot(fTextWidget->text()));
	writeMemo(currentMemo);
}

/* virtual */ bool MemoWidget::preHotSync(TQString &)
{
	FUNCTIONSETUP;
	saveChangedMemo();
	return true;
}

bool MemoWidget::addMemo(const TQString &s, int category)
{
	FUNCTIONSETUP;

	if (s.length() >= MemoWidget::MAX_MEMO_LEN)
	{
		return false;
	}
	if ((category<0) || (category>=(int)Pilot::CATEGORY_COUNT))
	{
		category=Pilot::Unfiled;
	}

	PilotMemo *aMemo = new PilotMemo();
	aMemo->setCategory(category);
	aMemo->setText(s);

	d->fMemoList.append(aMemo);
	writeMemo(aMemo);
	updateWidget();
#ifdef DEBUG
	DEBUGKPILOT << fname << ": New memo @" << (void *)aMemo << endl;
#endif
	showMemo(aMemo);
	return true;
}

void MemoWidget::slotAddMemo()
{
	FUNCTIONSETUP;
	int currentCatID = findSelectedCategory(fCatList,
		d->fMemoAppInfo->categoryInfo(), true);
	addMemo(TQDateTime::currentDateTime().toString(), currentCatID);
}

void MemoWidget::slotImportMemo()
{
	FUNCTIONSETUP;
	if (!shown || !d->fMemoAppInfo ) return;

	int currentCatID = findSelectedCategory(fCatList,
		d->fMemoAppInfo->categoryInfo(), true);

	TQString fileName = KFileDialog::getOpenFileName();

	if (!fileName.isEmpty())
	{
		TQFile importFile(fileName);

		if (importFile.open(IO_ReadOnly) == FALSE)
		{
			// show error!
			return;
		}

		if (importFile.size() > MemoWidget::MAX_MEMO_LEN)
		{
			// Perhaps read first 64k?
			return;
		}

		TQTextStream stream(&importFile);
		TQString memoText = stream.read();
		addMemo(memoText, currentCatID);
	}
}

void MemoWidget::slotExportMemo()
{
	FUNCTIONSETUP;
	if (!shown) return;

	int index = fListBox->numRows();
	if (index == 0)
		return;

	TQString data;

	const TQString filter = CSL1("*|Plain text output\n*.xml|XML output");
	TQString fileName;

	KFileDialog tdefile( TQString() , filter, fExportButton , "memoSave" , true );
	tdefile.setOperationMode( KFileDialog::Saving );

	if ( tdefile.exec() == TQDialog::Accepted ) {
		fileName = tdefile.selectedFile();
	}

	if (fileName.isEmpty())
		return;

	TQPtrList<PilotListItem> menu_items;

	for (int x = 0; x < index; x++){
		if (fListBox->item(x)->isSelected()){
			menu_items.append((PilotListItem *) fListBox->item(x));
		}
	}

	if (tdefile.currentFilter() == CSL1("*.xml") )
	{
		MemoWidget::saveAsXML( fileName , menu_items );
	}
	else
	{
		MemoWidget::saveAsText( fileName , menu_items );
	}


	return;
}

bool MemoWidget::saveAsText(const TQString &fileName,const TQPtrList<PilotListItem> &memo_list)
{
	TQFile f( fileName );
	TQTextStream stream(&f);

	if ( TQFile::exists( fileName ) )
	{
		if( !f.open(IO_ReadWrite | IO_Append) )
		{
			return false;
		}
	}
	else
	{
		if( !f.open(IO_WriteOnly) )
		{
			return false;
		}
	}

	TQPtrListIterator<PilotListItem> it(memo_list);
	for ( ; it.current(); ++it )
	{
		PilotListItem *p = it.current();
		PilotMemo *theMemo = (PilotMemo *) p->rec();
		stream << theMemo->text() << endl;
	}


	return true;
}

bool MemoWidget::saveAsXML(const TQString &fileName,const TQPtrList<PilotListItem> &memo_list)
{
	TQDomDocument doc( CSL1("kpilotmemos") );
	TQFile f( fileName );
	TQTextStream stream( &f );
	TQDomElement memos;
	int append = 0;


	if ( f.exists() )
	{
		if ( !f.open(IO_ReadOnly ) ) return false;

		if ( doc.setContent( &f ) )
		{
		//
		//
		//Only if TQDom can read the .xml file and set the doc object to be populated with it's contents
			memos = doc.documentElement();
			if ( memos.tagName()!= CSL1("memos") )
			{
				return false;
			}
				//
				//
				//This is an XML Document but it isn't a valid KPilot-Memo xml document
			else
			{
				append = 1;
			}
				//
				//
				//This is a valid KPilot memo, and we will append the current memo to the xml
		}
		else
		{
		//
		//
		//We *couldn't* understand the xml.  Return false!
			return false;
		}
	}
	else
	{
		if ( !f.open(IO_ReadWrite ) ) return false;
		//
		//
		//If there's no such file, we are not appending, just opening the file to read/write.
	}

	f.close();
    // These are temporary, and should be retrieved from the pilot stuff
    TQString mpilotid;
    mpilotid = "1";
    //  End of temp variables

	if (append == 1)
	{
		memos = doc.documentElement();
	}
	else
	{
		memos = doc.createElement( CSL1("memos") );
		doc.appendChild ( memos );
	}

	TQPtrListIterator<PilotListItem> it(memo_list);
	for ( ; it.current(); ++it )
	{
		PilotListItem *p = it.current();
		PilotMemo *theMemo = (PilotMemo *) p->rec();


    	TQDomElement memo = doc.createElement( CSL1("memo") );
	    memo.setAttribute ( CSL1("pilotid") , mpilotid );
	    memos.appendChild ( memo );

	    //TQDomElement category = doc.createElement( "category" );
	    //head.appendChild ( category );
		//
	 	//TQDomText categorytext = doc.createTextNode( memo->category() );
		//category.appendChild ( categorytext );
		//FIXME

		TQDomElement title = doc.createElement(CSL1("title" ));
		memo.appendChild ( title );

		TQDomText titletext = doc.createTextNode( theMemo->shortTitle() );
		title.appendChild ( titletext );

		TQDomText body = doc.createTextNode( theMemo->text() );
		memo.appendChild ( body );
	}
	if ( !f.open(IO_WriteOnly ) ) return false;
	stream << doc.toString();
	return true;
}

