#include <config.h>
#include <iostream.h>
#include <qmessagebox.h>
#include <strings.h>
#include "dirview.h"

DirViewItem::DirViewItem(DirViewItem *parent, const char *label, const char *addr, QPixmap *icon, QPixmap *iconSel, bool hasChilds, int type)
	: Label(label), Address(addr), ParentItem(parent), Next(0), Opened(FALSE), Icon(icon), IconSel(iconSel), ChildsScanned(FALSE), HasChilds(hasChilds), Type(type)
{
	Childs = new DirViewItemList;
	Childs->setAutoDelete(TRUE);
	if (ParentItem) {
		Depth = ParentItem->Depth + 1;
		ParentDirView = ParentItem->ParentDirView;
		ParentItem->addChildItem(this);
	}
	else {
		Depth = 0;
		ParentDirView = 0;
	}
	if (ParentDirView) ParentDirView->addItem(this);
	Index = 0;
	Sorting = FALSE;
}

DirViewItem::~DirViewItem()
{ if (ParentDirView) ParentDirView->removeItem(this); delete Childs;}

void DirViewItem::clear()
{ Childs->clear();}

void DirViewItem::addChildItem(DirViewItem *item)
{
	if (Sorting && Childs->count() > 0) {
		Childs->inSort(item);
		int	pos = Childs->findRef(item);
		if (pos > 0) Childs->at(pos-1)->Next = item;
		if (pos < (int)(Childs->count())-1) item->Next = Childs->at(pos+1);
		else item->Next = 0;
	}
	else {
		DirViewItem	*last = Childs->last();
		if (last) last->Next = item;
		item->Next = 0;
		Childs->append(item);
	}
}

DirViewItem* DirViewItem::getFirstParent()	// on depth = 1
{
	if (Depth < 1) return 0;
	DirViewItem	*current = this;
	while (current->ParentItem->ParentItem) current = current->ParentItem;
	return current;
}

QList<DirViewItem> DirViewItem::getParentList()
{
	QList<DirViewItem>	list;
	list.setAutoDelete(FALSE);
	DirViewItem	*current = this;
	while (current) {
		list.insert(0,current);
		current = current->ParentItem;
	}
	return list;
}

DirViewItem* DirViewItem::findChild(const char *name)
{
	if (Childs->count() < 1) return 0;
	DirViewItem	*item = Childs->first();
	while (item) {
		if (item->Label == name) break;
		item = Childs->next();
	}
	return item;
}

//-------------------------------------------------------------------------------------------------------

DirView::DirView(QWidget *parent, const char *name)
	: QTableView(parent,name), QDropSite(this)
{
	setTableFlags(Tbl_autoVScrollBar | Tbl_smoothScrolling | Tbl_clipCellPainting);
	setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
//	setLineWidth(2);
	setBackgroundColor(colorGroup().base());
	setNumCols(1);
	setNumRows(1);
	setCellHeight(ICON_HEIGHT);
	setCellWidth(100);
	LockPixmap = MountPixmap = LinkPixmap = 0;
	Combo = 0;
	Root = new DirViewItem(0,"Desktop",0);
	Root->ParentDirView = this;
	Root->Opened = TRUE;
	Root->ChildsScanned = TRUE;
	CurrentItem = Root;
	PopupItem = 0;
	ViewList.setAutoDelete(FALSE);
	ItemList.setAutoDelete(FALSE);
	ItemList.append(Root);
	updateViewList();
}

DirView::~DirView()
{
	delete Root;
}

void DirView::setRootIcon(QPixmap *icon)
{ Root->Icon = Root->IconSel = icon;}

void DirView::addItem(DirViewItem *parent, const char *name, const char *addr, QPixmap *icon, QPixmap *iconSel, bool sorting, bool hasChilds, int type, bool updateF)
{
	DirViewItem	*item, *Parent = (parent ? parent : Root);
	item = new DirViewItem(Parent,name,addr,icon,iconSel,hasChilds,type);
	item->Sorting = sorting;
	if (updateF) {
		emit updateHasChilds(Parent);
		if (Parent->Opened) {
			updateViewList();
			update();
		}
	}
	else updateCell(Parent);
}

