/**
	 Copyright (C) 2004-2005 Mickael Marchand <marchand@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.

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

#include "subversion_fileinfo.h"
#include "subversion_core.h"
#include <kdebug.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <kdevproject.h>
#include <unistd.h>
#include <tdeapplication.h>
#include <kdevmainwindow.h>
#include <tdemainwindow.h>
#include <tqregexp.h>

#include <tdeio/netaccess.h>
#include <tdelocale.h>

SVNFileInfoProvider::SVNFileInfoProvider(subversionPart *parent, const char *name)
    : KDevVCSFileInfoProvider( parent, "svnfileinfoprovider" ),
	m_cachedDirEntries( 0 ), m_recursiveDirEntries(0) {
	Q_UNUSED(name);
	m_part = parent;
}

SVNFileInfoProvider::~SVNFileInfoProvider() {
	delete m_cachedDirEntries;
	m_cachedDirEntries = 0;
	delete m_recursiveDirEntries;
 	m_recursiveDirEntries = 0;
}

//synchronous
const VCSFileInfoMap *SVNFileInfoProvider::status( const TQString &dirPath ) {
	if ( !m_cachedDirEntries )
		m_cachedDirEntries = new VCSFileInfoMap;
//	return m_cachedDirEntries;

	kdDebug(9036) << "svn provider : status " << dirPath << endl;

	if ( dirPath != m_previousDirPath ) {
		m_previousDirPath = dirPath;
		KURL servURL = "kdevsvn+http://fakeserver_this_is_normal_behavior/";
		TQByteArray parms;
		TQDataStream s( parms, IO_WriteOnly );
		int cmd = 9;
		TQString rPath = projectDirectory( );
		rPath += TQDir::separator() + dirPath;
		kdDebug(9036) << "DIR : " << rPath << " " << KURL( TQFileInfo( rPath ).absFilePath() ) << endl;

// 		s << cmd << KURL( TQFileInfo( rPath ).absFilePath() ) << true << true; //original line

		// Dukju Ahn: if checkRepos is set, status() accesses remote repository,
		// which causes significant delaym_owner especially when network speed is not fast enough.
		// Of course, the user cannot get information about the out-of-dateness of his local copy.
		s << cmd << KURL( TQFileInfo( rPath ).absFilePath() ) << false/*checkRepos*/ << false /*fullRecurse*/;

		TDEIO::SimpleJob *job2 = TDEIO::special(servURL, parms, false);
		job2->setWindow( m_part->mainWindow()->main() );


        TQMap<TQString,TQString> ma;

		TDEIO::NetAccess::synchronousRun(job2, m_part->mainWindow()->main(), 0, 0, &ma );

		TQValueList<TQString> keys = ma.keys();
		qHeapSort( keys );
		TQValueList<TQString>::Iterator begin = keys.begin(), end = keys.end(), it;

		TQString path;
		int text_status = 0, prop_status = 0, repos_text_status = 0, repos_prop_status = 0;
		long int rev = 0;
		int curIdx, lastIdx;

		TQRegExp rx( "([0-9]*)(.*)" );
		for ( it = begin; it != end; ) {
			kdDebug(9036) << "METADATA : " << *it << ":" << ma[ *it ] << endl;
			if ( rx.search( *it ) == -1 ) return m_cachedDirEntries; // something is wrong ! :)
            /* if some notification comes here, consume these notification metadatas */
            if ( rx.cap( 2 ) == "action" ){
                curIdx = lastIdx = rx.cap( 1 ).toInt();
                while ( curIdx == lastIdx ){
                    ++it;
                    if ( it == end ) break;
                    if ( rx.search( *it ) == -1 ) continue; // something is wrong
                    curIdx = rx.cap( 1 ).toInt();
                }
                continue;
            }
			curIdx = lastIdx = rx.cap( 1 ).toInt();
			while ( curIdx == lastIdx ) {
				if ( rx.cap( 2 ) == "path" )
					path = ma[ *it ];
				else if ( rx.cap( 2	 ) == "text" )
					text_status = ma[ *it ].toInt();
				else if ( rx.cap( 2	 ) == "prop" )
					prop_status = ma[ *it ].toInt();
				else if ( rx.cap( 2	 ) == "reptxt" )
					repos_text_status = ma[ *it ].toInt();
				else if ( rx.cap( 2	 ) == "repprop" )
					repos_prop_status = ma[ *it ].toInt();
				else if ( rx.cap( 2	 ) == "rev" )
					rev = ma[ *it ].toLong();
				++it;
                if ( it == end )
                    break;
				if ( rx.search( *it ) == -1 ) break; // something is wrong ! :)
				curIdx = rx.cap( 1 ).toInt();
			}
			slotStatus(path, text_status, prop_status, repos_text_status, repos_prop_status, rev);
		}
	}
	kdDebug(9036) << " Returning VcsFileInfoMap. provider::status() finished " << endl;
    return m_cachedDirEntries;
}

