/***************************************************************************
                          kfocustasksview.cpp  -  description
                             -------------------
    begin                : Mon Aug 13 2001
    copyright            : (C) 2001 by Jeffrey Yu
    email                : jeffyu@cs.stanford.edu
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
#include <stdio.h>
#include <qlayout.h>
#include <qframe.h>
#include <qstring.h>
#include <qmessagebox.h>
#include <qpushbutton.h>
#include <qlistview.h>
#include <qtoolbutton.h>
#include <qlabel.h>

#include <kiconloader.h>
#include <kmessagebox.h>

#include "kfocustasksview.h"
#include "kfocustaskpropsview.h"
#include "kfocustask.h"
#include "kfocustasklist.h"
#include "kfmenddate.h"

KFVTaskList::KFVTaskList(QWidget *parent, const char *name )
	: QWidget(parent,name)
{
	taskList_ = NULL;
	currentTask_ = NULL;
	itemList_.setAutoDelete(true);
	
	createWidgets();
	connect(tbUp_, SIGNAL( clicked() ), this, SLOT( onUpButtonClicked() ));
	connect(tbDown_, SIGNAL( clicked() ), this, SLOT( onDownButtonClicked() ));
	connect(tbTrash_, SIGNAL( clicked() ), this, SLOT( onPbDeleteClicked() ));
		
	active_ = true;
	enterBaseState();
}


KFVTaskList::~KFVTaskList(){
}

////////////////////////////////////////////////////////////////////////////////
// Private methods

/** Make the widgets in the view */
void KFVTaskList::createWidgets(){
	QHBoxLayout* hbl = new QHBoxLayout(this);
		hbl->setMargin(7);
		hbl->setSpacing(7);
		
		// Layout the left panel
		QVBoxLayout* vbl = new QVBoxLayout(hbl);
			vbl->setSpacing(7);
			
			// Panel label
			vbl->addWidget(new QLabel("Task", this));
			
			// Layout listview and buttons
			QHBoxLayout* hbl0 = new QHBoxLayout(vbl);

				// Layout listview and horz buttons			
				QVBoxLayout* vbl0 = new QVBoxLayout(hbl0);
			
					// ListView
					lvTasks_ = new QListView(this, "TaskList QListView");			
					posCol_ = lvTasks_->addColumn("#");
					etaCol_ = lvTasks_->addColumn("ETA");
						lvTasks_->setColumnAlignment(etaCol_, AlignRight);
					titleCol_ = lvTasks_->addColumn("Task");			
					lvTasks_->setSorting(-1);
					lvTasks_->setAllColumnsShowFocus(true);

					vbl0->addWidget(lvTasks_);
	
					// Horizontal buttons below
					QHBoxLayout* hbl1 = new QHBoxLayout(vbl0);
						hbl1->setSpacing(7);
			
						pbAdd_ = new QPushButton("&Add", this);
							hbl1->addWidget(pbAdd_);
				
						pbComplete_ = new QPushButton("&Complete", this);
							pbComplete_->setEnabled(false);
							hbl1->addWidget(pbComplete_);
		
						pbDelete_ = new QPushButton("&Delete", this);
							pbDelete_->setEnabled(false);
							hbl1->addWidget(pbDelete_);
				
				// Layout vert buttons
				QVBoxLayout* vbl1 = new QVBoxLayout(hbl0);
					vbl1->setSpacing(3);
		
					tbUp_ = new QToolButton(this);
						tbUp_->setIconSet(toolIconSet("1uparrow"));
						tbUp_->setAutoRepeat(true);
						tbUp_->setAutoRaise(true);
						tbUp_->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
						tbUp_->setEnabled(false);
						vbl1->addWidget(tbUp_);
			
					tbDown_ = new QToolButton(this);
						tbDown_->setIconSet(toolIconSet("1downarrow"));
						tbDown_->setAutoRepeat(true);
						tbDown_->setAutoRaise(true);
						tbDown_->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
						tbDown_->setEnabled(false);
						vbl1->addWidget(tbDown_);
							
					tbTrash_ = new QToolButton(this);
						tbTrash_->setIconSet(toolIconSet("edittrash"));
						tbTrash_->setAutoRaise(true);
						tbTrash_->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
						tbTrash_->setEnabled(false);
						vbl1->addWidget(tbTrash_);
				
					QWidget* filler = new QWidget(this);
						filler->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding));
						vbl1->addWidget(filler);
		
		// Divider between tasklist and task properties
		QFrame* fDivider = new QFrame(this);
			fDivider->setFrameShape(QFrame::VLine);
			fDivider->setFrameShadow(QFrame::Raised);
			fDivider->setMidLineWidth(1);			
			hbl->addWidget(fDivider);

		// Task properties	
		hbl->addWidget(props_ = new KFVTask(this));
}