void DirView::addItem(const char *path, const char *name, const char *addr, QPixmap *icon, QPixmap *iconSel, bool sorting, bool hasChilds, int type, bool updateF)
{
	DirViewItem	*Parent = findItem(path);
	if (Parent == 0) return;
	DirViewItem	*item;
	item = new DirViewItem(Parent,name,addr,icon,iconSel,hasChilds,type);
	item->Sorting = sorting;
	if (updateF) emit updateHasChilds(Parent);
	if (Parent->Opened) {
		updateViewList();
		update();
	}
	else updateCell(Parent);
}

void DirView::removeItem(DirViewItem *parent, const char *name)
{
	DirViewItem	*item = parent->findChild(name);
	bool	needUpdate = FALSE, needChangeSelection = FALSE;
	if (item == 0) return;
	int	pos = parent->Childs->findRef(item);
	if (pos > 0) parent->Childs->at(pos-1)->Next = item->Next;
	if (parent->Opened) needUpdate = TRUE;
	DirViewItem	*next = item;
	while (next->Next == 0 && next->ParentItem) next = next->ParentItem;
	next = next->Next;
	if (next != 0) { if (CurrentItem->Index >= item->Index && CurrentItem->Index < next->Index) needChangeSelection = TRUE;}
	else if (CurrentItem->Index >= item->Index) needChangeSelection = TRUE;
	parent->Childs->removeRef(item);
	emit updateHasChilds(parent);
	needUpdate = TRUE;
	if (needUpdate) {
		updateViewList();
		update();
	}
	if (needChangeSelection) {
		changeSelection(Root);
		emit selectionChanged(CurrentItem);
	}
}

void DirView::removeItem(const char *path, const char *name)
{
	DirViewItem	*Parent = findItem(path);
	if (Parent != 0) removeItem(Parent,name);
}

void DirView::removeItem(DirViewItem *item)
{ ItemList.removeRef(item);}

void DirView::addItem(DirViewItem *item)
{ ItemList.append(item);}

void DirView::removeItem(const char *url)
{
	DirViewItem	*item;
	while ((item = findItemFromURL(url)) != 0) {
cout << "removing item " << item->Address << endl;
		removeItem(item->ParentItem,item->Label.data());
	}
}

void DirView::renameItem(const char *urlsrc, const char *urldest)
{
	DirViewItem	*item;
	QString		dest(urldest);
	if (dest[dest.length()-1] == '/') dest.truncate(dest.length()-1);
	QString		name(dest.right(dest.length()-dest.findRev('/')-1));
	while ((item = findItemFromURL(urlsrc)) != 0) {
		DirViewItem	*parent = item->ParentItem;
		parent->Childs->setAutoDelete(FALSE);
		removeItem(parent,item->Label.data());
		item->Label = name.data();
		item->Address = urldest;
		parent->addChildItem(item);
		parent->Childs->setAutoDelete(TRUE);
	}
	updateViewList();
	update();
}

void DirView::removePointFiles()
{
	setUpdatesEnabled(FALSE);
	bool	done = FALSE;
	while (!done) {
		done = TRUE;
		QListIterator<DirViewItem>	it(ItemList);
		for (;it.current();++it)
			if (it.current()->Label[0] == '.') {
				done = FALSE;
				removeItem(it.current()->ParentItem,it.current()->Label.data());
			}
	}
	setUpdatesEnabled(TRUE);
	repaint();
}

void DirView::removeArchiveFiles(SubProtList *list)
{
	setUpdatesEnabled(FALSE);
	bool	done = FALSE;
	while (!done) {
		done = TRUE;
		int	pos;
		QListIterator<DirViewItem>	it(ItemList);
		for (;it.current();++it)
			if ((pos=it.current()->Address.findRev('#')) != -1 && pos == it.current()->Address.length()-5
			    && (list == 0 || matchFileName(it.current()->Label.data(),list) == 0)) {
				done = FALSE;
				removeItem(it.current()->ParentItem,it.current()->Label.data());
			}
	}
	setUpdatesEnabled(TRUE);
	repaint();
}