bool SVNFileInfoProvider::requestStatus( const TQString &dirPath, void *callerData, bool recursive, bool checkRepos ) {
	kdDebug(9036) << "##################################################################################### svn provider : request status" << endl;
    m_savedCallerData = callerData;
    // Flush old cache
    if (m_cachedDirEntries)
    {
        delete m_cachedDirEntries;
        m_cachedDirEntries = 0;
        m_previousDirPath = dirPath;
    }

	TQByteArray parms;
	TQDataStream s( parms, IO_WriteOnly );
	int cmd = 9;
	TQString rPath = projectDirectory( );
	rPath += TQDir::separator() + dirPath;
    
    if( ! m_part->isValidDirectory( rPath ) ){
        return false;
    }
    
	kdDebug(9036) << "DIR : " << rPath << " " << TQFileInfo( rPath ).absFilePath() << endl;
	s << cmd << KURL( TQFileInfo( rPath ).absFilePath() ) << checkRepos << recursive;
	KURL servURL = "kdevsvn+http://fakeserver_this_is_normal_behavior/";
	job = TDEIO::special(servURL, parms, false);
	connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), this, TQ_SLOT( slotResult( TDEIO::Job * ) ) );
	if( checkRepos )
		m_part->svncore()->initProcessDlg( job, dirPath, i18n("Subversion File/Directory Status") );
    return true;
}

void SVNFileInfoProvider::slotResult( TDEIO::Job *j ) {
	if ( j->error() )
		j->showErrorDialog( m_part->mainWindow()->main() );

	TDEIO::MetaData ma = j->metaData();
	TQValueList<TQString> keys = ma.keys();
	qHeapSort( keys );
	TQValueList<TQString>::Iterator begin = keys.begin(), end = keys.end(), it;

	TQString path;
	int text_status = 0, prop_status = 0, repos_text_status = 0, repos_prop_status = 0;
	long int rev = 0;
	int curIdx, lastIdx;

	TQRegExp rx( "([0-9]*)(.*)" );
	for ( it = begin; it != end; ) {
		kdDebug(9036) << "METADATA : " << *it << ":" << ma[ *it ] << endl;
		if ( rx.search( *it ) == -1 ) return; // something is wrong ! :)
        /* if some notification comes here, consume these notification metadatas */
        if ( rx.cap( 2 ) == "action" ){
            curIdx = lastIdx = rx.cap( 1 ).toInt();
            while ( curIdx == lastIdx ){
                ++it;
                if ( it == end ) break;
                if ( rx.search( *it ) == -1 ) continue; // something is wrong
                curIdx = rx.cap( 1 ).toInt();
            }
            continue;
        }
		curIdx = lastIdx = rx.cap( 1 ).toInt();
		while ( curIdx == lastIdx ) {
			if ( rx.cap( 2 ) == "path" )
				path = ma[ *it ];
			else if ( rx.cap( 2	 ) == "text" )
				text_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "prop" )
				prop_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "reptxt" )
				repos_text_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "repprop" )
				repos_prop_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "rev" )
				rev = ma[ *it ].toLong();
			++it;
            if ( it == end )
                break;
			if ( rx.search( *it ) == -1 ) break; // something is wrong ! :)
			curIdx = rx.cap( 1 ).toInt();
		}
		slotStatus(path, text_status, prop_status, repos_text_status, repos_prop_status, rev);
	}

	if ( m_cachedDirEntries )
		emit statusReady(*m_cachedDirEntries, m_savedCallerData);
}

