/***************************************************************************
                                diffmodel.cpp  -  description
                                -------------------
        begin                   : Sun Mar 4 2001
        copyright               : (C) 2001-2004 Otto Bruggeman
                                  (C) 2001-2003 John Firebaugh
        email                   : otto.bruggeman@home.nl
                                  jfirebaugh@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 <tqregexp.h>
#include <tqvaluelist.h>

#include <kdebug.h>
#include <tdelocale.h>

#include "difference.h"
#include "diffhunk.h"
#include "diffmodel.h"

using namespace Diff2;

/**  */
DiffModel::DiffModel( const TQString& source, const TQString& destination ) :
	m_source( source ),
	m_destination( destination ),
	m_sourcePath( "" ),
	m_destinationPath( "" ),
	m_sourceFile( "" ),
	m_destinationFile( "" ),
	m_sourceTimestamp( "" ),
	m_destinationTimestamp( "" ),
	m_sourceRevision( "" ),
	m_destinationRevision( "" ),
	m_appliedCount( 0 ),
	m_modified( false ),
	m_diffIndex( 0 ),
	m_selectedDifference( 0 ),
	m_blended( false )
{
	splitSourceInPathAndFileName();
	splitDestinationInPathAndFileName();
}

DiffModel::DiffModel() :
	m_source( "" ),
	m_destination( "" ),
	m_sourcePath( "" ),
	m_destinationPath( "" ),
	m_sourceFile( "" ),
	m_destinationFile( "" ),
	m_sourceTimestamp( "" ),
	m_destinationTimestamp( "" ),
	m_sourceRevision( "" ),
	m_destinationRevision( "" ),
	m_appliedCount( 0 ),
	m_modified( false ),
	m_diffIndex( 0 ),
	m_selectedDifference( 0 ),
	m_blended( false )
{
}

/**  */
DiffModel::~DiffModel()
{
}

void DiffModel::splitSourceInPathAndFileName()
{
	int pos;

	if( ( pos = m_source.findRev( "/" ) ) >= 0 )
		m_sourcePath = m_source.mid( 0, pos+1 );

	if( ( pos = m_source.findRev( "/" ) ) >= 0 )
		m_sourceFile = m_source.mid( pos+1, m_source.length() - pos );
	else
		m_sourceFile = m_source;

	kdDebug(8101) << m_source << " was split into " << m_sourcePath << " and " << m_sourceFile << endl;
}

void DiffModel::splitDestinationInPathAndFileName()
{
	int pos;

	if( ( pos = m_destination.findRev( "/" ) )>= 0 )
		m_destinationPath = m_destination.mid( 0, pos+1 );

	if( ( pos = m_destination.findRev( "/" ) ) >= 0 )
		m_destinationFile = m_destination.mid( pos+1, m_destination.length() - pos );
	else
		m_destinationFile = m_source;

	kdDebug(8101) << m_destination << " was split into " << m_destinationPath << " and " << m_destinationFile << endl;
}

DiffModel& DiffModel::operator=( const DiffModel& model )
{
	if ( &model != this ) // Guard from self-assignment
	{
		m_source = model.m_source;
		m_destination = model.m_destination;
		m_sourcePath = model.m_sourcePath;
		m_sourceFile = model.m_sourceFile;
		m_sourceTimestamp = model.m_sourceTimestamp;
		m_sourceRevision = model.m_sourceRevision;
		m_destinationPath = model.m_destinationPath;
		m_destinationFile = model.m_destinationFile;
		m_destinationTimestamp = model.m_destinationTimestamp;
		m_destinationRevision = model.m_destinationRevision;
		m_appliedCount = model.m_appliedCount;
		m_modified = model.m_modified;

		m_diffIndex = model.m_diffIndex;
		m_selectedDifference = model.m_selectedDifference;
	}

	return *this;
}

bool DiffModel::operator<( const DiffModel& model )
{
	if ( localeAwareCompareSource( model ) < 0 )
		return true;
	return false;
}

int DiffModel::localeAwareCompareSource( const DiffModel& model )
{
	int result = m_sourcePath.localeAwareCompare( model.m_sourcePath );

	if ( result == 0 )
		return m_sourceFile.localeAwareCompare( model.m_sourceFile );

	return result;
}

TQString DiffModel::recreateDiff() const
{
	// For now we'll always return a diff in the diff format
	TQString diff;

	// recreate header
	TQString tab = TQString::fromLatin1( "\t" );
	TQString nl  = TQString::fromLatin1( "\n" );
	diff += TQString::fromLatin1( "--- %1\t%2" ).arg( m_source ).arg( m_sourceTimestamp );
	if ( !m_sourceRevision.isEmpty() )
		diff += tab + m_sourceRevision;
	diff += nl;
	diff += TQString::fromLatin1( "+++ %1\t%2" ).arg( m_destination ).arg( m_destinationTimestamp );
	if ( !m_destinationRevision.isEmpty() )
		diff += tab + m_destinationRevision;
	diff += nl;

	// recreate body by iterating over the hunks
	DiffHunkListConstIterator hunkIt = m_hunks.begin();
	DiffHunkListConstIterator hEnd   = m_hunks.end();

	for ( ; hunkIt != hEnd; ++hunkIt )
	{
		if ((*hunkIt)->type() != DiffHunk::AddedByBlend)
			diff += (*hunkIt)->recreateHunk();
	}

	return diff;
}