DirViewItem* DirView::findItem(const char *path)
{
	QString	tmp(path);
	char	*c = strtok(tmp.data(),"/");
	if (c) c = strtok(0,"/");
	else return 0;
	DirViewItem	*item = Root;
	while (c && item) {
		item = item->findChild(c);
		c = strtok(0,"/");
	}
	return item;
}

DirViewItem* DirView::findItemFromURL(const char *url)
{
	QListIterator<DirViewItem>	it(ItemList);
	for (;it.current();++it) if (it.current()->Address == url) return it.current();
	return 0;
}

void DirView::paintCell(QPainter *p, int row, int col)
{
	DirViewItem	*current = ViewList.at(row);
	QFontMetrics	fm(p->fontMetrics());
	int	w = current->Depth*ICON_HEIGHT;
	QPen	normal(foregroundColor(),0,SolidLine), dashed(foregroundColor(),0,DotLine);
	p->setPen(normal);
	p->drawText(w+ICON_HEIGHT+4,0,fm.width(current->Label.data()),ICON_HEIGHT,AlignLeft | AlignVCenter,current->Label.data());
	if (current == CurrentItem) {
		if (current->IconSel) p->drawPixmap(w+1,1,*(current->IconSel));
		p->drawRect(w+ICON_HEIGHT,1,fm.width(current->Label.data())+8,ICON_HEIGHT-2);
	}
	else if (current->Icon) p->drawPixmap(w+1,1,*(current->Icon));
	switch(current->Type) {
	   case DirNormal: break;
	   case DirLink: if (LinkPixmap) p->drawPixmap(w+1,1,*LinkPixmap);
		      	 break;
	   case DirMount: if (MountPixmap) p->drawPixmap(w,ICON_HEIGHT-MountPixmap->height(),*MountPixmap);
		          break;
	   case DirLock: if (LockPixmap) p->drawPixmap(w,ICON_HEIGHT-LockPixmap->height(),*LockPixmap);
		         break;
	   default: break;
	}
	if (current->ParentItem) {
		p->setPen(dashed);
		p->drawLine(w-ICON_HEIGHT/2,ICON_HEIGHT/2,w,ICON_HEIGHT/2);
		if (current->Next) p->drawLine(w-ICON_HEIGHT/2,0,w-ICON_HEIGHT/2,ICON_HEIGHT);
		else p->drawLine(w-ICON_HEIGHT/2,0,w-ICON_HEIGHT/2,ICON_HEIGHT/2);
		DirViewItem	*prev = current->ParentItem;
		while (prev != Root) {
			if (prev->Next) p->drawLine(prev->Depth*ICON_HEIGHT-ICON_HEIGHT/2,0,prev->Depth*ICON_HEIGHT-ICON_HEIGHT/2,ICON_HEIGHT);
			prev = prev->ParentItem;
		}
		p->setPen(normal);
		if (current->HasChilds || current->Childs->count() > 0 || current->Depth == 2) {
			p->drawRect(w-ICON_HEIGHT/2-4,ICON_HEIGHT/2-4,9,9);
			p->fillRect(w-ICON_HEIGHT/2-3,ICON_HEIGHT/2-3,7,7,backgroundColor());
			p->drawLine(w-ICON_HEIGHT/2-2,ICON_HEIGHT/2,w-ICON_HEIGHT/2+2,ICON_HEIGHT/2);
			if (!(current->Opened))
				p->drawLine(w-ICON_HEIGHT/2,ICON_HEIGHT/2-2,w-ICON_HEIGHT/2,ICON_HEIGHT/2+2);
		}
	}
}

void DirView::updateViewList()
{
	ViewList.clear();
	DirViewItem	*current = Root->Childs->first();
	int	index = 1;
	ViewList.append(Root);
	if (Root->Opened) {
		while (current) {
			ViewList.append(current);
			current->Index = index;
			if (current->Opened && current->Childs->first()) {
				current = current->Childs->first();
			}
			else if (current->Next == 0) {
				while (current->Depth > 0 && current->Next == 0) {
					current = current->ParentItem;
				}
				current = current->Next;
			}
			else current = current->Next;
			index++;
		}
	}
	setNumRows(ViewList.count());
//	scrollInView();
}