/** Set the current task */
void KFVTaskList::currentTask(KFMTask* task){
	if (task != currentTask_) {
		if (currentTask_ != NULL) {
			KFMTask* oldTask = currentTask_;
			currentTask_ = NULL;
			
			// Disconnect the old task.  It may have been deleted already,
			// so check that it exists first
			if (taskList_->taskPosition(oldTask) >= 0) {
				disconnect(oldTask, 0, this, 0);
			}

			lvTasks_->clearSelection();

			updateTrashButton();	
			updateArrows();
			pbDelete_->setEnabled(false);
			pbComplete_->setEnabled(false);	
			props_->task(NULL);
		}
		
		currentTask_ = task;
		if (currentTask_ != NULL) {
			// Do stuff to restore the current task
			props_->task(currentTask_);
			//props_->titleLineEditSelected(true);
			pbComplete_->setEnabled(true);
			pbDelete_->setEnabled(true);
			updateArrows();
			updateTrashButton();
			
			lvTasks_->setSelected(taskViewItem(currentTask_), true);
			
			connect(currentTask_, SIGNAL( titleChanged() ),	this,
					SLOT( onCurrentTaskTitleChanged() ));
		}	
	}
}


/** Load the view's listview */
void KFVTaskList::loadListView(){
	unloadListView();
	reloadListView();
}

/** Unload the view's list box */
void KFVTaskList::unloadListView(){
	// Deleting the itemList also clears lvTasks_
	itemList_.clear();
}

/** Load the view's ListView with fresh data */
void KFVTaskList::reloadListView(){
	int i = 0;
	QListViewItem* last = NULL;
	
	KFMTaskList::Iterator it = taskList_->taskIterator();
	for (;it.current();++it,++i) {
		bool append;
		QListViewItem* item;
		
		if (i >= (int) itemList_.count()) {		
			if (last == NULL) {
				item = new QListViewItem(lvTasks_);
			}
			else {
				item = new QListViewItem(lvTasks_, last);
			}		
			append = true;
		}
		else {
			item = itemList_.at(i);
			append = false;
		}
		
		fillViewItem(item, it.current());
		if (append) {		
			itemList_.append(item);
		}
		
		last = item;
	}
	
	// Remove extra view items
	while (i < (int) itemList_.count()) {
		itemList_.remove(i);
	}
	
	// Restore the selection
	if (taskList_ != NULL && taskList_->currentPosition() >= 0) {
		lvTasks_->setSelected(itemList_.at(taskList_->currentPosition()), true);
	}
}


void KFVTaskList::updateArrows(){
	if (currentTask() == NULL) {
		tbUp_->setEnabled(false);
		tbDown_->setEnabled(false);
	}
	else {
		tbUp_->setEnabled(taskList_->taskCanRise(currentTask()));
		tbDown_->setEnabled(taskList_->taskCanFall(currentTask()));
	}
}


/** Update the state of the trash toolbutton */
void KFVTaskList::updateTrashButton(){
	tbTrash_->setEnabled(taskList_->size() > 0);
}


/** Retrieve an QIconSet for a tool button */
QIconSet KFVTaskList::toolIconSet(QString name){
	static KIconLoader iconLoader;
	
	QIconSet icon(iconLoader.loadIcon(name, KIcon::Toolbar));
		icon.setPixmap(iconLoader.loadIcon(	name,
																				KIcon::Toolbar,
																				0, KIcon::DisabledState),
									 QIconSet::Automatic,
									 QIconSet::Disabled);
	
	return icon;
}


