/*
Gwenview - A simple image viewer for TDE
Copyright 2000-2004 Aur�ien G�eau

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; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/
#include "treeview.moc"

// TQt
#include <tqheader.h>
#include <tqtimer.h>

// KDE
#include <kdebug.h>
#include <kiconloader.h>
#include <kmimetype.h>
#include <kurldrag.h>

// Local
#include "../gvcore/fileoperation.h"

namespace Gwenview {

#undef ENABLE_LOG
#undef LOG
//#define ENABLE_LOG
#ifdef ENABLE_LOG
#define LOG(x) kdDebug() << k_funcinfo << x << endl
#else
#define LOG(x) ;
#endif

const int AUTO_OPEN_DELAY=1000;
const int DND_ICON_COUNT=8;
const char* DND_PREFIX="dnd";


struct TreeView::Private {
	TreeView* mView;
	KFileTreeBranch* mBranch;
	KFileTreeViewItem* mDropTarget;
	TQTimer* mAutoOpenTimer;

	KFileTreeViewItem* findViewItem(KFileTreeViewItem* parent,const TQString& text) {
		TQListViewItem* item;

		for (item=parent->firstChild();item;item=item->nextSibling()) {
			if (item->text(0)==text) {
				return static_cast<KFileTreeViewItem*>(item);
			}
		}
		return 0L;
	}
	
	void setURLInternal(const KURL& url) {
		LOG(url.prettyURL() );
		TQString path=url.path();
		
		if (!mBranch || !mBranch->rootUrl().isParentOf(url)) {
			mView->createBranch(url);
			return;
		}

		// The request URL is a child of the branch, expand the branch to reach
		// the child
		LOG("Expanding to reach child");
		if(mBranch->rootUrl().path()!="/") {
			path.remove(0,mBranch->rootUrl().path().length());
		}
		LOG("Path=" << path);

		// Finds the deepest existing view item
		TQStringList folderParts=TQStringList::split('/',path);
		TQStringList::Iterator folderIter=folderParts.begin();
		TQStringList::Iterator endFolderIter=folderParts.end();
		KFileTreeViewItem* viewItem=mBranch->root();
		for(;folderIter!=endFolderIter;++folderIter) {

			KFileTreeViewItem* nextViewItem=findViewItem(viewItem,*folderIter);
			if (!nextViewItem) break;
			viewItem=nextViewItem;
		}
		LOG("Deepest existing view item=" << viewItem->url());

		// If this is the wanted item, select it,
		// otherwise set the url as the next to select
		if (viewItem->url().equals(url,true)) {
			LOG("We are done");
			mView->setCurrentItem(viewItem);
			mView->ensureItemVisible(viewItem);
			mView->slotSetNextUrlToSelect(KURL());
		} else {
			LOG("We continue");
			mView->slotSetNextUrlToSelect(url);
		}
		
		LOG("Opening deepest existing view item");
		viewItem->setOpen(true);
	}
};


TreeView::TreeView(TQWidget* parent)
: KFileTreeView(parent) {
	d=new Private;
	d->mView=this;
	d->mBranch=0;
	d->mDropTarget=0;
	d->mAutoOpenTimer=new TQTimer(this);
	
	// Look
	addColumn(TQString());
	header()->hide();
	setAllColumnsShowFocus(true);
	setRootIsDecorated(false);
	setFullWidth(true);
	
	// Drag'n'drop
	setDragEnabled(true);
	setDropVisualizer(false);
	setDropHighlighter(true);
	setAcceptDrops(true);

	connect(d->mAutoOpenTimer, TQ_SIGNAL(timeout()),
		this, TQ_SLOT(autoOpenDropTarget()));
}


TreeView::~TreeView() {
	delete d;
}


void TreeView::setURL(const KURL& url) {
	LOG(url.prettyURL());
	if (currentURL().equals(url,true)) return;
	if (m_nextUrlToSelect.equals(url,true)) return;
	slotSetNextUrlToSelect(url);
	
	// Do not update the view if it's hidden, the url has been stored with
	// slotSetNextUrlToSelect. The view will expand to it next time it's shown.
	if (!isVisible()) {
		LOG("We are hidden, just store the url");
		return;
	}

	d->setURLInternal(url);
}


void TreeView::slotTreeViewPopulateFinished(KFileTreeViewItem* item) {
	TQListViewItem* child;
	if (!item) return;
	KURL url=item->url();

	if (d->mDropTarget) {
		startAnimation(d->mDropTarget,DND_PREFIX,DND_ICON_COUNT);
	}

	LOG("itemURL=" << url);
	LOG("m_nextUrlToSelect=" << m_nextUrlToSelect);

	// We reached the URL to select, get out
	if (url.equals(m_nextUrlToSelect, true)) {
		slotSetNextUrlToSelect(KURL());
		return;
	}

	// This URL is not a parent of a wanted URL, get out
	if (!url.isParentOf(m_nextUrlToSelect)) return;

	// Find the next child item and open it
	LOG("Looking for next child");
	for (child=item->firstChild(); child; child=child->nextSibling()) {
		url=static_cast<KFileTreeViewItem*>(child)->url();
		if (url.isParentOf(m_nextUrlToSelect)) {
			LOG("Opening child with URL=" << url);
			ensureItemVisible(child);
			child->setOpen(true);
			return;
		}
	}
}


void TreeView::createBranch(const KURL& url) {
	if (d->mBranch) {
		removeBranch(d->mBranch);
	}
	TQString title=url.prettyURL(0, KURL::StripFileProtocol);
	d->mBranch=addBranch(url, title, SmallIcon(KMimeType::iconForURL(url)) );
	setDirOnlyMode(d->mBranch, true);
	d->mBranch->setChildRecurse(false);
	d->mBranch->root()->setOpen(true);

	connect(d->mBranch, TQ_SIGNAL(populateFinished(KFileTreeViewItem*) ),
		this, TQ_SLOT(slotTreeViewPopulateFinished(KFileTreeViewItem*)) );
}
	


/**
 * Override this method to make sure that the item is selected, opened and
 * visible
 */
