/*
   TDevelop Autotools Support
   Copyright (c) 2001-2002 by Bernd Gehrmann <bernd@kdevelop.org>
   Copyright (c) 2002 by Victor Roeder <victor_roeder@gmx.de>
   Copyright (c) 2005 by Matt Rogers <mattr@kde.org>

 ***************************************************************************
 *                                                                         *
 *   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 "autoprojectwidget.h"

#include <tqcheckbox.h>
#include <tqdom.h>
#include <tqfile.h>
#include <tqheader.h>
#include <tqpainter.h>
#include <tqptrstack.h>
#include <tqregexp.h>
#include <tqsplitter.h>
#include <tqstringlist.h>
#include <tqtextstream.h>
#include <tqtoolbutton.h>
#include <tqtooltip.h>
#include <tqwhatsthis.h>
#include <tqtimer.h>

#include <kdebug.h>
#include <tdefiledialog.h>
#include <tdelistview.h>
#include <tdemessagebox.h>
#include <kregexp.h>
#include <kurl.h>
#include <tdefile.h>
#include <kxmlguiclient.h>
#include <tdeaction.h>

#include "kdevcore.h"
#include "domutil.h"
#include "misc.h"
#include "choosetargetdialog.h"

#include "autolistviewitems.h"

#include "autoprojectpart.h"
#include "autosubprojectview.h"
#include "autodetailsview.h"
#include "urlutil.h"
#include "makefilehandler.h"

static TQString nicePrimary( const TQString &primary )
{
	if ( primary == "PROGRAMS" )
		return i18n( "Program" );
	else if ( primary == "LIBRARIES" )
		return i18n( "Library" );
	else if ( primary == "LTLIBRARIES" )
		return i18n( "Libtool Library" );
	else if ( primary == "SCRIPTS" )
		return i18n( "Script" );
	else if ( primary == "HEADERS" )
		return i18n( "Header" );
	else if ( primary == "DATA" )
		return i18n( "Data" );
	else if ( primary == "JAVA" )
		return i18n( "Java" );
	else
		return TQString();
}


AutoProjectWidget::AutoProjectWidget( AutoProjectPart *part, bool kde )
		: TQVBox( 0, "auto project widget" )
{
	m_part = part;
	m_kdeMode = kde;
	m_activeSubproject = 0;
	m_activeTarget = 0;
	m_shownSubproject = 0;
	m_choosenTarget = 0;
        m_makefileHandler = new MakefileHandler();

	TQSplitter *splitter = new TQSplitter(TQt::Vertical, this);

	initOverview ( splitter );
	initDetailview ( splitter );

	initActions ();
}


AutoProjectWidget::~AutoProjectWidget()
{
    delete m_makefileHandler;
}

void AutoProjectWidget::initOverview ( TQWidget* parent )
{
	m_subprojectView = new AutoSubprojectView( this, m_part, parent, "project overview widget" );
}

void AutoProjectWidget::initDetailview ( TQWidget* parent )
{
	m_detailView = new AutoDetailsView( this, m_part, parent, "project details widget" );
}

void AutoProjectWidget::initActions()
{
	connect( m_subprojectView, TQ_SIGNAL( selectionChanged( TQListViewItem* ) ),
	         this, TQ_SLOT( slotOverviewSelectionChanged( TQListViewItem* ) ) );
}

AutoSubprojectView* AutoProjectWidget::getSubprojectView ()
{
	return m_subprojectView;
}

AutoDetailsView* AutoProjectWidget::getDetailsView ()
{
	return m_detailView;
}

void AutoProjectWidget::openProject( const TQString &dirName )
{
	m_subprojectView->loadMakefileams ( dirName );
	MakefileHandler mfh;
	mfh.parse( m_part->projectDirectory(), true );

}


void AutoProjectWidget::closeProject()
{
	m_shownSubproject = 0;
	m_subprojectView->listView()->clear();
	m_detailView->listView()->clear();
}

SubprojectItem* AutoProjectWidget::activeSubproject ()
{
	return m_activeSubproject;
}

TargetItem* AutoProjectWidget::activeTarget ()
{
	return m_activeTarget;
}

TQStringList AutoProjectWidget::allSubprojects()
{
	int prefixlen = m_part->projectDirectory().length() + 1;
	TQStringList res;

	TQListViewItemIterator it( m_subprojectView->listView() );
	for ( ; it.current(); ++it )
	{
		// Skip root subproject
		// if ( it.current() == m_subprojectView->firstChild() )
		//	continue;
		TQString path = static_cast<SubprojectItem*>( it.current() ) ->path;
		res.append( path.mid( prefixlen ) );
	}

	return res;
}

TQPtrList <SubprojectItem> AutoProjectWidget::allSubprojectItems()
{
	TQPtrList <SubprojectItem> res;

	TQListViewItemIterator it ( m_subprojectView->listView() );

	for ( ; it.current(); ++it )
	{
		// Skip root subproject
		// if ( it.current() == m_subprojectView->firstChild() )
		//	continue;

		SubprojectItem* spitem = static_cast <SubprojectItem*> ( it.current() );
		res.append ( spitem );
	}

	return res;
}

SubprojectItem* AutoProjectWidget::subprojectItemForPath(const TQString & path, bool pathIsAbsolute)
{
	kdDebug(9020) << "Looking for path " << path << endl;

	int prefixLen = m_part->projectDirectory().length() + 1;
	TQListViewItemIterator it( m_subprojectView->listView() );
	for(; it.current(); ++it)
	{
		SubprojectItem* spitem = static_cast<SubprojectItem*>(it.current() );
		TQString relpath = (spitem->path).mid(prefixLen);
		kdDebug(9020) << " ... checking -" << spitem->path << "-" << endl;
		kdDebug(9020) << " ... (tailored: -" << relpath << "- against -" << (pathIsAbsolute ? path.mid(prefixLen) : path) << "- )" << endl;
		if ( relpath == (pathIsAbsolute ? path.mid(prefixLen) : path))
		{
			kdDebug(9020) << "Found it!" << endl;
			return spitem;
		}
	}
	kdDebug(9020) << "Not found" << endl;
	return NULL;
}

TQString AutoProjectWidget::pathForTarget(const TargetItem *titem) const
{

	if (!titem)
		return TQString();

	kdDebug(9020) << "Looking for target " << titem->name << endl;
	int prefixLen = m_part->projectDirectory().length() + 1;
	TQListViewItemIterator it( m_subprojectView->listView() );
	for(; it.current(); ++it)
	{
		SubprojectItem* spitem = static_cast<SubprojectItem*>(it.current() );
		kdDebug(9020) << "Checking: " << spitem->path << endl;
		if (spitem->targets.containsRef(titem))
		{
			kdDebug(9020) << "Found it!" << endl;
			TQString relpath = (spitem->path).mid(prefixLen);
			return relpath;
		}
	}
	kdDebug(9020) << "Not found" << endl;
	return TQString();
}

TQStringList AutoProjectWidget::allLibraries()
{
	int prefixlen = m_part->projectDirectory().length() + 1;
	TQStringList res;

	TQListViewItemIterator it( m_subprojectView->listView() );
	for ( ; it.current(); ++it )
	{
		SubprojectItem *spitem = static_cast<SubprojectItem*>( it.current() );
		TQString path = spitem->path;
		TQPtrListIterator<TargetItem> tit( spitem->targets );
		for ( ; tit.current(); ++tit )
		{
			TQString primary = ( *tit ) ->primary;
			if ( primary == "LIBRARIES" || primary == "LTLIBRARIES" )
			{
				TQString fullname = path + "/" + ( *tit ) ->name;
				res.append( fullname.mid( prefixlen ) );
			}
		}
	}

	return res;
}


TQStringList AutoProjectWidget::allFiles()
{
	TQPtrStack<TQListViewItem> s;
	TQMap<TQString, bool> dict;

	for ( TQListViewItem * item = m_subprojectView->listView()->firstChild(); item;
	      item = item->nextSibling() ? item->nextSibling() : s.pop() )
	{
		if ( item->firstChild() )
			s.push( item->firstChild() );

		SubprojectItem *spitem = static_cast<SubprojectItem*>( item );
		// use URLUtil so paths in root project dir are worked out correctly
		TQString relPath = URLUtil::relativePath(m_part->projectDirectory(), spitem->path, URLUtil::SLASH_SUFFIX);
		TQPtrListIterator<TargetItem> tit( spitem->targets );
		for ( ; tit.current(); ++tit )
		{
			TQPtrListIterator<FileItem> fit( tit.current() ->sources );
			for ( ; fit.current(); ++fit )
			{

				if((*fit)->is_subst)
					continue;

				TQFileInfo fileInfo( (*fit)->name );
				if( fileInfo.extension() == "ui" )
				{
					dict.insert( relPath + fileInfo.baseName() + ".h", true );
					dict.insert( relPath + fileInfo.baseName() + ".cpp", true );
				}

				dict.insert( relPath + ( *fit ) ->name, true );
			}
		}
	}

	// Files may be in multiple targets, so we have to remove
	// duplicates
	TQStringList res;
	TQMap<TQString, bool>::Iterator it = dict.begin();
	while( it != dict.end() ){
		res << it.key();
		++it;
	}

	return res;
}


TQString AutoProjectWidget::subprojectDirectory()
{
	if ( !selectedSubproject() )
		return TQString();

	return selectedSubproject()->path;
}


void AutoProjectWidget::setActiveTarget( const TQString &targetPath )
{
	int prefixlen = m_part->projectDirectory().length() + 1;
	TQString olddir = m_part->activeDirectory();
	m_activeSubproject = 0;
	m_activeTarget = 0;

	TQListViewItemIterator it( m_subprojectView->listView() );
	for ( ; it.current(); ++it )
	{
		SubprojectItem *spitem = static_cast<SubprojectItem*>( it.current() );
		TQString path = spitem->path;
		TQPtrListIterator<TargetItem> tit( spitem->targets );
		for ( ; tit.current(); ++tit )
		{
			TQString primary = ( *tit ) ->primary;
			if ( primary != "PROGRAMS" && primary != "LIBRARIES"
			        && primary != "LTLIBRARIES" && primary != "JAVA" )
				continue;

			TQString currentTargetPath = ( path + "/" + ( *tit ) ->name ).mid( prefixlen );

			bool hasTarget = ( targetPath == currentTargetPath );
			( *tit )->setBold( hasTarget );
			if ( hasTarget )
			{
				spitem->setBold( true );
				m_activeSubproject = spitem;
				m_activeTarget = ( *tit );
				m_subprojectView->listView()->setSelected( m_activeSubproject, true );
				m_subprojectView->listView()->ensureItemVisible ( m_activeSubproject );
				m_subprojectView->listView()->viewport()->update();
				m_detailView->listView()->setSelected ( m_activeTarget, true );
				m_detailView->listView()->ensureItemVisible ( m_activeTarget );
				m_detailView->listView()->viewport()->update();
			}
			else
			{
				// to avoid a setBold ( false ) if there's another target in the current Subproject (i.e. spitem) ...
				spitem->setBold ( ( m_activeSubproject == spitem ) );
				m_detailView->listView()->viewport()->update();
			}
		}
	}
	if( olddir != m_part->activeDirectory() )
	{
		emit m_part->activeDirectoryChanged( olddir, m_part->activeDirectory() );
	}

	if ( m_activeSubproject == 0 && m_activeTarget == 0 )
	{
		m_subprojectView->listView()->setSelected ( m_subprojectView->listView()->firstChild(), true );
		m_subprojectView->listView()->ensureItemVisible ( m_subprojectView->listView()->firstChild() );
		m_subprojectView->listView()->viewport()->update();
	}
}


TQString AutoProjectWidget::activeDirectory()
{
	if ( m_activeSubproject )
		return m_activeSubproject->path.mid( m_part->projectDirectory().length() + 1 );
	else
	{
/*		if ( selectedSubproject() )
			return selectedSubproject()->path;
		else*/
			return TQString();
	}
}