void KFVTaskList::fillViewItem(QListViewItem* item, const KFMTask* task){
	item->setText(posCol_, QString::number(task->position()+1) + ". ");
		
	QString etaString = "";
		
		if (!task->started())
			etaString += "[";
			
		switch (task->endDate()->type()) {
		case KFMEndDate::Absolute:
			if (task->endDate()->eta() >= 0)
				etaString += "+" + QString::number(task->endDate()->eta());
			else
				etaString += QString::number(task->endDate()->eta());				
			break;
		default: /* KFMEndDate::NotADate */
			etaString += "";
			break;
		}
		
				
		if (!task->started())
			etaString += "]";
		
		item->setText(etaCol_, etaString);		
	
	item->setText(titleCol_, task->title());
}


QListViewItem* KFVTaskList::taskViewItem(const KFMTask* task){
	return itemList_.at(task->position());
}


/** Switch into the active state */
void KFVTaskList::enterActiveState(){
	if (active_) return;
	active_ = true;

	/* Connect to signals from the tasklist */	
	connect(taskList_, SIGNAL( taskAdded(KFMTask*) ), this,
			SLOT( onTaskListTaskAdded(KFMTask*) ));
	connect(taskList_, SIGNAL( taskRemoved(KFMTask*) ), this,
			SLOT( onTaskListTaskRemoved(KFMTask*) ));
	connect(taskList_, SIGNAL( currentTaskChanged(KFMTask*) ), this,
			SLOT( currentTask(KFMTask*) ));
	connect(taskList_, SIGNAL( orderChanged() ), this, SLOT( onTaskListOrderChanged() ));
	connect(taskList_, SIGNAL( cleared() ), this, SLOT( enterBaseState() ));
	
	/* If there's any changes to the widgets that may alter the
		tasklist, do it here before we connect the widgets.  We do
		not want to tell the tasklist it has new values unless it
		truly does have new values. */

	/* Connect to signals from the widgets.  This must occur before
		changing the widgets so widget signals get processed */
	connect(lvTasks_, SIGNAL( selectionChanged(QListViewItem*) ), this,
			SLOT( onLvTasksSelectionChanged(QListViewItem*) ));
	connect(pbDelete_, SIGNAL( clicked() ),	this, SLOT( onPbDeleteClicked() ));
	connect(pbComplete_, SIGNAL( clicked() ),	this, SLOT( onPbCompleteClicked() ));
	connect(pbAdd_, SIGNAL( clicked() ), this, SLOT( onPbAddClicked() ));
	
	/* Initialize the widgets, but only after we're connected to them. */
	loadListView();						
	pbAdd_->setEnabled(true);
	updateTrashButton();
	currentTask(taskList_->currentTask());

	setEnabled(true);
}

/** Put the tasks view into its base state */
void KFVTaskList::enterBaseState(){
	if (!active_) return;
	setEnabled(false);

	/* Reset the widgets.  This must be done before disconnecting them
			so that widget signals get processed.  Don't do anything to
			change the contents of the tasklist, though. */
	currentTask(NULL);	
	tbTrash_->setEnabled(false);
	pbAdd_->setEnabled(false);	
	unloadListView();
	
	/* Disconnect the widgets, but only after we've reset them. */
	disconnect(pbAdd_, 0, this, 0);
	disconnect(pbComplete_, 0, this, 0);
	disconnect(pbDelete_, 0, this, 0);
	disconnect(lvTasks_, 0, this, 0);
	
	/* If there's anything we do to the widgets that might change the
		contents of the tasklist, do it here so that signals from the
		widgets aren't processed.  We just want to reset the widgets;
		we do not want to change the tasklist to have default widget
		values. */
		
	/* Disconnect from the tasklist */
	if (taskList_ != NULL)
		disconnect(taskList_, 0, this, 0);

	active_ = false;	
}


/** Slot to receive titleChanged signal from currentTItle */
void KFVTaskList::onCurrentTaskTitleChanged(){
	if (currentTask() != NULL) {
		fillViewItem(taskViewItem(currentTask()), currentTask());
	}
}


