//Author:    Max Howell <max.howell@methylblue.com>, (C) 2003-4
//Copyright: See COPYING file that comes with this distribution

#include "fileTree.h"
#include "radialMap.h"   //class Segment
#include "widget.h"

#include <cmath>         //::segmentAt()
#include <kcursor.h>     //::mouseMoveEvent()
#include <kiconeffect.h> //::mousePressEvent()
#include <kiconloader.h> //::mousePressEvent()
#include <kio/job.h>     //::mousePressEvent()
#include <klocale.h>
#include <kmessagebox.h> //::mousePressEvent()
#include <kpopupmenu.h>  //::mousePressEvent()
#include <krun.h>        //::mousePressEvent()
#include <kurldrag.h>
#include <tqapplication.h>//TQApplication::setOverrideCursor()
#include <tqclipboard.h>
#include <tqpainter.h>
#include <tqtimer.h>      //::resizeEvent()



void
RadialMap::Widget::resizeEvent( TQResizeEvent* )
{
    if( m_map.resize( rect() ) )
       m_timer.start( 500, true ); //will cause signature to rebuild for new size

    //always do these as they need to be initialised on creation
    m_offset.rx() = (width() - m_map.width()) / 2;
    m_offset.ry() = (height() - m_map.height()) / 2;
}

void
RadialMap::Widget::paintEvent( TQPaintEvent* )
{
    //bltBit for some TQt setups will bitBlt _after_ the labels are painted. Which buggers things up!
    //shame as bitBlt is faster, possibly TQt bug? Should report the bug? - seems to be race condition
    //bitBlt( this, m_offset, &m_map );

    TQPainter paint( this );

    paint.drawPixmap( m_offset, m_map );

    //vertical strips
    if( m_map.width() < width() )
    {
        paint.eraseRect( 0, 0, m_offset.x(), height() );
        paint.eraseRect( m_map.width() + m_offset.x(), 0, m_offset.x() + 1, height() );
    }
    //horizontal strips
    if( m_map.height() < height() )
    {
        paint.eraseRect( 0, 0, width(), m_offset.y() );
        paint.eraseRect( 0, m_map.height() + m_offset.y(), width(), m_offset.y() + 1 );
    }

    //exploded labels
    if( !m_map.isNull() && !m_timer.isActive() )
       paintExplodedLabels( paint );
}

const RadialMap::Segment*
RadialMap::Widget::segmentAt( TQPoint &e ) const
{
    //determine which segment TQPoint e is above

    e -= m_offset;

    if( !m_map.m_signature )
       return 0;

    if( e.x() <= m_map.width() && e.y() <= m_map.height() )
    {
        //transform to cartesian coords
        e.rx() -= m_map.width() / 2; //should be an int
        e.ry()  = m_map.height() / 2 - e.y();

        double length = hypot( e.x(), e.y() );

        if( length >= m_map.m_innerRadius ) //not hovering over inner circle
        {
            uint depth  = ((int)length - m_map.m_innerRadius) / m_map.m_ringBreadth;

            if( depth <= m_map.m_visibleDepth ) //**** do earlier since you can //** check not outside of range
            {
                //vector calculation, reduces to simple trigonometry
                //cos angle = (aibi + ajbj) / albl
                //ai = x, bi=1, aj=y, bj=0
                //cos angle = x / (length)

                uint a  = (uint)(acos( (double)e.x() / length ) * 916.736);  //916.7324722 = #radians in circle * 16

                //acos only understands 0-180 degrees
                if( e.y() < 0 ) a = 5760 - a;

                #define ring (m_map.m_signature + depth)
                for( ConstIterator<Segment> it = ring->constIterator(); it != ring->end(); ++it )
                    if( (*it)->intersects( a ) )
                        return *it;
                #undef ring
            }
        }
        else return m_rootSegment; //hovering over inner circle
    }

    return 0;
}

void
RadialMap::Widget::mouseMoveEvent( TQMouseEvent *e )
{
   //set m_focus to what we hover over, update UI if it's a new segment

   Segment const * const oldFocus = m_focus;
   TQPoint p = e->pos();

   m_focus = segmentAt( p ); //NOTE p is passed by non-const reference

   if( m_focus && m_focus->file() != m_tree )
   {
      if( m_focus != oldFocus ) //if not same as last time
      {
         setCursor( KCursor::handCursor() );
         m_tip->updateTip( m_focus->file(), m_tree );
         emit mouseHover( m_focus->file()->fullPath() );

         //repaint required to update labels now before transparency is generated
         repaint( false );
      }

      m_tip->moveTo( e->globalPos(), *this, ( p.y() < 0 ) ); //updates tooltip psuedo-tranparent background
   }
   else if( oldFocus && oldFocus->file() != m_tree )
   {
      unsetCursor();
      m_tip->hide();
      update();

      emit mouseHover( TQString() );
   }
}