void AutoProjectWidget::addFiles( const TQStringList &list )
{
	TQDomDocument &dom = *m_part->projectDom();
	TQStringList fileList = list;

	if ( DomUtil::readBoolEntry( dom, "/kdevautoproject/general/useactivetarget" ) )
	{
		TQStringList::iterator it;

		TQString fileName;

		for ( it = fileList.begin(); it != fileList.end(); ++it )
		{
			int pos = ( *it ).findRev('/');
			if (pos != -1)
				fileName = ( *it ).mid(pos+1);
			else
				fileName = ( *it );

			//FileItem * fitem = createFileItem( fileName,m_activeSubproject );
			//m_activeTarget->sources.append( fitem );
			//m_activeTarget->insertItem( fitem );

			/// @todo Merge with code in addfiledlg.cpp
			// Check wether a selected subproject+target exists and matches this file
			// If so use that as target.
			if( m_detailView->listView()->selectedItem() && m_subprojectView->listView()->selectedItem() )
			{
				TargetItem *titem = dynamic_cast <TargetItem*> ( m_detailView->listView()->selectedItem() );
				SubprojectItem * subitem = dynamic_cast <SubprojectItem*> ( m_subprojectView->listView()->selectedItem() );
				TQString relativeDir = URLUtil::directory(*it);
				SubprojectItem* spitem = subprojectItemForPath(relativeDir);

				if( titem && subitem && subitem == spitem )
				{
					addToTarget(fileName, subitem, titem);
				}else
				{
					addToTarget(fileName, m_activeSubproject, m_activeTarget);
				}
			}else
			{
				addToTarget(fileName, m_activeSubproject, m_activeTarget);
			}

//			TQString canontargetname = AutoProjectTool::canonicalize( m_activeTarget->name );
//			TQString varname = canontargetname + "_SOURCES";
//			m_activeSubproject->variables[ varname ] += ( " " + fileName );
//
//			TQMap<TQString, TQString> replaceMap;
//			replaceMap.insert( varname, m_activeSubproject->variables[ varname ] );
//
//			AutoProjectTool::addToMakefileam( m_activeSubproject->path + "/Makefile.am", replaceMap );
		}

		emitAddedFiles ( list );
	}
	else
	{
		TQStringList doManually, doneAutomatically;
		// First check wether the detail view has a selected target and the subproject
		// view selected subproject matches the path of the new file. Then
		// we can assume the user used the right-click option on the target
		for( TQStringList::iterator it = fileList.begin(); it != fileList.end(); ++it)
		{
			bool autoAdded = false;
			if( m_detailView->listView()->selectedItem() && m_subprojectView->listView()->selectedItem() )
			{
				TargetItem *titem = dynamic_cast <TargetItem*> ( m_detailView->listView()->selectedItem() );
				SubprojectItem * subitem = dynamic_cast <SubprojectItem*> ( m_subprojectView->listView()->selectedItem() );
				TQString relativeDir = URLUtil::directory(*it);
				SubprojectItem* spitem = subprojectItemForPath(relativeDir);
				if( titem && subitem && subitem == spitem )
				{
					addToTarget(URLUtil::filename(*it), subitem, titem);
					autoAdded = true;
					doneAutomatically << *it;
				}
			}
			if(!autoAdded) doManually << *it;
		}

		// See if we can figure out the target for each file without asking the user
		// I think it's a valid assumption that if a directory contains only one target
		// the file can be added to that target (Julian Rockey linux at jrockey.com)
		TQStringList temp = doManually;
		doManually.clear();
		for(TQStringList::iterator it = temp.begin();it!=temp.end();++it)
		{
			bool autoAdded = false;
			TQString relativeDir = URLUtil::directory(*it);
			SubprojectItem* spitem = subprojectItemForPath(relativeDir);
			if (spitem)
			{
				TQPtrList<TargetItem> titemList = spitem->targets;
				if (titemList.count()==1) {
					addToTarget( URLUtil::filename(*it), spitem, titemList.first() );
					doneAutomatically.append(*it);
					autoAdded = true;
				}
			}

			// add to manual list if this file wasn't auto-added
			if (!autoAdded) doManually.append(*it);
		}
		if (doneAutomatically.count()>0) emitAddedFiles(doneAutomatically);

                // raise dialog for any files that weren't added automatically
		if (doManually.count()>0) {
			ChooseTargetDialog chooseTargetDlg ( this, m_part, doManually, this, "choose target dialog" );

		        //chooseTargetDlg = new ChooseTargetDialog ( this, this, "choose target dialog" );

			if ( chooseTargetDlg.exec() && chooseTargetDlg.alwaysUseActiveTarget() )
				DomUtil::writeBoolEntry( dom, "/kdevautoproject/general/useactivetarget", true );
		}
	}
}

