/***************************************************************************
 *   Copyright (C) 2001 by Harald Fernengel                                *
 *   harry@kdevelop.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 "diffpart.h"

#include <sys/stat.h>

#include <tqwhatsthis.h>
#include <tqpopupmenu.h>

#include <tdelocale.h>
#include <kdevgenericfactory.h>
#include <tdeaction.h>
#include <tdefiledialog.h>
#include <kprocess.h>
#include <tdeio/jobclasses.h>
#include <tdeio/job.h>
#include <tdeparts/part.h>
#include <tdetexteditor/editinterface.h>
#include <tdemessagebox.h>
#include <kdebug.h>
#include <kiconloader.h>

#include "kdevcore.h"
#include "kdevmainwindow.h"
#include "kdevpartcontroller.h"
#include "kdevplugininfo.h"

#include "diffdlg.h"
#include "diffwidget.h"

static const KDevPluginInfo pluginData("kdevdiff");

typedef KDevGenericFactory<DiffPart> DiffFactory;
K_EXPORT_COMPONENT_FACTORY( libkdevdiff, DiffFactory( pluginData ) )

DiffPart::DiffPart(TQObject *parent, const char *name, const TQStringList &)
    : KDevDiffFrontend(&pluginData, parent, name ? name : "DiffPart"), proc(0)
{
  setInstance(DiffFactory::instance());
  setXMLFile("kdevdiff.rc");

  diffWidget = new DiffWidget(this, 0, "diffWidget");
  diffWidget->setIcon( SmallIcon("edit-copy") );
  TQString nm( i18n( "Diff" ) );
  diffWidget->setCaption( i18n( "Diff Output" ) );
  TQWhatsThis::add(diffWidget, i18n("<b>Difference viewer</b><p>Shows output of the diff format. "
    "Can utilize every installed component that is able to show diff output. "
    "For example if you have Kompare installed, Difference Viewer can use its graphical diff view."));
  mainWindow()->embedOutputView( diffWidget, nm, i18n("Output of the diff command") );
  mainWindow()->setViewAvailable( diffWidget, false );

  TDEAction *action = new TDEAction( i18n("Difference Viewer..."), 0,
	       this, TQ_SLOT(slotExecDiff()),
	       actionCollection(), "tools_diff" );
  action->setToolTip(i18n("Difference viewer"));
  action->setWhatsThis(i18n("<b>Difference viewer</b><p>Shows the contents of a patch file."));

  connect( core(), TQ_SIGNAL(contextMenu(TQPopupMenu *, const Context *)),
           this, TQ_SLOT(contextMenu(TQPopupMenu *, const Context *)) );
}

static bool urlIsEqual(const KURL &a, const KURL &b)
{
  if (a.isLocalFile() && b.isLocalFile())
  {
    struct stat aStat, bStat;

    if ((::stat(TQFile::encodeName(a.fileName()), &aStat) == 0)
        && (::stat(TQFile::encodeName(b.fileName()), &bStat) == 0))
    {
      return (aStat.st_dev == bStat.st_dev) && (aStat.st_ino == bStat.st_ino);
    }
  }

  return a == b;
}

static KParts::ReadWritePart* partForURL(const KURL &url, KDevPartController* pc)
{
  if ( !pc )
    return 0;
  TQPtrListIterator<KParts::Part> it(*(pc->parts()));
  for ( ; it.current(); ++it)
  {
    KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*>(it.current());
    if ( rw_part && dynamic_cast<KTextEditor::EditInterface*>(it.current()) && urlIsEqual(url, rw_part->url()) )
      return rw_part;
  }

  return 0;
}

void DiffPart::contextMenu( TQPopupMenu* popup, const Context* context )
{
	if ( context->hasType( Context::EditorContext ) )
	{
		const EditorContext *eContext = static_cast<const EditorContext*>(context);
		popupFile = eContext->url();
	}
	else if ( context->hasType( Context::FileContext ) )
	{
		const FileContext * fContext = static_cast<const FileContext*>( context );
		popupFile.setPath( fContext->urls().first().fileName() );	//@fixme - assuming absolute path. is this correct?
	}
	else
	{
		return;
	}

	KParts::ReadWritePart* rw_part = partForURL( popupFile, partController() );
	if ( !rw_part ) return;
	
	if ( partController()->documentState( rw_part->url() ) != Clean )
	{
		int id = popup->insertItem( i18n( "Difference to Disk File" ),
							this, TQ_SLOT(localDiff()) );
		popup->TQMenuData::setWhatsThis(id, i18n("<b>Difference to disk file</b><p>Shows the difference between "
			"the file contents in this editor and the file contents on disk."));
	}
}

DiffPart::~DiffPart()
{
  if ( diffWidget )
    mainWindow()->removeView( diffWidget );

  delete proc;
  delete (DiffWidget*) diffWidget;
}

void DiffPart::localDiff()
{
  KParts::ReadWritePart* rw_part = partForURL( popupFile, partController() );
  if ( !rw_part )
    return;

  KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>(rw_part);
  if ( !editIface )
    return;
  buffer = editIface->text().local8Bit();
  resultBuffer = resultErr = TQString();

  delete proc;
  proc = new TDEProcess();

  *proc << "diff";
  *proc << "-u" << popupFile.path() << "-";
  proc->setWorkingDirectory( popupFile.directory() );

  connect( proc, TQ_SIGNAL(processExited( TDEProcess* )),
           this, TQ_SLOT(processExited( TDEProcess* )) );
  connect( proc, TQ_SIGNAL(receivedStdout( TDEProcess*, char*, int )),
           this, TQ_SLOT(receivedStdout( TDEProcess*, char*, int )) );
  connect( proc, TQ_SIGNAL(receivedStderr( TDEProcess*, char*, int )),
           this, TQ_SLOT(receivedStderr( TDEProcess*, char*, int )) );
  connect( proc, TQ_SIGNAL(wroteStdin( TDEProcess* )),
           this, TQ_SLOT(wroteStdin( TDEProcess* )) );

  if ( !proc->start( TDEProcess::NotifyOnExit, TDEProcess::All ) ) {
    KMessageBox::error( 0, i18n( "Could not invoke the \"diff\" command." ) );
    delete proc;
    proc = 0;
    return;
  }
  proc->writeStdin( buffer.data(), buffer.length() );
}

void DiffPart::processExited( TDEProcess* p )
{
  // diff has exit status 0 and 1 for success
  if ( p->normalExit() && ( p->exitStatus() == 0 || p->exitStatus() == 1 ) ) {
    if ( resultBuffer.isEmpty() )
      KMessageBox::information( 0, i18n("DiffPart: No differences found.") );
    else
      showDiff( resultBuffer );
  } else {
    KMessageBox::error( 0, i18n("Diff command failed (%1):\n").arg( p->exitStatus() ) + resultErr );
  }
  resultBuffer = resultErr = TQString();
  delete proc;
  proc = 0;
}

void DiffPart::receivedStdout( TDEProcess* /* p */, char* buf, int buflen )
{
  resultBuffer += TQString::fromLocal8Bit( buf, buflen );
}