void DirView::resizeEvent(QResizeEvent *)
{
	setCellWidth(viewWidth());
	QTableView::resizeEvent(0);
}

void DirView::mousePressEvent(QMouseEvent *e)
{
	int	row = findRow(e->y());
	if (row < 0) return;
	DirViewItem	*current = ViewList.at(row);
	if (e->button() == LeftButton) {
		if (e->x() > current->Depth*cellHeight()) {
			if (CurrentItem != current) {
				changeSelection(current);
				emit selectionChanged(CurrentItem);
			}
		}
		else if (expandRect(current).contains(e->pos())) {
			if (current->Opened) collapseItem(current);
			else expandItem(current);
		}
	}
	else if (e->button() == RightButton && e->x() > current->Depth*cellHeight()) {
		PopupItem = current;
		emit rightButtonPressed(current);
	}
}

void DirView::mouseDoubleClickEvent(QMouseEvent *e)
{
	if (e->button() == LeftButton) {
		int	row = findRow(e->y());
		if (row < 0) return;
		DirViewItem	*current = ViewList.at(row);
		if (current->Opened) collapseItem(current);
		else expandItem(current);
	}
}

void DirView::changeSelection(DirViewItem *newItem)
{
	if (newItem == CurrentItem) return;
	DirViewItem	*old = CurrentItem;
	CurrentItem = newItem;
	QTableView::updateCell(old->Index,0);
	QTableView::updateCell(CurrentItem->Index,0);
	if (Combo) updateCombo();
	scrollInView();
}

QRect DirView::expandRect(DirViewItem *item)
{       if (item->HasChilds || item->Childs->count() > 0 || item->Depth <= 2)
		return QRect(item->Depth*ICON_HEIGHT-xOffset()-lineWidth()-ICON_HEIGHT/2-2,item->Index*ICON_HEIGHT-yOffset()-lineWidth()+ICON_HEIGHT/2-2,9,9);
	else return QRect(0,0,0,0);
}

void DirView::expandItem(DirViewItem *item, bool redraw)
{
	if (!item->Opened) {
		if (!(item->ChildsScanned)) {
			emit requestItemChilds(item);
item->ChildsScanned = TRUE;
			if (!item->ChildsScanned) return;
		}
		item->Opened = TRUE;
		if (redraw) {
			updateViewList();
			update();
		}
	}
}

void DirView::collapseItem(DirViewItem *item, bool redraw)
{
	if (item->Opened) {
		item->Opened = FALSE;
		DirViewItem	*next = item;
		bool		needEmit = FALSE;
		while (next && next->Next == 0) next = next->ParentItem;
		if (next) next = next->Next;
		if ((CurrentItem && next && CurrentItem->Index > item->Index && CurrentItem->Index < next->Index) ||
		    (CurrentItem && next == 0 && CurrentItem->Index > item->Index)) {
			changeSelection(item);
			needEmit = TRUE;
		}
		if (redraw) {
			updateViewList();
			update();
		}
		if (needEmit) emit selectionChanged(item);
	}
}

void DirView::updateCombo()
{
	int	index = 0;
	Combo->clear();
	Combo->insertItem("Desktop",Root->Icon,0);
	if (CurrentItem != Root && Root->Childs->count() > 0) {
		DirViewItem	*item = CurrentItem->getFirstParent();
		DirViewItemListIterator		it(*(Root->Childs));
		for (; it.current() && it.current() != item; ++it)
			Combo->insertItem(it.current()->Label.data(),it.current()->Icon,1);
		if (item) {
			Combo->insertItem(item->Label.data(),item->Icon,1);
			QList<DirViewItem>	list;
			list.setAutoDelete(FALSE);
			DirViewItem	*tmp = CurrentItem;
			while (tmp) {
				list.insert(0,tmp);
				tmp = tmp->ParentItem;
			}
			QListIterator<DirViewItem>	it2(list);
			for (int i=0;it2.current() && i<2;++it2,i++) ;
			for (int i=2;it2.current();++it2,i++) Combo->insertItem(it2.current()->Label.data(),it2.current()->Icon,i);
		}
		index = Combo->count()-1;
		for (++it; it.current(); ++it)
			Combo->insertItem(it.current()->Label.data(),it.current()->Icon,1);
	}
	Combo->setCurrentItem(index);
}