void AutoProjectWidget::addToTarget(const TQString & fileName, SubprojectItem* spitem, TargetItem* titem)
{
	TQString varname;
    /// \FIXME a quick hack to prevent adding header files to _SOURCES and display them in noinst_HEADERS
	if (AutoProjectPrivate::isHeader(fileName) &&
	    ( titem->primary == "PROGRAMS" || titem->primary == "LIBRARIES" ||  titem->primary == "LTLIBRARIES" ) )
	{
		kdDebug ( 9020 ) << "Ignoring header file and adding it to noinst_HEADERS: " << fileName << endl;
		TargetItem* noinst_HEADERS_item = getSubprojectView()->findNoinstHeaders(spitem);
		FileItem *fitem = createFileItem( fileName, spitem );
		noinst_HEADERS_item->sources.append( fitem );
		noinst_HEADERS_item->insertItem( fitem );
		varname = "noinst_HEADERS";
	}
	else
	{
		FileItem * fitem = createFileItem( fileName, spitem );
		titem->sources.append( fitem );
		titem->insertItem( fitem );

		TQString canontargetname = AutoProjectTool::canonicalize( titem->name );
		varname = canontargetname + "_SOURCES";
	}
	spitem->variables[ varname ] += ( " " + fileName );

	TQMap<TQString, TQString> replaceMap;
	replaceMap.insert( varname, spitem->variables[ varname ] );

	AutoProjectTool::addToMakefileam( spitem->path + "/Makefile.am", replaceMap );

	m_detailView->slotSelectionChanged( spitem );
}

