#include <kmessagebox.h>
#include <klocale.h>
#include <tqpushbutton.h>
#include <klistview.h>
#include <kiconloader.h>
#include <klistviewsearchline.h>
#include <tqheader.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <ktoolbarbutton.h>
#include "../krusader.h"
#include "../krslots.h"
#include "popularurls.h"

#define STARTING_RANK	20
#define INCREASE			2
#define DECREASE			1

PopularUrls::PopularUrls(TQObject *parent, const char *name) : TQObject(parent, name), 
	head(0), tail(0), count(0) {
	dlg = new PopularUrlsDlg();
}

PopularUrls::~PopularUrls() {
	clearList();
	delete dlg;
}

void PopularUrls::clearList() {
	if (head) {
		UrlNodeP p=head, tmp;
		while (p) {
			tmp = p;
			p=p->next;
			delete tmp;
		}
	}
	ranks.clear();
	head = tail = 0;
}

void PopularUrls::save() {
	KConfigGroupSaver svr(krConfig, "Private");
	// prepare the string list containing urls and int list with ranks
	TQStringList urlList;
	TQValueList<int> rankList;
	UrlNodeP p = head;
	while (p) {
		urlList << p->url.prettyURL();
		rankList << p->rank;
		p = p->next;
	}
	krConfig->writeEntry("PopularUrls", urlList);
	krConfig->writeEntry("PopularUrlsRank", rankList);
}

void PopularUrls::load() {
	KConfigGroupSaver svr(krConfig, "Private");
	TQStringList urlList = krConfig->readListEntry("PopularUrls");
	TQValueList<int> rankList = krConfig->readIntListEntry("PopularUrlsRank");
	if (urlList.count() != rankList.count()) {
		KMessageBox::error(krApp, i18n("Saved 'Popular Urls' are invalid. List will be cleared"));
		return;
	}
	clearList();
	count = 0;
	// iterate through both lists and
	TQStringList::Iterator uit;
	TQValueList<int>::Iterator rit;
	for (uit=urlList.begin(), rit=rankList.begin(); uit!=urlList.end() && rit!=rankList.end(); ++uit, ++rit) {
		UrlNodeP node = new UrlNode;
		node->url = KURL::fromPathOrURL( *uit );
		node->rank = *rit;
		appendNode(node);
		ranks.insert(*uit, node);
	}
}


// returns a url list with the 'max' top popular urls 
KURL::List PopularUrls::getMostPopularUrls(int max) {
	// get at most 'max' urls
	KURL::List list;
	UrlNodeP p = head;
	int tmp = 0;
	if (maxUrls < max) max = maxUrls; // don't give more than maxUrls
	while (p && tmp < max) {
		list << p->url;
		p = p->next;
		++tmp;
	}
	
	return list;
}

// adds a url to the list, or increase rank of an existing url, making
// sure to bump it up the list if needed
void PopularUrls::addUrl(const KURL& url) {
	KURL tmpurl = url;
	tmpurl.adjustPath(1); // make a uniform trailing slash policy
	UrlNodeP pnode;
	
	decreaseRanks();
	if (!head) { // if the list is empty ... (assumes dict to be empty as well)
		pnode = new UrlNode;
		pnode->rank = STARTING_RANK;
		pnode->url = tmpurl;
		appendNode(pnode);
		ranks.insert(tmpurl.url(), head);
	} else {
		pnode = ranks.find(tmpurl.url());
		if (!pnode) { // is the added url new? if so, append it
			pnode = new UrlNode;
			pnode->rank = STARTING_RANK;
			pnode->url = tmpurl;
			appendNode(pnode);
			ranks.insert(tmpurl.url(), pnode);
		} else {
			pnode->rank += INCREASE;
		}
	}
	
	// do we need to change location for this one?
	relocateIfNeeded(pnode);

	// too many urls?
	if (count > maxUrls) removeNode(tail);
	
	//dumpList();
}

// checks if 'node' needs to be bumped-up the ranking list and does it if needed
void PopularUrls::relocateIfNeeded(UrlNodeP node) {
	if (node->prev && (node->prev->rank < node->rank)) {
		// iterate until we find the correct place to put it
		UrlNodeP tmp = node->prev->prev;
		while (tmp) {
			if (tmp->rank >= node->rank)
				break; // found it!
			else tmp = tmp->prev;
		}
		// now, if tmp isn't null, we need to move node to tmp->next
		// else move it to become head
		removeNode(node);
		insertNode(node, tmp);
	}
}
	
	
// iterate over the list, decreasing each url's rank
// this is very naive, but a 1..30 for loop is acceptable (i hope)
void PopularUrls::decreaseRanks() {
	if (head) {
		UrlNodeP p=head;
		while (p) {
			if (p->rank-DECREASE>=0)
				p->rank -= DECREASE;
			else p->rank = 0;
			p=p->next;
		}
	}
}