void
RadialMap::Widget::mousePressEvent( TQMouseEvent *e )
{
   //m_tip is hidden already by event filter
   //m_focus is set correctly (I've been strict, I assure you it is correct!)

   enum { Konqueror, Konsole, Center, Open, Copy, Delete };

   if (m_focus && !m_focus->isFake())
   {
      const KURL url   = Widget::url( m_focus->file() );
      const bool isDir = m_focus->file()->isDirectory();

      if( e->button() == Qt::RightButton )
      {
         KPopupMenu popup;
         popup.insertTitle( m_focus->file()->fullPath( m_tree ) );

         if (isDir) {
            popup.insertItem( SmallIconSet( "konqueror" ), i18n( "Open &Konqueror Here" ), Konqueror );

            if( url.protocol() == "file" )
               popup.insertItem( SmallIconSet( "konsole" ), i18n( "Open &Konsole Here" ), Konsole );

            if (m_focus->file() != m_tree) {
               popup.insertSeparator();
               popup.insertItem( SmallIconSet( "viewmag" ), i18n( "&Center Map Here" ), Center );
            }
         }
         else
            popup.insertItem( SmallIconSet( "fileopen" ), i18n( "&Open" ), Open );

         popup.insertSeparator();
         popup.insertItem( SmallIconSet( "editcopy" ), i18n( "&Copy to clipboard" ), Copy );

         popup.insertSeparator();
         popup.insertItem( SmallIconSet( "editdelete" ), i18n( "&Delete" ), Delete );

         switch (popup.exec( e->globalPos(), 1 )) {
            case Konqueror:
                //KRun::runCommand will show an error message if there was trouble
                KRun::runCommand( TQString( "kfmclient openURL \"%1\"" ).arg( url.url() ) );
                break;

            case Konsole:
                // --workdir only works for local file paths
                KRun::runCommand( TQString( "konsole --workdir \"%1\"" ).arg( url.path() ) );
                break;

            case Center:
            case Open:
                goto section_two;

            case Copy:
                TQApplication::clipboard()->setData( new KURLDrag( KURL::List( url ) ) );
                break;

            case Delete:
            {
                const KURL url = Widget::url( m_focus->file() );
                const TQString message = m_focus->file()->isDirectory()
                        ? i18n( "<qt>The directory at <i>'%1'</i> will be <b>recursively</b> and <b>permanently</b> deleted." )
                        : i18n( "<qt><i>'%1'</i> will be <b>permanently</b> deleted." );
                const int userIntention = KMessageBox::warningContinueCancel(
                        this, message.arg( url.prettyURL() ),
                        TQString(), KGuiItem( i18n("&Delete"), "editdelete" ) );

                if (userIntention == KMessageBox::Continue) {
                    KIO::Job *job = KIO::del( url );
                    job->setWindow( this );
                    connect( job, TQT_SIGNAL(result( KIO::Job* )), TQT_SLOT(deleteJobFinished( KIO::Job* )) );
                    TQApplication::setOverrideCursor( KCursor::workingCursor() );
                }
            }

            default:
                //ensure m_focus is set for new mouse position
                sendFakeMouseEvent();
         }
      }
      else { // not right mouse button

      section_two:
         const TQRect rect( e->x() - 20, e->y() - 20, 40, 40 );

         m_tip->hide(); // user expects this

         if (!isDir || e->button() == Qt::MidButton) {
            KIconEffect::visualActivate( this, rect );
            new KRun( url, this, true ); //FIXME see above
         }
         else if (m_focus->file() != m_tree) { // is left click
            KIconEffect::visualActivate( this, rect );
            emit activated( url ); //activate first, this will cause UI to prepare itself
            createFromCache( (Directory *)m_focus->file() );
         }
         else
            emit giveMeTreeFor( url.upURL() );
      }
   }
}

void
RadialMap::Widget::deleteJobFinished( KIO::Job *job )
{
   TQApplication::restoreOverrideCursor();
   if( !job->error() )
      invalidate();
   else
      job->showErrorDialog( this );
}

#include "debug.h"
void
RadialMap::Widget::dropEvent( TQDropEvent *e )
{
    DEBUG_ANNOUNCE

    KURL::List urls;
    if (KURLDrag::decode( e, urls ) && urls.count())
        emit giveMeTreeFor( urls.first() );
}

void
RadialMap::Widget::dragEnterEvent( TQDragEnterEvent *e )
{
    DEBUG_ANNOUNCE

    e->accept( KURLDrag::canDecode( e ) );
}