void AutoProjectWidget::removeFiles( const TQStringList &list )
{
	Q_UNUSED( list )
}

void AutoProjectWidget::slotOverviewSelectionChanged( TQListViewItem *item )
{
	if ( !item )
		return;

	// Delete the items from the details view first.
	if ( m_shownSubproject )
	{
		// Remove all TargetItems and all of their children from the view
		kdDebug ( 9020 ) << "m_shownSubproject (before takeItem()): " << m_shownSubproject->subdir << endl;

		TQListViewItem* i = m_detailView->listView()->firstChild();
		while( i )
		{
			TQListViewItem* o = i;
			i = i->nextSibling();
			m_detailView->listView()->takeItem(o);
		}
	}

	// We assume here that ALL items in the over list view
	// are SubprojectItem's
	m_shownSubproject = dynamic_cast<SubprojectItem*>( item );
	if ( !m_shownSubproject) return;
	kdDebug ( 9020 ) << "m_shownSubproject (after takeItem()):  " << selectedSubproject()->subdir << endl;

	// Insert all TargetItems and all of their children into the view
	TQPtrListIterator<TargetItem> it2( selectedSubproject()->targets );
	for ( ; it2.current(); ++it2 )
	{
		kdDebug ( 9020 ) << "insertItem in detail " << ( *it2 )->name << endl;
		m_detailView->listView()->insertItem( *it2 );
		TQPtrListIterator<FileItem> it3( ( *it2 ) ->sources );
		for ( ; it3.current(); ++it3 )
			( *it2 )->insertItem( *it3 );
		TQString primary = ( *it2 ) ->primary;
		if ( primary == "PROGRAMS" || primary == "LIBRARIES" ||
		     primary == "LTLIBRARIES" || primary == "JAVA" )
			( *it2 ) ->setOpen( true );
	}
}

