/* KPilot
**
** Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
**
** This file defines the todoWidget, that part of KPilot that
** displays todo records from the Pilot.
*/

/*
** 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 <tqptrlist.h>
#include <tdelistview.h>
#include <tqpushbutton.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqtextview.h>
#include <tqcombobox.h>
#include <tqwhatsthis.h>
#include <tqtextcodec.h>

#include <tdemessagebox.h>

#include "kpilotConfig.h"
#include "todoEditor.h"
#include "pilotLocalDatabase.h"
#include "todoWidget.moc"




TodoCheckListItem::TodoCheckListItem(TQListView*parent, const TQString&text,
	recordid_t pilotid, void*r):PilotCheckListItem(parent, text, pilotid, r)
{

}

void TodoCheckListItem::stateChange(bool state)
{
	TodoListView*par=dynamic_cast<TodoListView*>(listView());
	if (par) par->itemWasChecked(this, state);
}



TodoWidget::TodoWidget(TQWidget * parent,
	const TQString & path) :
	PilotComponent(parent, "component_todo", path),
	fTodoInfo(0L),
	fTodoAppInfo(0L),
	fTodoDB(0L),
	fPendingTodos(0)
{
	FUNCTIONSETUP;

	setupWidget();
	fTodoList.setAutoDelete(true);

}

TodoWidget::~TodoWidget()
{
	FUNCTIONSETUP;
	KPILOT_DELETE( fTodoDB );
}

int TodoWidget::getAllTodos(PilotDatabase * todoDB)
{
	FUNCTIONSETUP;

	int currentRecord = 0;
	PilotRecord *pilotRec;
	PilotTodoEntry *todo;

#ifdef DEBUG
	DEBUGKPILOT << fname << ": Reading ToDoDB..." << endl;
#endif

	while ((pilotRec = todoDB->readRecordByIndex(currentRecord)) != 0L)
	{
		if (!(pilotRec->isDeleted()) &&
			(!(pilotRec->isSecret()) || KPilotSettings::showSecrets()))
		{
			todo = new PilotTodoEntry(pilotRec);
			if (todo == 0L)
			{
				WARNINGKPILOT << "Couldn't allocate record "
					<< currentRecord++
					<< endl;
				break;
			}
			fTodoList.append(todo);
		}
		KPILOT_DELETE( pilotRec );

		currentRecord++;
	}

#ifdef DEBUG
	DEBUGKPILOT << fname
		<< ": Total " << currentRecord << " records" << endl;
#endif

	return currentRecord;
}

void TodoWidget::showComponent()
{
	FUNCTIONSETUP;
	if ( fPendingTodos>0 ) return;

#ifdef DEBUG
	DEBUGKPILOT << fname
		<< ": Reading from directory " << dbPath() << endl;
#endif

	fTodoDB = new PilotLocalDatabase(dbPath(), CSL1("ToDoDB"));

	fTodoList.clear();

	if (fTodoDB->isOpen())
	{
		KPILOT_DELETE(fTodoAppInfo);
		fTodoAppInfo = new PilotToDoInfo(fTodoDB);
		populateCategories(fCatList, fTodoAppInfo->categoryInfo());
		getAllTodos(fTodoDB);

	}
	else
	{
		populateCategories(fCatList, 0L);
		WARNINGKPILOT << "Could not open local TodoDB" << endl;
	}

	KPILOT_DELETE( fTodoDB );

	updateWidget();
}

/* virtual */ bool TodoWidget::preHotSync(TQString &s)
{
	FUNCTIONSETUP;

	if (fPendingTodos)
	{
#ifdef DEBUG
		DEBUGKPILOT << fname
			<< ": fPendingTodo="
			<< fPendingTodos
			<< endl;
#endif

		s = i18n("There is still a to-do editing window open.",
			"There are still %n to-do editing windows open.",
			fPendingTodos);
		return false;
	}

	return true;
}

void TodoWidget::postHotSync()
{
	FUNCTIONSETUP;

	fTodoList.clear();
	showComponent();
}

void TodoWidget::hideComponent()
{
	FUNCTIONSETUP;
	if ( fPendingTodos==0 )
	{
		fTodoList.clear();
		fListBox->clear();
		KPILOT_DELETE( fTodoDB );
	}
}