void TreeView::slotNewTreeViewItems(KFileTreeBranch* branch, const KFileTreeViewItemList& itemList) {
	if( ! branch ) return;
	LOG("");
	if(m_nextUrlToSelect.isEmpty()) return;

	KFileTreeViewItemListIterator it( itemList );

	for(;it.current(); ++it ) {
		KURL url = (*it)->url();

		// This is an URL to select
		// (We block signals to avoid simulating a click on the dir item)
		if (m_nextUrlToSelect.equals(url,true)) {
			blockSignals(true);
			setCurrentItem(*it);
			blockSignals(false);

			ensureItemVisible(*it);
			(*it)->setOpen(true);
			m_nextUrlToSelect = KURL();
			return;
		}
	}
}


/**
 * Override showEvent to make sure the view shows the correct
 * dir when it's shown. Since the view doesn't update if it's
 * hidden
 */
void TreeView::showEvent(TQShowEvent* event) {
	LOG("m_nextUrlToSelect=" << m_nextUrlToSelect.pathOrURL());
	if (m_nextUrlToSelect.isValid() && !currentURL().equals(m_nextUrlToSelect,true)) {
		d->setURLInternal(m_nextUrlToSelect);
	}
	TQWidget::showEvent(event);
}


void TreeView::contentsDragMoveEvent(TQDragMoveEvent* event) {
	if (!KURLDrag::canDecode(event)) {
		event->ignore();
		return;
	}

	// Get a pointer to the new drop item
	TQPoint point(0,event->pos().y());
	KFileTreeViewItem* newDropTarget=static_cast<KFileTreeViewItem*>( itemAt(contentsToViewport(point)) );
	if (!newDropTarget) {
		event->ignore();
		d->mAutoOpenTimer->stop();
		if (d->mDropTarget) {
			stopAnimation(d->mDropTarget);
			d->mDropTarget=0L;
		}
		return;
	}

	event->accept();
	if (newDropTarget==d->mDropTarget) return;
	if (d->mDropTarget) {
		stopAnimation(d->mDropTarget);
	}

	// Restart auto open timer if we are over a new item
	d->mAutoOpenTimer->stop();
	d->mDropTarget=newDropTarget;
	startAnimation(newDropTarget,DND_PREFIX,DND_ICON_COUNT);
	d->mAutoOpenTimer->start(AUTO_OPEN_DELAY,true);
}


void TreeView::contentsDragLeaveEvent(TQDragLeaveEvent*) {
	d->mAutoOpenTimer->stop();
	if (d->mDropTarget) {
		stopAnimation(d->mDropTarget);
		d->mDropTarget=0L;
	}
}


void TreeView::contentsDropEvent(TQDropEvent* event) {
	d->mAutoOpenTimer->stop();

	// Get data from drop (do it before showing menu to avoid mDropTarget changes)
	if (!d->mDropTarget) return;
	KURL dest=d->mDropTarget->url();

	KURL::List urls;
	if (!KURLDrag::decode(event,urls)) return;

	// Show popup
	bool wasMoved;
	FileOperation::openDropURLMenu(this, urls, dest, &wasMoved);

	if (wasMoved) {
		// If the current url was in the list, set the drop target as the new
		// current item
		KURL current=currentURL();
		KURL::List::ConstIterator it=urls.begin();
		for (; it!=urls.end(); ++it) {
			if (current.equals(*it,true)) {
				setCurrentItem(d->mDropTarget);
				break;
			}
		}
	}

	// Reset drop target
	if (d->mDropTarget) {
		stopAnimation(d->mDropTarget);
		d->mDropTarget=0L;
	}
}


void TreeView::autoOpenDropTarget() {
	if (d->mDropTarget) {
		d->mDropTarget->setOpen(true);
	}
}
} // namespace