TargetItem *AutoProjectWidget::selectedTarget()
{
	ProjectItem * pvitem = static_cast<ProjectItem*>( m_detailView->listView()->selectedItem() );
	if ( !pvitem || ( pvitem->type() != ProjectItem::Target ) )
		return 0;

	return static_cast<TargetItem*>( pvitem );
}


FileItem *AutoProjectWidget::selectedFile()
{
	ProjectItem * pvitem = static_cast<ProjectItem*>( m_detailView->listView()->selectedItem() );
	if ( !pvitem || ( pvitem->type() != ProjectItem::File ) )
		return 0;

	return static_cast<FileItem*>( pvitem );
}

SubprojectItem* AutoProjectWidget::selectedSubproject()
{
	ProjectItem * pvitem = static_cast <SubprojectItem*> ( m_subprojectView->listView()->selectedItem() );

	if ( !pvitem || ( pvitem->type() != ProjectItem::Subproject ) )
		return 0;

	return static_cast <SubprojectItem*> ( pvitem );
}

TargetItem *AutoProjectWidget::createTargetItem( const TQString &name,
                                                 const TQString &prefix, const TQString &primary,
                                                 bool take )
{
	bool docgroup = ( primary == "KDEDOCS" );
	bool icongroup = ( primary == "KDEICON" );
	bool group = !(docgroup || icongroup);

	TQString text;
	if ( docgroup )
		text = i18n( "Documentation data" );
	else if ( icongroup )
		text = i18n( "TDE Icon data" ).arg( prefix );
	else
		text = i18n( "%1 (%2 in %3)" ).arg( name ).arg( nicePrimary( primary ) ).arg( prefix );

	// Workaround because of TQListView not being able to create
	// items without actually inserting them
	TargetItem *titem = new TargetItem( m_detailView->listView(), group, text );
	titem->name = name;
	titem->prefix = prefix;
	titem->primary = primary;
	if( take )
		m_detailView->listView()->takeItem( titem );

	return titem;
}