/** Create a new task */
void KFVTaskList::onPbAddClicked(){
	if (taskList_ != NULL) {
		KFMTask* task = new KFMTask();
		
		// Copy the enddate from the current task if it exists.  It
		// may not exists if the task list is empty.
		if (currentTask() != NULL) {
			task->endDate((const KFMEndDate*)(currentTask()->endDate()));
		}
		taskList_->insertTask(task);
	
		// Highlight the new task's title for editing
		props_->titleLineEditSelected(true);
	}
}


/** Slot to receive the clicked signal from pbDelete */
void KFVTaskList::onPbDeleteClicked(){
	if (taskList_ != NULL) {
		if (currentTask() != NULL) {
	
			// Confirm intent
			QMessageBox mb("Confirm Delete",
				"Delete the selected task?",
				QMessageBox::Warning,
				QMessageBox::Ok | QMessageBox::Default,
				QMessageBox::Cancel | QMessageBox::Escape,
				QMessageBox::NoButton
			);
		
			if (mb.exec() != QMessageBox::Ok)
				return;
				
			KFMTask* deletedTask = currentTask();
				
			// Pick the next current item
			int currentPosition = taskList_->taskPosition(currentTask());
				if (currentPosition < (taskList_->size()-1))
					currentPosition += 1;
				else
					currentPosition = (taskList_->size()-2);
							
				if (currentPosition >= 0) {
					taskList_->currentPosition(currentPosition);
				}
			
			// Kill the delete target
			taskList_->removeTask(deletedTask);
		}
	}
}


/** Slot to receive the clicked signal from pbComplete */
void KFVTaskList::onPbCompleteClicked(){
	if (taskList_ != NULL) {
		if (currentTask() != NULL) {
			int currentPosition = taskList_->taskPosition(currentTask());
			int currentSize = taskList_->size();
			
			// Emit a signal so that KFocusView can deal with the event
			emit taskCompleted(currentTask());
			
			if (currentSize > taskList_->size()) {
				// The size is lower, meaning the task was completed wo
				// a followup task.  So, we should pick a new current item.
				if (currentPosition >= taskList_->size())
					currentPosition = taskList_->size()-1;
				if (currentPosition >= 0)
					taskList_->currentPosition(currentPosition);		
			}
		}
	}	
}

/** Slot to receive currentChanged signal from lbTasks */
void KFVTaskList::onLvTasksSelectionChanged(QListViewItem * item){
	if (item->isSelected()) {
	
		// Make the new task current
		taskList_->currentPosition(itemList_.findRef(item));
	}
}

/** Slot for when the user clicks the Up button */
void KFVTaskList::onUpButtonClicked(){
	if (currentTask() != NULL) {
		taskList_->moveTask(currentTask(), taskList_->taskPosition(currentTask())-1);
	}
}


/** Slot for when the user clicks on the Down button */
void KFVTaskList::onDownButtonClicked(){
	if (currentTask() != NULL) {
		taskList_->moveTask(currentTask(), taskList_->taskPosition(currentTask())+1);
	}
}


/** Slot to receive taskAdded signal from the tasklist */
void KFVTaskList::onTaskListTaskAdded( KFMTask* /*addedTask*/){
	
	reloadListView();
	updateArrows();
}

/** Slot to handle removal of a task from the task list */
void KFVTaskList::onTaskListTaskRemoved( KFMTask* removedTask){
	if (currentTask() == removedTask) {
		currentTask(NULL);
	}

	reloadListView();
	updateArrows();
}

/** Slot for when the tasklist ordering changes */
void KFVTaskList::onTaskListOrderChanged(){
	if (active_) {
		// Reload the ListView to reflect the changed order
		reloadListView();
		
		// Update the arrows
		updateArrows();
		
		// currentTask hasn't changed, but now the ListView has
		// the wrong currentItem
		lvTasks_->clearSelection();
		
		lvTasks_->setSelected(taskViewItem(currentTask()), true);	
	}
}


////////////////////////////////////////////////////////////////////////////
// Public methods

void KFVTaskList::taskList(KFMTaskList* taskList){
	if (taskList != taskList_) {
		if (taskList_ != NULL) {
			enterBaseState();		
		}
		
		taskList_ = taskList;
		
		if (taskList_ != NULL) {
			enterActiveState();
		}	
	}
}
