/***************************************************************************
 *   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 <tqlayout.h>
#include <tqtextedit.h>
#include <tqpopupmenu.h>
#include <tqcursor.h>
#include <tqfile.h>

#include <tdeconfig.h>
#include <tdeapplication.h>
#include <tdelocale.h>
#include <kservice.h>
#include <tdetempfile.h>
#include <tdepopupmenu.h>
#include <kiconloader.h>
#include <tdefiledialog.h>
#include <tdemessagebox.h>

#include <tdeparts/componentfactory.h>
#include <tdeparts/part.h>

#include <tdeio/jobclasses.h>
#include <tdeio/job.h>

#include <kdevmainwindow.h>

#include "diffpart.h"
#include "diffwidget.h"

// yup, magic value for the popupmenu-id
static const int POPUP_BASE = 130977;

TQStringList KDiffTextEdit::extParts;
TQStringList KDiffTextEdit::extPartsTranslated;

KDiffTextEdit::KDiffTextEdit( TQWidget* parent, const char* name ): TQTextEdit( parent, name )
{
  TDEConfig* config = tdeApp->config();
  config->setGroup( "Diff" );
  _highlight = config->readBoolEntry( "Highlight", true );

  searchExtParts();
}

KDiffTextEdit::~KDiffTextEdit()
{
  TDEConfig* config = tdeApp->config();

  config->setGroup( "Diff" );
  config->writeEntry( "Highlight", _highlight );
}

TQPopupMenu* KDiffTextEdit::createPopupMenu()
{
  return createPopupMenu( TQPoint() );
}

TQPopupMenu* KDiffTextEdit::createPopupMenu( const TQPoint& p )
{
  TQPopupMenu* popup = TQTextEdit::createPopupMenu( p );
  if ( !popup )
    popup = new TQPopupMenu( this );

  int i = 0;

  for ( TQStringList::Iterator it = extPartsTranslated.begin(); it != extPartsTranslated.end(); ++it ) {
    popup->insertItem( i18n( "Show in %1" ).arg( *it ), i + POPUP_BASE, i );
    i++;
  }
  if ( !extPartsTranslated.isEmpty() )
    popup->insertSeparator( i );
  connect( popup, TQ_SIGNAL(activated(int)), this, TQ_SLOT(popupActivated(int)) );

  popup->insertItem( SmallIconSet( "document-save-as" ), i18n( "&Save As..." ), this, TQ_SLOT(saveAs()), CTRL + Key_S, POPUP_BASE - 2, 0 );
  popup->setItemEnabled( POPUP_BASE - 2, length() > 0 );

  popup->insertSeparator( 1 );

  popup->insertItem( i18n( "Highlight Syntax" ), this, TQ_SLOT(toggleSyntaxHighlight()), 0, POPUP_BASE - 1, 2 );
  popup->setItemChecked( POPUP_BASE - 1, _highlight );
  popup->insertSeparator( 3 );

  popup->insertSeparator();
  popup->insertItem( i18n("Hide view"), parent(), TQ_SLOT(hideView()) );

  return popup;
}

void KDiffTextEdit::saveAs()
{
  TQString fName = KFileDialog::getSaveFileName();
  if ( fName.isEmpty() )
    return;

  TQFile f( fName );
  if ( f.open( IO_WriteOnly ) ) {
    TQTextStream stream( &f );
    int pCount = paragraphs();
    for ( int i = 0; i < pCount; ++i )
      stream << text( i ) << "\n";
    f.close();
  } else {
    KMessageBox::sorry( 0, i18n("Unable to open file."), i18n("Diff Frontend") );
  }
}

void KDiffTextEdit::toggleSyntaxHighlight()
{
  _highlight = !_highlight;
  if ( _highlight )
    applySyntaxHighlight();
  else
    clearSyntaxHighlight();
}

void KDiffTextEdit::applySyntaxHighlight()
{
  // the diff has been loaded so we apply a simple highlighting
  static TQColor cAdded( 190, 190, 237);
  static TQColor cRemoved( 190, 237, 190 );

  if ( !_highlight )
    return;

  int paragCount = paragraphs();
  for ( int i = 0; i < paragCount; ++i ) {
    TQString txt = text( i );
    if ( txt.length() > 0 ) {
      if ( txt.startsWith( "+" ) || txt.startsWith( ">" ) ) {
        setParagraphBackgroundColor( i, cAdded );
      } else if ( txt.startsWith( "-" ) || txt.startsWith( "<" ) ) {
        setParagraphBackgroundColor( i, cRemoved );
      }
    }
  }
}

void KDiffTextEdit::clearSyntaxHighlight()
{
  int paragCount = paragraphs();
  for ( int i = 0; i < paragCount; ++i ) {
    clearParagraphBackground( i );
  }
}
 
void KDiffTextEdit::searchExtParts()
{
  // only execute once
  static bool init = false;
  if ( init )
    return;
  init = true;

  // search all parts that can handle text/x-diff
  TDETrader::OfferList offers = TDETrader::self()->query("text/x-diff", "('KParts/ReadOnlyPart' in ServiceTypes) and ('text/x-diff' in ServiceTypes) and (DesktopEntryName != 'katepart')");
  TDETrader::OfferList::const_iterator it;
  for ( it = offers.begin(); it != offers.end(); ++it ) {
    KService::Ptr ptr = (*it);
    extPartsTranslated << ptr->name();
    extParts << ptr->desktopEntryName();
  }
  return;
}

void KDiffTextEdit::popupActivated( int id )
{
  id -= POPUP_BASE;
  if ( id < 0 || id > (int)extParts.count() )
    return;

  emit externalPartRequested( extParts[ id ] );
}

DiffWidget::DiffWidget( DiffPart * part, TQWidget *parent, const char *name, WFlags f ):
    TQWidget( parent, name, f ), m_part( part ), tempFile( 0 )
{
  job = 0;
  extPart = 0;

  te = new KDiffTextEdit( this, "Main Diff Viewer" );
  te->setReadOnly( true );
  te->setTextFormat( TQTextEdit::PlainText );
//  te->setMinimumSize( 300, 200 );
  connect( te, TQ_SIGNAL(externalPartRequested(const TQString&)), this, TQ_SLOT(loadExtPart(const TQString&)) );

  TQVBoxLayout* layout = new TQVBoxLayout( this );
  layout->addWidget( te );
}

DiffWidget::~DiffWidget()
{
    m_part = 0;
    delete tempFile;
}

void DiffWidget::setExtPartVisible( bool visible )
{
  if ( !extPart || !extPart->widget() ) {
    te->show();
    return;
  }
  if ( visible ) {
    te->hide();
    extPart->widget()->show();
  } else {
    te->show();
    extPart->widget()->hide();
  }
}

void DiffWidget::loadExtPart( const TQString& partName )
{
  if ( extPart ) {
    setExtPartVisible( false );
    delete extPart;
    extPart = 0;
  }

  KService::Ptr extService = KService::serviceByDesktopName( partName );
  if ( !extService )
    return;

  extPart = KParts::ComponentFactory::createPartInstanceFromService<KParts::ReadOnlyPart>( extService, this, 0, this, 0 );
  if ( !extPart || !extPart->widget() )
    return;

  layout()->add( extPart->widget() );

  setExtPartVisible( true );

  if ( te->paragraphs() > 0 )
    populateExtPart();
}

void DiffWidget::slotClear()
{
  rawDiff = TQString();
  te->clear();
  if ( extPart )
    extPart->closeURL();
}

// internally for the TextEdit only!
void DiffWidget::slotAppend( const TQString& str )
{
  te->append( str );
}

// internally for the TextEdit only!
void DiffWidget::slotAppend( TDEIO::Job*, const TQByteArray& ba )
{
  slotAppend( TQString( ba ) );
}

void DiffWidget::populateExtPart()
{
  if ( !extPart )
    return;

  bool ok = false;
  int paragCount = te->paragraphs();
  if ( extPart->openStream( "text/plain", KURL() ) ) {
    for ( int i = 0; i < paragCount; ++i )
      extPart->writeStream( rawDiff.local8Bit() );
    ok = extPart->closeStream();
  } else {
      // workaround for parts that cannot handle streams
      delete tempFile;
      tempFile = new KTempFile();
      tempFile->setAutoDelete( true );
        *(tempFile->textStream()) << rawDiff.local8Bit() << endl;
      tempFile->close();
      ok = extPart->openURL( KURL::fromPathOrURL( tempFile->name() ) );
  }
  if ( !ok )
    setExtPartVisible( false );
}

// internally for the TextEdit only!
void DiffWidget::slotFinished()
{
    te->applySyntaxHighlight();
    populateExtPart();
}

void DiffWidget::setDiff( const TQString& diff )
{
  slotClear();
  rawDiff = diff;
  slotAppend( diff );
  slotFinished();
}

void DiffWidget::openURL( const KURL& url )
{
  if ( job )
    job->kill();

  TDEIO::TransferJob* job = TDEIO::get( url );
  if ( !job )
    return;

  connect( job, TQ_SIGNAL(data( TDEIO::Job *, const TQByteArray & )),
           this, TQ_SLOT(slotAppend( TDEIO::Job*, const TQByteArray& )) );
  connect( job, TQ_SIGNAL(result( TDEIO::Job * )),
           this, TQ_SLOT(slotFinished()) );
}

void DiffWidget::contextMenuEvent( TQContextMenuEvent* /* e */ )
{
  TQPopupMenu* popup = new TQPopupMenu( this );

  if ( !te->isVisible() )
  {
    popup->insertItem( i18n("Display &Raw Output"), this, TQ_SLOT(showTextEdit()) );
    popup->insertSeparator();
    popup->insertItem( i18n("Hide view"), this, TQ_SLOT(hideView()) );
  }

  popup->exec( TQCursor::pos() );
  delete popup;
}

void DiffWidget::showExtPart()
{
  setExtPartVisible( true );
}

void DiffWidget::showTextEdit()
{
  setExtPartVisible( false );
}

void DiffWidget::hideView()
{
  m_part->mainWindow()->setViewAvailable( this, false );
}

#include "diffwidget.moc"
