/*
 * history.cc
 *
 * Copyright (c) 2002, 2003 Frerich Raabe <raabe@kde.org>
 *
 * 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. For licensing and distribution details, check the
 * accompanying file 'COPYING'.
 */
#include "history.h"

#include <kaction.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kmainwindow.h>
#include <kpopupmenu.h>
#include <kstdguiitem.h>

using namespace Barry;

Command::Command(): m_executed( false )
{
}

Command::~Command()
{
}

void Command::unexec()
{
	if ( !m_executed )
		return;
	m_executed = false;
	doUnexec();
}

void Command::exec()
{
	if ( m_executed )
		return;
	m_executed = true;
	doExec();
}

History *History::m_instance = 0;

History &History::self()
{
	if ( !m_instance )
		m_instance = new History;
	return *m_instance;
}

History::History(): m_idx( 0 )
{
	m_commands.setAutoDelete( true );
	reparseConfig();
}

void History::reparseConfig()
{
	KConfig *cfg = kapp->config();
	m_maxEntries = cfg->readUnsignedNumEntry(
			QString::fromLatin1( "History entries" ), 50 );

	truncateHistory();
}

void History::truncateHistory()
{
	while ( m_commands.count() > m_maxEntries )
		m_commands.remove( m_commands.getFirst() );
}

void History::setupActions( KActionCollection *coll )
{
	const QPair<KGuiItem, KGuiItem> backForward = KStdGuiItem::backAndForward();
	
	m_backAction = new KToolBarPopupAction( backForward.first, ALT+Key_Left,
		this, SLOT( back() ), coll, "back" );
	connect( m_backAction->popupMenu(), SIGNAL( aboutToShow() ),
	         this, SLOT( fillBackMenu() ) );
	connect( m_backAction->popupMenu(), SIGNAL( activated( int ) ),
	         this, SLOT( commandActivated( int ) ) );
	m_backAction->setEnabled( false );

	m_forwardAction = new KToolBarPopupAction( backForward.second,
		ALT+Key_Right, this, SLOT( forward() ), coll, "forward" );
	connect( m_forwardAction->popupMenu(), SIGNAL( aboutToShow() ),
	         this, SLOT( fillForwardMenu() ) );
	connect( m_forwardAction->popupMenu(), SIGNAL( activated( int ) ),
	         this, SLOT( commandActivated( int ) ) );
	m_forwardAction->setEnabled( false );
}

void History::installMenuBarHook( KMainWindow *mainWindow )
{
	QPopupMenu *goMenu = dynamic_cast<QPopupMenu *>(
		mainWindow->guiFactory()->container( QString::fromLatin1( "go" ),
		                                     mainWindow )
	);
	if ( goMenu ) {
		connect( goMenu, SIGNAL( aboutToShow() ), this, SLOT( fillGoMenu() ) );
		connect( goMenu, SIGNAL( activated( int ) ),
		         this, SLOT( commandActivated( int ) ) );
	}
}

bool History::canBack() const
{
	return m_idx > 0;
}

bool History::canForward() const
{
	return !m_commands.isEmpty() && m_idx < m_commands.count();
}

void History::back()
{
	if ( !canBack() )
		return;

	Command *cmd = m_commands.at( --m_idx );
	Q_ASSERT( cmd );
	cmd->unexec();

	m_backAction->setEnabled( canBack() );
	m_forwardAction->setEnabled( canForward() );
}

void History::forward()
{
	if ( !canForward() )
		return;

	Command *cmd = m_commands.at( m_idx++ );
	Q_ASSERT( cmd );
	cmd->exec();

	m_backAction->setEnabled( canBack() );
	m_forwardAction->setEnabled( canForward() );
}

void History::registerCommand( Command *cmd )
{
	m_commands.append( cmd );

	truncateHistory();
	m_idx = m_commands.count();

	m_backAction->setEnabled( true );
}

void History::fillBackMenu()
{
	QPopupMenu *menu = m_backAction->popupMenu();
	menu->clear();

	unsigned int i = 0;
	for ( Command *cmd = m_commands.at( m_idx - 1 );
	      cmd && i <= 5;
	      cmd = m_commands.prev(), ++i )
		menu->insertItem( cmd->text(), m_commands.at() );
}

void History::fillForwardMenu()
{
	QPopupMenu *menu = m_forwardAction->popupMenu();
	menu->clear();

	unsigned int i = 0;
	for ( Command *cmd = m_commands.at( m_idx );
	      cmd && i <= 5;
	      cmd = m_commands.next(), ++i )
		menu->insertItem( cmd->text(), m_commands.at() );
}

void History::fillGoMenu()
{
	KMainWindow *mainWindow = dynamic_cast<KMainWindow *>( kapp->mainWidget() );
	Q_ASSERT( mainWindow );
	QPopupMenu *goMenu = dynamic_cast<QPopupMenu *>(
		mainWindow->guiFactory()->container( QString::fromLatin1( "go" ), mainWindow )
	);
	if ( goMenu ) {
		while ( goMenu->count() > 3 )
			goMenu->removeItemAt( goMenu->count() - 1 );
		unsigned int i = 0;
	for ( Command *cmd = m_commands.last();
	      cmd && i <= 10;
		  cmd = m_commands.prev(), ++i )
		goMenu->insertItem( cmd->text(), m_commands.at() );
	}
}

void History::commandActivated( int id )
{
	const int origIdx = m_idx;

	while ( static_cast<unsigned int>( id ) != m_idx ) {
		if ( static_cast<unsigned int>( id ) > m_idx )
			forward();
		else
			back();
	}

	if ( id >= origIdx )
		forward();
}

#include "history.moc"
// vim:ts=4:sw=4:noet:list