// removes a node from the list, but doesn't free memory!
// note: this will be buggy in case the list becomes empty (which should never happen)
void PopularUrls::removeNode(UrlNodeP node) {
	if (node->prev) {
		if (tail == node) tail = node->prev;
		node->prev->next = node->next;
	}
	if (node->next) {
		if (head == node) head = node->next;
		node->next->prev = node->prev;
	}
	--count;
}

void PopularUrls::insertNode(UrlNodeP node, UrlNodeP after) {
	if (!after) { // make node head
		node->next = head;
		node->prev = 0;
		head->prev = node;
		head = node;
	} else {
		if (tail == after) tail = node;
		node->prev = after;
		node->next = after->next;
		if( node->next ) {
			after->next->prev = node;
			after->next = node;
		}
	}
	++count;
}

// appends 'node' to the end of the list, collecting garbage if needed
void PopularUrls::appendNode(UrlNodeP node) {
	if (!tail) { // creating the first element
		head = tail = node;
		node->prev = node->next = 0;
	} else {
		node->next = 0;
		node->prev = tail;
		tail->next = node;
		tail = node;
	}
	++count;
}

void PopularUrls::dumpList() {
	UrlNodeP p = head;
	printf("====start %d====\n",count);
	while (p) {
		printf("%d : %s\n", p->rank, p->url.url().latin1());
		p = p->next;
	}
	fflush(stdout);
}

void PopularUrls::showDialog() {
	KURL::List list = getMostPopularUrls(maxUrls);
	dlg->run(list);
	if (dlg->result() == -1) return;
	SLOTS->refresh(list[dlg->result()]);
	//printf("running %s\n", list[dlg->result()].url().latin1());fflush(stdout);
}

// ===================================== PopularUrlsDlg ======================================
PopularUrlsDlg::PopularUrlsDlg(): 
	KDialogBase(Plain, i18n("Popular Urls"), Close, KDialogBase::NoDefault, krApp) {
	TQGridLayout *layout = new TQGridLayout( plainPage(), 0, KDialog::spacingHint() );
	
	// listview to contain the urls
	urls = new KListView(plainPage());
	urls->header()->hide();
	urls->addColumn("");
	urls->setSorting(-1);
	urls->setVScrollBarMode(TQScrollView::AlwaysOn);
	
	// quick search
	TQToolButton *btn = new TQToolButton(plainPage());
	btn->setIconSet(SmallIcon("locationbar_erase"));
	search = new KListViewSearchLine(plainPage(), urls);
	search->setTrapReturnKey(true);
	TQLabel *lbl = new TQLabel(search, i18n(" &Search: "), plainPage());

	layout->addWidget(btn,0,0);
	layout->addWidget(lbl,0,1);
	layout->addWidget(search,0,2);
	layout->addMultiCellWidget(urls,1,1,0,2);
	setMaximumSize(600, 500);
	
	setTabOrder(search, urls);
	setTabOrder(urls, actionButton(Close));
	
	connect(urls, TQT_SIGNAL(executed(TQListViewItem*)), 
		this, TQT_SLOT(slotItemSelected(TQListViewItem*)));
	connect(urls, TQT_SIGNAL(returnPressed(TQListViewItem*)), 
		this, TQT_SLOT(slotItemSelected(TQListViewItem*)));		
	connect(btn, TQT_SIGNAL(clicked()), search, TQT_SLOT(clear()));
	connect(search, TQT_SIGNAL(returnPressed(const TQString&)), 
		this, TQT_SLOT(slotSearchReturnPressed(const TQString&)));
}

void PopularUrlsDlg::slotItemSelected(TQListViewItem *it) {
	selection = urls->itemIndex(it);
	accept();
}

void PopularUrlsDlg::slotSearchReturnPressed(const TQString&) {
	urls->setFocus();
	// select the first visible item
	TQListViewItemIterator it( urls );
   while ( it.current() ) {
		if ( it.current()->isVisible() ) {
			urls->setSelected(it.current(), true);
			urls->setCurrentItem(it.current());
			break;
		} else ++it;
	} 
}

PopularUrlsDlg::~PopularUrlsDlg() {
	delete search;
	delete urls;
}

void PopularUrlsDlg::run(KURL::List list) {
	// populate the listview
	urls->clear();
	KURL::List::Iterator it;
	for (it = list.begin(); it!=list.end(); ++it) {
		KListViewItem *item = new KListViewItem(urls, urls->lastItem());
		item->setText(0, (*it).isLocalFile() ? (*it).path() : (*it).prettyURL());
		item->setPixmap(0, (*it).isLocalFile() ? SmallIcon("folder") : SmallIcon("folder_html"));
	}
	//urls->setCurrentItem(urls->firstChild());
	//urls->setSelected(urls->firstChild(), true);
	setMinimumSize(urls->sizeHint().width()+45, 400);
	
	search->clear();
	search->setFocus();
	selection = -1;
	exec();
}

#include "popularurls.moc"
