#include <tqlayout.h>
#include <tqpainter.h>
#include <tqpopupmenu.h>

#include <tdeparts/part.h>
#include <klibloader.h>
#include <kurl.h>
#include <kdebug.h>
#include <tdelistview.h>
#include <tdelocale.h>
#include <kstatusbar.h>

#include <kdevcore.h>
#include <kdevpartcontroller.h>
#include <kdevmainwindow.h>
#include <kdevproject.h>

#include "valgrind_part.h"
#include "valgrind_widget.h"

#define VALLISTVIEWITEMRTTI 130977

// helper class to sort the ListView by item number instead of the string representation of the item number
class ValListViewItem: public TQListViewItem
{
public:
  ValListViewItem( TQListView* parent, int key, int pid, const TQString& message ):
    TQListViewItem( parent, TQString::number( key ), TQString::number( pid ), message ),
    _key( key ), _pid ( pid ), backtrace( false ), _line( -1 ), _active( false ) {}

  ValListViewItem( ValListViewItem* parent, int key, int pid, const TQString& message, const TQString& filename, int line, bool active ):
    TQListViewItem( parent, TQString::number( key ), TQString(), message ),
    _key( key ), _pid( pid ), backtrace( true ), _filename( filename ), _line( line ), _active( active )
  {
    if ( parent->_pid != _pid && _pid > 0 )
      setText( 1, TQString::number( _pid ) );
  }

  virtual ~ValListViewItem();

  static int intCompare( int i1, int i2 )
  {
    if ( i1 > i2 )
      return 1;
    else if ( i1 < i2 )
      return -1;
    else
      return 0;
  }

  int compare( TQListViewItem* i, int col, bool ascending ) const
  {
    if ( !i || i->rtti() != VALLISTVIEWITEMRTTI )
      return TQListViewItem::compare( i, col, ascending );
    switch ( col ) {
      case 0 : return intCompare( ((ValListViewItem*)i)->_key, _key );
      case 1 : return intCompare( ((ValListViewItem*)i)->_pid, _pid );
      default: return TQListViewItem::compare( i, col, ascending );
    }
  }

  void paintCell( TQPainter* p, const TQColorGroup& cg, int column, int width, int align )
  {
    if ( _active ) {
      TQFont fnt = p->font();
      fnt.setBold( true );
      p->setFont( fnt );
    }
    TQListViewItem::paintCell( p, cg, column, width, align );
  }

  int rtti() const { return VALLISTVIEWITEMRTTI; }

  TQString fileName() const { return _filename; }
  int line() const { return _line; }
  TQString message() const { return text( 2 ); }
  bool isHighlighted() const { return _active; }

private:
  int _key;
  int _pid;
  bool backtrace;
  TQString _filename;
  int _line;
  bool _active;
};

ValListViewItem::~ValListViewItem() {}

ValgrindWidget::ValgrindWidget( ValgrindPart *part )
 : TQWidget(0, "valgrind widget"), _part( part )
{
  TQVBoxLayout* vbl = new TQVBoxLayout( this );
  lv = new TDEListView( this );
  lv->addColumn( i18n( "No." ) );
  lv->addColumn( i18n( "Thread" ) );
  lv->addColumn( i18n( "Message" ) );
  lv->setSorting( 0, false );
  lv->setRootIsDecorated( true );
  lv->setAllColumnsShowFocus( true );
  vbl->addWidget( lv );

  popup = new TQPopupMenu( lv, "valPopup" );
  popup->insertItem( i18n( "&Open Valgrind Output..." ), _part, TQ_SLOT(loadOutput()), 0, 0 );
  popup->insertSeparator();
  popup->insertItem( i18n( "Expand All Items" ), this, TQ_SLOT(expandAll()), 0, 2 );
  popup->insertItem( i18n( "Collapse All Items" ), this, TQ_SLOT(collapseAll()), 0, 3 );

  connect( popup, TQ_SIGNAL(aboutToShow()),
           this, TQ_SLOT(aboutToShowPopup()) );
  connect( lv, TQ_SIGNAL(executed(TQListViewItem*)),
           this, TQ_SLOT(executed(TQListViewItem*)) );
  connect( lv, TQ_SIGNAL(contextMenu(TDEListView*, TQListViewItem*, const TQPoint&)),
           this, TQ_SLOT(slotContextMenu(TDEListView*, TQListViewItem*, const TQPoint&)) );
}


ValgrindWidget::~ValgrindWidget()
{
}

void ValgrindWidget::clear()
{
  lv->clear();
  msgNumber = 1;
}

void ValgrindWidget::addMessage( const ValgrindItem& vi )
{
  TQStringList projectFiles;
  TQString projectDirectory;

  ValListViewItem* lvi = new ValListViewItem( lv, msgNumber++, vi.pid(), vi.message() );
  lvi->setMultiLinesEnabled( true );
  const ValgrindItem::BacktraceList backtrace = vi.backtrace();
  if ( !backtrace.isEmpty() )
    lvi->setExpandable( true );

  int i = 0;
  for ( ValgrindItem::BacktraceList::ConstIterator it = backtrace.begin(); it != backtrace.end(); ++it ) {
    new ValListViewItem( lvi, ++i, (*it).pid(), (*it).message(), (*it).url(), (*it).line(), (*it).isHighlighted() );
  }
}

void ValgrindWidget::executed( TQListViewItem* lvi )
{
  Q_ASSERT( _part );
  Q_ASSERT( _part->partController() );
  Q_ASSERT( _part->mainWindow() );

  if ( !lvi || lvi->rtti() != VALLISTVIEWITEMRTTI )
    return;
  ValListViewItem* vli = 0;
  if ( !((ValListViewItem*)lvi)->fileName().isEmpty() ) {
    vli = (ValListViewItem*)lvi;
  } else if ( lvi->isExpandable() ) {
    // find the memleak position
    TQListViewItemIterator it( lv );
    while ( vli == 0 && it.current() ) {
      if ( it.current()->rtti() == VALLISTVIEWITEMRTTI && ((ValListViewItem*)it.current())->isHighlighted() )
          vli = (ValListViewItem*)it.current();
      ++it;
    }
  }
  if ( vli ) {
    // display the file
    _part->partController()->editDocument( KURL( vli->fileName() ), vli->line() - 1 );
    _part->mainWindow()->statusBar()->message( vli->message(), 10000 );
  }
}

void ValgrindWidget::expandAll()
{
  TQListViewItem* child = lv->firstChild();
  while ( child ) {
    child->setOpen( true );
    child = child->nextSibling();
  }
}

void ValgrindWidget::collapseAll()
{
  TQListViewItem* child = lv->firstChild();
  while ( child ) {
    child->setOpen( false );
    child = child->nextSibling();
  }
}

void ValgrindWidget::aboutToShowPopup()
{
  bool en = (lv->firstChild() != 0);
  popup->setItemEnabled( 2, en );
  popup->setItemEnabled( 3, en );
}

void ValgrindWidget::slotContextMenu( TDEListView* l, TQListViewItem* /*i*/, const TQPoint& p )
{
  if ( l != lv )
    return;

  popup->exec( p );
}

#include "valgrind_widget.moc"
