/**************************************************************************
                          kfocustasklist.cpp  -  description
                             -------------------
    begin                : Wed Aug 15 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 "kfocustasklist.h"
#include "kfocustask.h"
#include "kfmenddate.h"

KFMTaskList::KFMTaskList(){
	sorted_ = true;
	tasks_ = new QSortedList<KFMTask>;
	tasks_->setAutoDelete(true);
	currentTask_ = NULL;
}

KFMTaskList::~KFMTaskList(){
	delete tasks_;
}

////////////////////////////////////////////////////

KFMTaskList::Iterator KFMTaskList::taskIterator() const{
	return Iterator(*tasks_);
}


QString KFMTaskList::xmlTag() {
	return QString("tasklist");
}

QDomElement KFMTaskList::domElement(QDomDocument* dDoc) const{
	QDomElement el = dDoc->createElement(xmlTag());
	el.setAttribute("size", size());

	Iterator it = taskIterator();
	for (; it.current(); ++it) {
		el.appendChild( it.current()->domElement(dDoc) );
	}	
	
	return el;
}

void KFMTaskList::domElement(QDomElement eTaskList){
	// Disable sorting temporarily for efficiency.  The tasks
	// in the dom element are already sorted anyway.
	sorted_ = false;
	
	QDomNode n = eTaskList.firstChild();
	while (!n.isNull()) {
		QDomElement e = n.toElement();
		if (!e.isNull()) {
			if (e.tagName() == "task") {
				KFMTask* task = new KFMTask();
				task->domElement(e);
				appendTask(task);
			}
		}
		n = n.nextSibling();
	}
	
	sorted_ = true;
	sort();
	
	// Select the first task in the list to be current
	if (size() > 0) {		
		// Set the taskList's current position to 0
		currentPosition(0);
	}
}


int KFMTaskList::size() const{
	return tasks_->count();
}


void KFMTaskList::appendTask(KFMTask* task){
	currentTask(NULL);
	insertTask(task);
}


void KFMTaskList::removeTask(KFMTask* task){
	if (tasks_->findRef(task) >= 0) {
		
		if (currentTask() == task) {
			currentTask(NULL);
		}

				
		disconnect(task, 0, this, 0);
		tasks_->removeRef(task);
		
		// sort so that task positions are updated
		updatePositions();
	
		emit taskRemoved(task);
		emit contentsChanged();
	}
}


void KFMTaskList::insertTask(KFMTask* task){
	if (tasks_->findRef(task) < 0) {
		if (currentTask() == NULL) {
			tasks_->append(task);
		}
		else {
			tasks_->insert(tasks_->findRef(currentTask()), task);
		}

		// We can't just use inSort here because we want to
		// affect the new task's position if equal tasks exists
		// in the list.
		sort();
		
		connect(task, SIGNAL( contentsChanged() ), this, SLOT( onTaskContentsChanged() ));
		
		emit taskAdded(task);
		emit contentsChanged();
		
		
		currentTask(task);
	}
}

void KFMTaskList::moveTask(KFMTask* task, int pos){

	// Is the new position reasonable
	if (pos < 0 || pos >= size())
		return;

	// Does the task exist?		
	if (taskPosition(task) < 0)
		return;
		
	// Is it already in place?
	if (taskPosition(task) == pos)
		return;
		
	// Make sure positions labels are correct
	updatePositions();
		
	// Move the task to a new position in the list
	tasks_->take();
	tasks_->insert(pos, task);
	
	// Sort to update task positions
	sort();
	
	// Signal observers
	emit orderChanged();
	emit contentsChanged();
	
	
	currentTask(task);
}

void KFMTaskList::clearTasks(){
	currentTask(NULL);
	
	tasks_->clear();
	emit cleared();
}


int KFMTaskList::taskPosition(KFMTask* task) const{
	if (task == NULL)
		return -1;
	else
		return tasks_->findRef(task);
}



KFMTask* KFMTaskList::firstTask() const{
	return tasks_->first();
}


KFMTask* KFMTaskList::lastTask() const{
	return tasks_->last();
}

KFMTask* KFMTaskList::currentTask() const{
	return currentTask_;
}

void KFMTaskList::currentTask(KFMTask* task){
	if (task != NULL && tasks_->findRef(task) < 0)
		return;

	if (task != currentTask()) {
		if (currentTask() != NULL) {
			KFMTask* oldCurrentTask = currentTask();
			currentTask_ = NULL;
			
			// v--Clearing operations go here--v
			disconnect(oldCurrentTask, SIGNAL(endDateChanged()), this,
					SLOT(onCurrentTaskDateChanged()));
			disconnect(oldCurrentTask, SIGNAL(startDateChanged()), this,
					SLOT(onCurrentTaskDateChanged()));

		}
		
		currentTask_ = task;
		if (currentTask() != NULL) {
		
			// v--Setting operations go here--v
			connect(currentTask(), SIGNAL(startDateChanged()), this,
					SLOT(onCurrentTaskDateChanged()));
			connect(currentTask(), SIGNAL(endDateChanged()), this,
					SLOT(onCurrentTaskDateChanged()));
		}
			
		emit currentTaskChanged(task);
	}
}

KFMTask* KFMTaskList::nextTask() const{
	if (currentTask() == NULL)
		return NULL;

	tasks_->findRef(currentTask());
	return tasks_->next();
}


/** Retrieve the task following the specified task */
KFMTask* KFMTaskList::nextTask(const KFMTask* task) const{
	tasks_->findRef(task);
	return tasks_->next();
}