void SVNFileInfoProvider::slotStatus( const TQString& path,int text_status, int prop_status,int repos_text_status, int repos_prop_status, long int rev) {
// 	kdDebug(9036) << "##################################################################################### svn provider : slotstatus"
// 		<< " path " << path << " text_status " << text_status << " prop_status " << prop_status << " repos_text_status " << repos_text_status
// 		<< " repos_prop_status " << repos_prop_status << " rev " << rev
// 		<< endl;

	if ( !m_cachedDirEntries )
		m_cachedDirEntries = new VCSFileInfoMap;

	TQString wRev = TQString::number( rev ); //work rev
	TQString rRev = TQString::number( rev );// repo rev
	VCSFileInfo::FileState state = VCSFileInfo::Unknown;

	switch ( text_status ) {
		case 1:
			break;
		case 2:
			break;
		case 3:
			state = VCSFileInfo::Uptodate;
			break;
		case 4:
			state = VCSFileInfo::Added;
			break;
		case 5:
			break;
		case 6: //deleted
			state = VCSFileInfo::Deleted;
			break;
		case 7: //replaced
            state = VCSFileInfo::Replaced;
			break;
		case 8: //modified
			state = VCSFileInfo::Modified;
			break;
		case 9: //merged
			break;
		case 10: //conflicted
			state = VCSFileInfo::Conflict;
			break;
		case 11: //ignored
			break;
		case 12: //obstructed
			break;
		case 13: //external
			break;
		case 14: //incomplete
			break;
	}
	switch( prop_status ) {
		case 8:
			state = VCSFileInfo::Modified;
			break;
	}
	switch ( repos_text_status ) {
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			break;
		case 6: //deleted
			break;
		case 7: //replaced
			break;
		case 8: //modified
			state = VCSFileInfo::NeedsPatch;
			break;
		case 9: //merged
			break;
		case 10: //conflicted
			break;
		case 11: //ignored
			break;
		case 12: //obstructed
			break;
		case 13: //external
			break;
		case 14: //incomplete
			break;
	}

	VCSFileInfo info(TQFileInfo( path ).fileName(),wRev,rRev,state);
	kdDebug(9036) << "Inserting " << info.toString() << endl;
	m_cachedDirEntries->insert( TQFileInfo( path ).fileName(), info);
}

TQString SVNFileInfoProvider::projectDirectory() const {
	return owner()->project()->projectDirectory();
}

const VCSFileInfoMap *SVNFileInfoProvider::statusExt( const TQString &dirPath,
	bool checkRepos, bool fullRecurse, bool getAll, bool noIgnore )
{
	if ( !m_recursiveDirEntries )
		m_recursiveDirEntries = new VCSFileInfoMap;

// 	if ( dirPath != m_recursivePreviousDirPath ) {
	m_recursiveDirEntries->clear();
	m_recursivePreviousDirPath = dirPath;
	KURL servURL = "kdevsvn+http://fakeserver_this_is_normal_behavior/";
	TQByteArray parms;
	TQDataStream s( parms, IO_WriteOnly );
	int cmd = 109;
	TQString rPath = projectDirectory( );
	rPath += TQDir::separator() + dirPath;
	kdDebug(9036) << "DIR : " << rPath << " " << KURL( TQFileInfo( rPath ).absFilePath() ) << endl;
	s << cmd << checkRepos << fullRecurse << getAll << noIgnore << -1 << "WORKING" << KURL( TQFileInfo( rPath ).absFilePath() );
	TDEIO::SimpleJob *job2 = TDEIO::special(servURL, parms, false);
	job2->setWindow( m_part->mainWindow()->main() );


	TQMap<TQString,TQString> ma;
	TDEIO::NetAccess::synchronousRun(job2, m_part->mainWindow()->main(), 0, 0, &ma );

	TQValueList<TQString> keys = ma.keys();
	qHeapSort( keys );
	TQValueList<TQString>::Iterator begin = keys.begin(), end = keys.end(), it;

	TQString path;
	int text_status = 0, prop_status = 0, repos_text_status = 0, repos_prop_status = 0;
	long int rev = 0;
	int curIdx, lastIdx;

	TQRegExp rx( "([0-9]*)(.*)" );
	for ( it = begin; it != end; ) {
		kdDebug(9036) << "METADATA : " << *it << ":" << ma[ *it ] << endl;
		if ( rx.search( *it ) == -1 ) return m_recursiveDirEntries; // something is wrong ! :)
		/* if some notification comes here, consume these notification metadatas */
		if ( rx.cap( 2 ) == "action" ){
			curIdx = lastIdx = rx.cap( 1 ).toInt();
			while ( curIdx == lastIdx ){
				++it;
				if ( it == end ) break;
				if ( rx.search( *it ) == -1 ) continue; // something is wrong
				curIdx = rx.cap( 1 ).toInt();
			}
			continue;
		}
		/* get properties */
		curIdx = lastIdx = rx.cap( 1 ).toInt();
		while ( curIdx == lastIdx ) {
			if ( rx.cap( 2 ) == "path" )
				path = ma[ *it ];
			else if ( rx.cap( 2	 ) == "text" )
				text_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "prop" )
				prop_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "reptxt" )
				repos_text_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "repprop" )
				repos_prop_status = ma[ *it ].toInt();
			else if ( rx.cap( 2	 ) == "rev" )
				rev = ma[ *it ].toLong();
			++it;
			if ( it == end )
				break;
			if ( rx.search( *it ) == -1 ) break; // something is wrong ! :)
			curIdx = rx.cap( 1 ).toInt();
		}
		slotStatusExt(dirPath, path, text_status, prop_status, repos_text_status, repos_prop_status, rev);
	}