FileItem *AutoProjectWidget::createFileItem( const TQString &name, SubprojectItem *subproject )
{
	bool is_subst;
	if(name.find("$(") == 0 || name.find("${") == 0)
		is_subst = true;
	else
		is_subst = false;

	FileItem * fitem = new FileItem( m_subprojectView->listView(), name, is_subst );
	fitem->uiFileLink = m_detailView->getUiFileLink(subproject->relativePath()+"/", name );
	m_subprojectView->listView()->takeItem( fitem );
	fitem->name = name;

	return fitem;
}


void AutoProjectWidget::emitAddedFiles( const TQStringList &fileList )
{
	emit m_part->addedFilesToProject( fileList );
}

void AutoProjectWidget::emitAddedFile( const TQString &name )
{
	TQStringList fileList;
	fileList.append ( name );
	emit m_part->addedFilesToProject( fileList );
}

void AutoProjectWidget::emitRemovedFiles( const TQStringList &fileList )
{
	emit m_part->removedFilesFromProject( fileList );
}

void AutoProjectWidget::emitRemovedFile( const TQString &name )
{
	TQStringList fileList;
	fileList.append ( name );
	emit m_part->removedFilesFromProject( fileList );
}

void AutoProjectWidget::restoreSession ( const TQDomElement* el )
{
	Q_UNUSED( el );
}

void AutoProjectWidget::saveSession ( TQDomElement* el )
{
	if ( m_activeTarget && m_activeSubproject )
	{
		TQDomDocument domDoc = el->ownerDocument();

		TQString activeTargetPath = m_activeSubproject->path.mid ( m_part->project()->projectDirectory().length() + 1 );
		activeTargetPath = activeTargetPath + "/" + m_activeTarget->name;

		TQDomElement generalEl = domDoc.createElement("general");

		kdDebug ( 9020 ) << k_funcinfo << "Saving session data of AutoProjectWidget: " << activeTargetPath << endl;

		generalEl.setAttribute("activetarget", activeTargetPath);
		el->appendChild(generalEl);
	}
}

void AutoProjectWidget::setActiveSubproject( SubprojectItem * spitem )
{
	TQString olddir = m_part->activeDirectory();
	m_activeSubproject = spitem;
	emit m_part->activeDirectoryChanged( olddir, m_part->activeDirectory() );
}

void AutoProjectWidget::focusInEvent( TQFocusEvent */*e*/ )
{
	switch (m_lastFocusedView)
	{
	case DetailsView:
		m_detailView->listView()->setFocus();
		break;
	case SubprojectView:
	default:
		m_subprojectView->listView()->setFocus();
	}
}

void AutoProjectWidget::setLastFocusedView( AutoProjectView view )
{
	m_lastFocusedView = view;
}

#include "autoprojectwidget.moc"

MakefileHandler* AutoProjectWidget::makefileHandler()
{
	return m_makefileHandler;
}