void TodoWidget::setupWidget()
{
	FUNCTIONSETUP;

	TQLabel *label;
	TQGridLayout *grid = new TQGridLayout(this, 6, 4, SPACING);

	fCatList = new TQComboBox(this);
	grid->addWidget(fCatList, 0, 1);
	connect(fCatList, TQ_SIGNAL(activated(int)),
		this, TQ_SLOT(slotSetCategory(int)));
	TQWhatsThis::add(fCatList,
		i18n("<qt>Select the category of to-dos to display here.</qt>"));

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

	fListBox = new TodoListView(this);
	fListBox->addColumn( i18n( "To-do Item" ) );
	fListBox->setAllColumnsShowFocus( true );
	fListBox->setResizeMode( TDEListView::LastColumn );
	fListBox->setFullWidth( true );
	fListBox->setItemsMovable( false );
	fListBox->setItemsRenameable (true);
	grid->addMultiCellWidget(fListBox, 1, 1, 0, 1);
	connect(fListBox, TQ_SIGNAL(selectionChanged(TQListViewItem*)),
		this, TQ_SLOT(slotShowTodo(TQListViewItem*)));
	connect(fListBox, TQ_SIGNAL(doubleClicked(TQListViewItem*)),
		this, TQ_SLOT(slotEditRecord(TQListViewItem*)));
	connect(fListBox, TQ_SIGNAL(returnPressed(TQListViewItem*)),
		this, TQ_SLOT(slotEditRecord(TQListViewItem*)));
	connect(fListBox, TQ_SIGNAL(itemChecked(TQCheckListItem*, bool)),
		this, TQ_SLOT(slotItemChecked(TQCheckListItem*, bool)));
	connect(fListBox, TQ_SIGNAL(itemRenamed(TQListViewItem*, const TQString &, int)),
		this, TQ_SLOT(slotItemRenamed(TQListViewItem*, const TQString &, int)));
	TQWhatsThis::add(fListBox,
		i18n("<qt>This list displays all the to-dos "
			"in the selected category. Click on "
			"one to display it to the right.</qt>"));

	label = new TQLabel(i18n("To-do info:"), this);
	grid->addWidget(label, 0, 2);

	// todo info text view
	fTodoInfo = new TQTextView(this);
	grid->addMultiCellWidget(fTodoInfo, 1, 4, 2, 2);

	TQPushButton *button;
	TQString wt;

	fEditButton = new TQPushButton(i18n("Edit Record..."), this);
	grid->addWidget(fEditButton, 2, 0);
	connect(fEditButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotEditRecord()));

	wt = KPilotSettings::internalEditors() ?
		i18n("<qt>You can edit a to-do when it is selected.</qt>") :
		i18n("<qt><i>Editing is disabled by the 'internal editors' setting.</i></qt>");
	TQWhatsThis::add(fEditButton,wt);

	button = new TQPushButton(i18n("New Record..."), this);
	grid->addWidget(button, 2, 1);
	connect(button, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotCreateNewRecord()));
	wt = KPilotSettings::internalEditors() ?
		i18n("<qt>Add a new to-do to the to-do list.</qt>") :
		i18n("<qt><i>Adding new to-dos is disabled by the 'internal editors' setting.</i></qt>");
	TQWhatsThis::add(button, wt);
	button->setEnabled(KPilotSettings::internalEditors());

	fDeleteButton = new TQPushButton(i18n("Delete Record"), this);
	grid->addWidget(fDeleteButton, 3, 0);
	connect(fDeleteButton, TQ_SIGNAL(clicked()),
		this, TQ_SLOT(slotDeleteRecord()));
	wt = KPilotSettings::internalEditors() ?
		i18n("<qt>Delete the selected to-do from the to-do list.</qt>") :
		i18n("<qt><i>Deleting is disabled by the 'internal editors' setting.</i></qt>") ;
	TQWhatsThis::add(fDeleteButton,wt);
}