/** Retrieve the task preceding the specified task */
KFMTask* KFMTaskList::prevTask(const KFMTask* task) const{
	tasks_->findRef(task);
	return tasks_->prev();

}

int KFMTaskList::currentPosition() const{
	return tasks_->findRef(currentTask());
}

void KFMTaskList::currentPosition(int pos){
	if (pos < 0 || pos >= (int)tasks_->count())
		currentTask(NULL);
	else
		currentTask(tasks_->at(pos));
}


/** Determine if a task can move up in the ordering */
bool KFMTaskList::taskCanRise(const KFMTask* task) const{
	if (task == NULL)
		return false;
	else {
		if (prevTask(task) == NULL)
			return false;
		
		if (prevTask(task)->started() && !task->started())
			return false;
		
		if (prevTask(task)->endDate()->type() == KFMEndDate::Absolute) {
			if (task->endDate()->type() != KFMEndDate::Absolute)
				return false;
			else {
				if (prevTask(task)->endDate()->date() < task->endDate()->date())
					return false;
			}
		}
	}
	
	return TRUE;
}

/** Determine if a task can move down in the ordering */
bool KFMTaskList::taskCanFall(const KFMTask* task) const{
	if (task == NULL)
		return false;
	else {
		if (nextTask(task) == NULL)
			return false;
		
		if (task->started() && !nextTask(task)->started())
			return false;
		
		if (task->endDate()->type() == KFMEndDate::Absolute) {
			if (nextTask(task)->endDate()->type() == KFMEndDate::Absolute) {
				if (task->endDate()->date() < nextTask(task)->endDate()->date())
					return false;
			}
			else
				return false;
		}
	}
	
	return TRUE;
}


///////////////////////////////////////////////////////////
// Private

void KFMTaskList::sort(){
	if (sorted_) {
		// label tasks so the sort works correctly
		updatePositions();
		
		tasks_->sort();
	
		// label tasks again so they have the right positions
		updatePositions();
	}
}

/** Update the position attributes of the tasks */
void KFMTaskList::updatePositions(){
	// label tasks with their linear positions
	Iterator it = taskIterator();
	int i = 0;
	for (;it.current();++it,++i) {
		it.current()->position(i);
	}
}


void KFMTaskList::onTaskContentsChanged(){
	emit contentsChanged();
}


void KFMTaskList::onCurrentTaskDateChanged(){
	// Sort to reorder the tasks based on the new date
	sort();
	
	// Signal observers
	emit orderChanged();
	emit contentsChanged();
}