// 	}

	return m_recursiveDirEntries;
}

void SVNFileInfoProvider::slotStatusExt(
	const TQString& reqPath, const TQString& path,int text_status, int prop_status,int repos_text_status, int repos_prop_status, long int rev)
{

	if ( !m_recursiveDirEntries )
		m_recursiveDirEntries = new VCSFileInfoMap;

	TQString wRev = TQString::number( rev ); //work rev
	TQString rRev = TQString::number( rev );// repo rev
	VCSFileInfo::FileState state = VCSFileInfo::Unknown;

	switch ( text_status ) {
		case 1: // does not exist
			break;
		case 2: // unversioned
			break;
		case 3:
			state = VCSFileInfo::Uptodate;
			break;
		case 4:
			state = VCSFileInfo::Added;
			break;
		case 5: // missing
			break;
		case 6: //deleted
			state = VCSFileInfo::Deleted;
			break;
		case 7: //replaced
			state = VCSFileInfo::Replaced;
			break;
		case 8: //modified
			state = VCSFileInfo::Modified;
			break;
		case 9: //merged
			break;
		case 10: //conflicted
			state = VCSFileInfo::Conflict;
			break;
		case 11: //ignored
			break;
		case 12: //obstructed
			break;
		case 13: //external
			break;
		case 14: //incomplete
			break;
	}
	switch( prop_status ) {
		case 8:
			state = VCSFileInfo::Modified;
			break;
	}
	switch ( repos_text_status ) {
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			break;
		case 6: //deleted
			break;
		case 7: //replaced
			break;
		case 8: //modified
			state = VCSFileInfo::NeedsPatch;
			break;
		case 9: //merged
			break;
		case 10: //conflicted
			break;
		case 11: //ignored
			break;
		case 12: //obstructed
			break;
		case 13: //external
			break;
		case 14: //incomplete
			break;
	}

	TQString relativeReqPath;
	if (reqPath == "./"){
		// case of project top directory
		TQString reqAbsPath = projectDirectory();

        if( path == reqAbsPath ){
            //key of VCSInfo is project directory itself. So it is set to .
            relativeReqPath = ".";
        }
        else{
            relativeReqPath = path.right( path.length() - reqAbsPath.length() - 1 );
        }
	}
	else {
		TQString reqAbsPath = projectDirectory() + TQDir::separator() + reqPath;
		relativeReqPath = path.right( path.length() - reqAbsPath.length() - 1 );

		if (relativeReqPath == reqAbsPath){
			// case of requested directory itself.
			relativeReqPath = ".";
		}
	}

	VCSFileInfo info(relativeReqPath, wRev, rRev, state);
	m_recursiveDirEntries->insert( relativeReqPath, info );

// 	VCSFileInfo info(TQFileInfo( path ).fileName(),wRev,rRev,state);
 	kdDebug(9036) << "Inserting " << info.toString() << endl;
// 	m_recursiveDirEntries->insert( TQFileInfo( path ).fileName(), info);
}

#include "subversion_fileinfo.moc"