void TodoWidget::updateWidget()
{
	FUNCTIONSETUP;
	if (!shown || !fTodoAppInfo ) return;

	int listIndex = 0;

	int currentCatID = findSelectedCategory(fCatList,
		fTodoAppInfo->categoryInfo());

	fListBox->clear();
	fTodoList.first();

#ifdef DEBUG
	DEBUGKPILOT << fname << ": Adding records..." << endl;
#endif

	PilotTodoEntry*todo;
	while (fTodoList.current())
	{
		todo=fTodoList.current();
		if ((currentCatID == -1) ||
			(todo->category() == currentCatID))
		{
			TQString title = todo->getDescription();

			TodoCheckListItem*item=new TodoCheckListItem(fListBox, title,
				listIndex, todo);
			item->setOn(todo->getComplete());
		}
		listIndex++;
		fTodoList.next();
	}

#ifdef DEBUG
	DEBUGKPILOT << fname << ": " << listIndex << " records" << endl;
#endif

	slotUpdateButtons();
}



/* slot */ void TodoWidget::slotUpdateButtons()
{
	FUNCTIONSETUP;

	bool enabled = (fListBox->currentItem() != 0L);

	enabled &= KPilotSettings::internalEditors() ;

	fEditButton->setEnabled(enabled);
	fDeleteButton->setEnabled(enabled);
}

void TodoWidget::slotSetCategory(int)
{
	FUNCTIONSETUP;

	updateWidget();
}

void TodoWidget::slotEditRecord()
{
	slotEditRecord(fListBox->currentItem());
}
void TodoWidget::slotEditRecord(TQListViewItem*item)
{
	FUNCTIONSETUP;
	if (!shown) return;

	TodoCheckListItem*p = static_cast<TodoCheckListItem*>(item);
	if (!p) return;
	PilotTodoEntry *selectedRecord = (PilotTodoEntry *) p->rec();

	if (selectedRecord->id() == 0)
	{
		KMessageBox::error(0L,
			i18n("Cannot edit new records until "
				"HotSynced with Pilot."),
			i18n("HotSync Required"));
		return;
	}

	TodoEditor *editor = new TodoEditor(selectedRecord,
		fTodoAppInfo->info(), this);

	connect(editor, TQ_SIGNAL(recordChangeComplete(PilotTodoEntry *)),
		this, TQ_SLOT(slotUpdateRecord(PilotTodoEntry *)));
	connect(editor, TQ_SIGNAL(cancelClicked()),
		this, TQ_SLOT(slotEditCancelled()));
	editor->show();

	fPendingTodos++;
}

void TodoWidget::slotCreateNewRecord()
{
	FUNCTIONSETUP;
	if (!shown) return;

	// Response to bug 18072: Don't even try to
	// add records to an empty or unopened database,
	// since we don't have the DBInfo stuff to deal with it.
	//
	//
	PilotDatabase *myDB = new PilotLocalDatabase(dbPath(), CSL1("ToDoDB"));

	if (!myDB || !myDB->isOpen())
	{
#ifdef DEBUG
		DEBUGKPILOT << fname
			<< ": Tried to open "
			<< dbPath()
			<< "/ToDoDB"
			<< " and got pointer @"
			<< (void *) myDB
			<< " with status "
			<< ( myDB ? myDB->isOpen() : false )
			<< endl;
#endif

		KMessageBox::sorry(this,
			i18n("You cannot add to-dos to the to-do list "
				"until you have done a HotSync at least once "
				"to retrieve the database layout from your Pilot."),
			i18n("Cannot Add New To-do"));

		if (myDB)
			KPILOT_DELETE( myDB );

		return;
	}

	TodoEditor *editor = new TodoEditor(0L,
		fTodoAppInfo->info(), this);

	connect(editor, TQ_SIGNAL(recordChangeComplete(PilotTodoEntry *)),
		this, TQ_SLOT(slotAddRecord(PilotTodoEntry *)));
	connect(editor, TQ_SIGNAL(cancelClicked()),
		this, TQ_SLOT(slotEditCancelled()));
	editor->show();

	fPendingTodos++;
}

void TodoWidget::slotAddRecord(PilotTodoEntry * todo)
{
	FUNCTIONSETUP;
	if ( !shown && fPendingTodos==0 ) return;

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


	todo->PilotRecordBase::setCategory(currentCatID);
	fTodoList.append(todo);
	writeTodo(todo);
	// TODO: Just add the new record to the lists
	updateWidget();

	// k holds the item number of the todo just added.
	//
//	int k = fListBox->count() - 1;
//
//	fListBox->setCurrentItem(k);	// Show the newest one
//	fListBox->setBottomItem(k);

	fPendingTodos--;
	if ( !shown && fPendingTodos==0 ) hideComponent();
}