void DiffPart::receivedStderr( TDEProcess* /* p */, char* buf, int buflen )
{
  kdDebug(9033) << "received Stderr: " << TQString(TQString::fromLocal8Bit( buf, buflen )).ascii() << endl;
  resultErr += TQString::fromLocal8Bit( buf, buflen );
}

void DiffPart::wroteStdin( TDEProcess* p )
{
  buffer = 0L;
  p->closeStdin();
}

void DiffPart::openURL( const KURL& url )
{
  diffWidget->slotClear();
  diffWidget->openURL( url );
  mainWindow()->raiseView( diffWidget );
/*
  DiffDlg* diffDlg = new DiffDlg( 0, "diffDlg" );

  diffDlg->openURL( url );
  diffDlg->exec();
  delete diffDlg;
*/
}

void DiffPart::showDiff( const TQString& diff )
{
  diffWidget->slotClear();
  diffWidget->setDiff( diff );
  mainWindow()->setViewAvailable( diffWidget, true );
  mainWindow()->raiseView( diffWidget );
/*
  DiffDlg* diffDlg = new DiffDlg( 0, "diffDlg" );

  diffDlg->setDiff( diff );
  diffDlg->exec();
  delete diffDlg;
*/
}

void DiffPart::slotExecDiff()
{
  KURL url = KFileDialog::getOpenURL( TQString(), TQString(), 0, i18n("Please Select Patch File") );

  if ( url.isEmpty() )
    return;

  openURL( url );
}

#include "diffpart.moc"