DifferenceList* DiffModel::allDifferences()
{
	if ( m_hunks.count() != 0 )
	{
		DiffHunkListConstIterator hunkIt = m_hunks.begin();
		DiffHunkListConstIterator hEnd   = m_hunks.end();

		for ( ; hunkIt != hEnd; ++hunkIt )
		{
			DiffHunk* hunk = *hunkIt;

			DifferenceListConstIterator diffIt = hunk->differences().begin();
			DifferenceListConstIterator dEnd   = hunk->differences().end();

			for ( ; diffIt != dEnd; ++diffIt )
			{
				m_allDifferences.append( *diffIt );
			}
		}
		return &m_allDifferences;
	}
	else
	{
		DifferenceList *diffList = new DifferenceList;
		return diffList;
	}
}

Difference* DiffModel::firstDifference()
{
	kdDebug( 8101 ) << "DiffModel::firstDifference()" << endl;
	m_diffIndex = 0;
	kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;

	m_selectedDifference = m_differences[ m_diffIndex ];

	return m_selectedDifference;
}

Difference* DiffModel::lastDifference()
{
	kdDebug( 8101 ) << "DiffModel::lastDifference()" << endl;
	m_diffIndex = m_differences.count() - 1;
	kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;

	m_selectedDifference = m_differences[ m_diffIndex ];

	return m_selectedDifference;
}

Difference* DiffModel::prevDifference()
{
	kdDebug( 8101 ) << "DiffModel::prevDifference()" << endl;
	if ( --m_diffIndex < m_differences.count() )
	{
		kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
		m_selectedDifference = m_differences[ m_diffIndex ];
	}
	else
	{
		m_selectedDifference = 0;
		m_diffIndex = 0;
		kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
	}

	return m_selectedDifference;
}

Difference* DiffModel::nextDifference()
{
	kdDebug( 8101 ) << "DiffModel::nextDifference()" << endl;
	if (  ++m_diffIndex < m_differences.count() )
	{
		kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
		m_selectedDifference = m_differences[ m_diffIndex ];
	}
	else
	{
		m_selectedDifference = 0;
		m_diffIndex = 0; // just for safety...
		kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
	}

	return m_selectedDifference;
}

const TQString DiffModel::sourceFile() const
{
	return m_sourceFile;
}

const TQString DiffModel::destinationFile() const
{
	return m_destinationFile;
}

const TQString DiffModel::sourcePath() const
{
	return m_sourcePath;
}

const TQString DiffModel::destinationPath() const
{
	return m_destinationPath;
}

void DiffModel::setSourceFile( TQString path )
{
	m_source = path;
	splitSourceInPathAndFileName();
}

void DiffModel::setDestinationFile( TQString path )
{
	m_destination = path;
	splitDestinationInPathAndFileName();
}

void DiffModel::setSourceTimestamp( TQString timestamp )
{
	m_sourceTimestamp = timestamp;
}

void DiffModel::setDestinationTimestamp( TQString timestamp )
{
	m_destinationTimestamp = timestamp;
}

void DiffModel::setSourceRevision( TQString revision )
{
	m_destinationRevision = revision;
}

void DiffModel::setDestinationRevision( TQString revision )
{
	m_destinationRevision = revision;
}

void DiffModel::addHunk( DiffHunk* hunk )
{
	m_hunks.append( hunk );
}

void DiffModel::addDiff( Difference* diff )
{
	m_differences.append( diff );
}

void DiffModel::applyDifference( bool apply )
{
	if ( apply && !m_selectedDifference->applied() )
		m_appliedCount++;
	else if ( !apply && m_selectedDifference->applied() )
		m_appliedCount--;

	bool modified;

	// Not setting the m_modified yet so i can still query the current
	// modified status from the slot that is connected to the signal
	if ( m_appliedCount == 0 )
		modified = false;
	else
		modified = true;

	emit setModified( modified );

	m_modified = modified;

	m_selectedDifference->apply( apply );
}

void DiffModel::applyAllDifferences( bool apply )
{
	bool modified;

	// Not setting the m_modified yet so i can still query the current
	// modified status from the slot that is connected to the signal
	if ( apply )
	{
		m_appliedCount = m_differences.count();
		modified = true;
	}
	else
	{
		m_appliedCount = 0;
		modified = false;
	}

	emit setModified( modified );

	m_modified = modified;

	DifferenceListIterator diffIt = m_differences.begin();
	DifferenceListIterator dEnd   = m_differences.end();
	
	for ( ; diffIt != dEnd; ++diffIt )
	{
		(*diffIt)->apply( apply );
	}
}

void DiffModel::slotSetModified( bool modified )
{
	// Not setting the m_modified yet so i can still query the current
	// modified status from the slot that is connected to the signal
	emit setModified( modified );

	m_modified = modified;
}

bool DiffModel::setSelectedDifference( Difference* diff )
{
	kdDebug(8101) << "diff = " << diff << endl;
	kdDebug(8101) << "m_selectedDifference = " << m_selectedDifference << endl;

	if ( diff != m_selectedDifference )
	{
		if ( ( m_differences.findIndex( diff ) ) == -1 )
			return false;
		// Dont set m_diffIndex if it cant be found
		m_diffIndex = m_differences.findIndex( diff );
		kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
		m_selectedDifference = diff;
	}

	return true;
}

#include "diffmodel.moc"