void TodoWidget::slotUpdateRecord(PilotTodoEntry * todo)
{
	FUNCTIONSETUP;
	if ( !shown && fPendingTodos==0 ) return;

	writeTodo(todo);
	TodoCheckListItem* currentRecord = static_cast<TodoCheckListItem*>(fListBox->currentItem());

	// TODO: Just change the record
	updateWidget();
	fListBox->setCurrentItem(currentRecord);

	emit(recordChanged(todo));

	fPendingTodos--;
	if ( !shown && fPendingTodos==0 ) hideComponent();
}

void TodoWidget::slotEditCancelled()
{
	FUNCTIONSETUP;

	fPendingTodos--;
	if ( !shown && fPendingTodos==0 ) hideComponent();
}

void TodoWidget::slotDeleteRecord()
{
	FUNCTIONSETUP;
	if (!shown) return;

	TodoCheckListItem* p = static_cast<TodoCheckListItem*>(fListBox->currentItem());
	if (p == 0L) return;

	PilotTodoEntry *selectedRecord = (PilotTodoEntry *) p->rec();

	if (selectedRecord->id() == 0)
	{
		KMessageBox::error(this,
			i18n("New records cannot be deleted until "
				"HotSynced with pilot."),
			i18n("HotSync Required"));
		return;
	}

	if (KMessageBox::questionYesNo(this,
			i18n("Delete currently selected record?"),
			i18n("Delete Record?"), KStdGuiItem::del(), KStdGuiItem::cancel()) == KMessageBox::No)
		return;

	selectedRecord->setDeleted(true);
	writeTodo(selectedRecord);
	emit(recordChanged(selectedRecord));
	showComponent();
}



void TodoWidget::slotShowTodo(TQListViewItem*item)
{
	FUNCTIONSETUP;
	if (!shown) return;

	TodoCheckListItem *p = dynamic_cast<TodoCheckListItem*>(item);
	if (!p) return;
	PilotTodoEntry *todo = (PilotTodoEntry *) p->rec();

#ifdef DEBUG
	DEBUGKPILOT << fname << ": Showing "<< todo->getDescription()<<endl;
#endif

	TQString text(CSL1("<qt>"));
	text += todo->getTextRepresentation(TQt::RichText);
	text += CSL1("</qt>\n");
	fTodoInfo->setText(text);

	slotUpdateButtons();
}



void TodoWidget::writeTodo(PilotTodoEntry * which,
	PilotDatabase * todoDB)
{
	FUNCTIONSETUP;

	// Open a database (myDB) only if needed,
	// i.e. only if the passed-in todoDB
	// isn't valid.
	//
	//
	PilotDatabase *myDB = todoDB;
	bool usemyDB = false;

	if (myDB == 0L || !myDB->isOpen())
	{
		myDB = new PilotLocalDatabase(dbPath(), CSL1("ToDoDB"));
		usemyDB = true;
	}

	// Still no valid todo database...
	//
	//
	if (!myDB->isOpen())
	{
#ifdef DEBUG
		DEBUGKPILOT << fname << ": Todo database is not open" <<
			endl;
#endif
		return;
	}


	// Do the actual work.
	PilotRecord *pilotRec = which->pack();

	myDB->writeRecord(pilotRec);
	markDBDirty(CSL1("ToDoDB"));
	KPILOT_DELETE(pilotRec);


	// Clean up in the case that we allocated our own DB.
	//
	//
	if (usemyDB)
	{
		KPILOT_DELETE(myDB);
	}
}

void TodoWidget::slotItemChecked(TQCheckListItem*item, bool on)
{
	TodoCheckListItem*p = static_cast<TodoCheckListItem*>(item);
	if (!p) return;
	PilotTodoEntry *selectedRecord = (PilotTodoEntry *) p->rec();
	if (!selectedRecord) return;
	selectedRecord->setComplete(on);
	slotShowTodo(item);
}

void TodoWidget::slotItemRenamed(TQListViewItem*item, const TQString &txt, int nr)
{
	TodoCheckListItem*p = static_cast<TodoCheckListItem*>(item);
	if (!p) return;
	PilotTodoEntry *selectedRecord = (PilotTodoEntry *) p->rec();
	if (!selectedRecord) return;
	if (nr==0)
	{
		selectedRecord->setDescription(txt);
		slotShowTodo(item);
	}
}