void DirView::cdUp()
{
	if (CurrentItem != Root) {
		changeSelection(CurrentItem->ParentItem);
		emit selectionChanged(CurrentItem);
	}
}

#include <kapp.h>
bool DirView::cdDown(const char *dir)
{
	bool	Scanned = CurrentItem->ChildsScanned, Rescanned = FALSE;
	DirViewItem	*item;
	while (1) {
		if (Scanned) {
			item = CurrentItem->Childs->first();
			while (item && item->Label != dir) item = CurrentItem->Childs->next();
			if (item) break;
			else if (Rescanned) {
				QMessageBox::critical(this,"Error",i18n("Can't find directory"),
						      QMessageBox::Ok | QMessageBox::Default,0);
				break;
			}
			else {
				CurrentItem->Childs->clear();
				emit requestItemChilds(CurrentItem);
				Rescanned = TRUE;
				CurrentItem->Opened = FALSE;
			}
		}
		else {
			emit requestItemChilds(CurrentItem);
			Scanned = Rescanned = TRUE;
		}
	}
	if (item) {
		if (!CurrentItem->Opened) {
			CurrentItem->Opened = TRUE;
			CurrentItem = item;
			updateViewList();
			update();
			if (Combo) updateCombo();
		}
		else changeSelection(item);
		if (isUpdatesEnabled()) emit selectionChanged(item);
		return TRUE;
	}
	else return FALSE;
}

void DirView::goToDir(const char *path)
{
	setUpdatesEnabled(FALSE);
	QString		Path(path);
	if (Path[Path.length()-1] != '/') Path += '/';
	char	*last, *c = strtok_r(Path.data(),"/",&last);
	changeSelection(Root);
	c = strtok_r(0,"/",&last);
	while (c) {
		if (!cdDown(c)) break;
		c = strtok_r(0,"/",&last);
	}
	setUpdatesEnabled(TRUE);
	scrollInView();
	update();
	emit selectionChanged(CurrentItem);
}

void DirView::comboChanged(int index)
{
	if (index == 0) {
		changeSelection(Root);
		emit selectionChanged(Root);
		return;
	}
	DirViewItem	*item = CurrentItem;
	while (item->Depth > 0 && item->Label != Combo->text(index)) item = item->ParentItem;
	if (item->Label != Combo->text(index)) {
		item = Root->Childs->first();
		while (item && item->Label != Combo->text(index)) item = Root->Childs->next();
	}
	if (item) {
		changeSelection(item);
		emit selectionChanged(item);
	}
}

void DirView::dropEvent(QDropEvent *e)
{
	int	row = findRow(e->pos().y());
	if (row < 0) return;
	emit dropAccepted(e,ViewList.at(row));
}

void DirView::refresh()
{
	repaint(TRUE);
	if (Combo) Combo->repaint(TRUE);
}

void DirView::clearItem(DirViewItem *item)
{
	while (item->Childs->first()) removeItem(item,item->Childs->first()->Label.data());
	collapseItem(item);
	if (CurrentItem == item) {
		changeSelection(Root);
		emit selectionChanged(CurrentItem);
	}
}

void DirView::rescanChilds(DirViewItem *item)
{
	if (item->Depth < 2) return;
	setUpdatesEnabled(FALSE);
	item->Childs->clear();
	updateViewList();
	emit requestItemChilds(item);
	item->Opened = TRUE;
	updateViewList();
	setUpdatesEnabled(TRUE);
	repaint();
}

void DirView::refreshItem(DirViewItem *item)
{
	emit updateHasChilds(item);
}

void DirView::refreshItem(const char *url)
{
	QListIterator<DirViewItem>	it(ItemList);
	for (;it.current();++it) if (it.current()->Address == url) refreshItem(it.current());
}

void DirView::updateCell(DirViewItem *item, bool redraw)
{ QTableView::updateCell(item->Index,0,redraw);}
