/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2000-2002 by Andreas Zehender
    email                : zehender@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 "pmglviewconnector.h"
#include "pmglview.h"
#include "pmpart.h"
#include "pmcamera.h"
#include "pmdatachangecommand.h"
#include "pmactions.h"
#include "pmrendermanager.h"

#include <kxmlguifactory.h>
#include <kaction.h>
#include <kstdaction.h>
#include <klocale.h>
#include <kpopupmenu.h>

#include <kdebug.h>
#include "pmglobals.h"

class PMCameraID
{
public:
   PMCameraID( PMCamera* c, int id ) { m_pCamera = c; m_id = id; }
   PMCamera* camera( ) const { return m_pCamera; }
   int id( ) const { return m_id; }
private:
   PMCamera* m_pCamera;
   int m_id;
};

class PMControlPointID
{
public:
   PMControlPointID( PMControlPoint* c, int id ) { m_pControlPoint = c; m_id = id; }
   PMControlPoint* controlPoint( ) const { return m_pControlPoint; }
   int id( ) const { return m_id; }
private:
   PMControlPoint* m_pControlPoint;
   int m_id;
};

PMGLViewConnector::PMGLViewConnector( PMPart* part, QObject* parent, const char* name )
      : QObject( parent, name )
{
   m_pPart = part;
   m_pActiveView = 0;

   if( PMRenderManager::hasOpenGL( ) )
   {
      // actions
      KActionCollection* ac = part->actionCollection( );
      m_pTranslateViewAction = new KToggleAction( i18n( "Translate Views" ), 0, this,
                                                  SLOT( slotTranslateView( ) ),
                                                  ac, "view_translate" );   
      m_pScaleViewAction = new KToggleAction( i18n( "Scale Views" ), 0, this,
                                              SLOT( slotScaleView( ) ),
                                              ac, "view_scale" );

      m_pViewPosXAction = new KAction( i18n( "Left View"), 0, this,
                                       SLOT( slotPosX( ) ), ac,
                                       "view_pos_x" );
      m_pViewPosXAction = new KAction( i18n( "Right View"), 0, this,
                                       SLOT( slotNegX( ) ), ac,
                                       "view_neg_x" );
      m_pViewPosYAction = new KAction( i18n( "Bottom View"), 0, this,
                                       SLOT( slotPosY( ) ), ac,
                                       "view_pos_y" );
      m_pViewPosYAction = new KAction( i18n( "Top View"), 0, this,
                                       SLOT( slotNegY( ) ), ac,
                                       "view_neg_y" );
      m_pViewPosZAction = new KAction( i18n( "Front View"), 0, this,
                                       SLOT( slotPosZ( ) ), ac,
                                       "view_pos_z" );
      m_pViewPosZAction = new KAction( i18n( "Back View"), 0, this,
                                       SLOT( slotNegZ( ) ), ac,
                                       "view_neg_z" );
      
      m_pCamerasAction = new KActionMenu( i18n( "Camera" ), "pmcamera",
                                          ac, "view_cameras_menu" );
      m_pControlPointsAction = new KActionMenu( i18n( "Control Points" ),
                                          ac, "view_control_points_menu" );

      m_pRepaintAction = KStdAction::redisplay( this, SLOT( slotRepaint( ) ), ac );

      m_pSnapToGridAction = new KAction( i18n( "Snap to Grid" ), 0, this,
                                         SLOT( slotSnapToGrid( ) ), ac,
                                         "cp_snaptogrid" );
      m_pSnapToGridAction->setEnabled( false );

      KPopupMenu* menu = m_pCamerasAction->popupMenu( );
      connect( menu, SIGNAL( aboutToShow( ) ),
               SLOT( slotCamerasMenuAboutToShow( ) ) );
      connect( menu, SIGNAL( activated( int ) ), SLOT( slotCamera( int ) ) );
      menu = m_pControlPointsAction->popupMenu( );
      connect( menu, SIGNAL( aboutToShow( ) ),
               SLOT( slotControlPointsMenuAboutToShow( ) ) );
      connect( menu, SIGNAL( activated( int ) ),
               SLOT( slotControlPoint( int ) ) );
   }
   else
   {
      m_pTranslateViewAction = 0;
      m_pScaleViewAction = 0;
      m_pCamerasAction = 0;
      m_pControlPointsAction = 0;
      m_pViewPosXAction = 0;
      m_pViewNegXAction = 0;
      m_pViewPosYAction = 0;
      m_pViewNegYAction = 0;
      m_pViewPosZAction = 0;
      m_pViewNegZAction = 0;
      m_pRepaintAction = 0;
      m_pSnapToGridAction = 0;
   }
   
   m_bScale = false;
   m_bTranslate = false;

   m_controlPoints.setAutoDelete( true );
   m_controlPointsMenuUpToDate = false;
   m_pActiveObject = 0;

   connect( part, SIGNAL( objectChanged( PMObject*, const int, QObject* ) ),
            SLOT( slotObjectChanged( PMObject*, const int, QObject* ) ) );
   connect( part, SIGNAL( clear( ) ), SLOT( slotClear( ) ) );

   m_cameraIDs.setAutoDelete( true );
   m_controlPointIDs.setAutoDelete( true );
   m_objectActions.setAutoDelete( true );
}

PMGLViewConnector::~PMGLViewConnector( )
{
}

void PMGLViewConnector::addGLView( PMGLView* v )
{
   m_GLViews.append( v );
   if( !m_pActiveView )
      m_pActiveView = v;
}

void PMGLViewConnector::removeGLView( PMGLView* v )
{
   kdDebug( PMArea ) << "PMGLViewConnector: GLView destroyed\n";
   m_GLViews.removeRef( v );
   if( m_pActiveView == v )
      m_pActiveView = 0;
}

void PMGLViewConnector::slotObjectChanged( PMObject* obj, const int mode, QObject* sender )
{
   bool changeControlPoints = false;
   PMObject* oldActive = m_pActiveObject;

   if( mode & PMCNewSelection )
   {
      if( obj )
      {
         if( obj != m_pActiveObject )
         {
            m_pActiveObject = obj;
            changeControlPoints = true;
         }
      }
      else
      {
         changeControlPoints = true;
         m_pActiveObject = 0;
      }
   }
   if( mode & ( PMCSelected | PMCDeselected ) )
   {
      m_pActiveObject = 0;
      changeControlPoints = true;
   }
   if( mode & PMCViewStructure )
   {
      if( obj == m_pActiveObject )
         changeControlPoints = true;
   }

   if( changeControlPoints )
   {
      emit stopRendering( );

      PMControlPointList newCPs;
      
      if( m_pActiveObject )
      {
         m_pActiveObject->controlPoints( newCPs );
         if( newCPs.count( ) > 0 )
            m_activeTransformation = m_pActiveObject->transformedWith( );

         if( m_pActiveObject == oldActive )
         {
            // check if the control points are the same
            bool same = true;
            PMControlPointListIterator oit( m_controlPoints );
            PMControlPointListIterator nit( newCPs );
            while( same && oit.current( ) && nit.current( ) )
            {
               if( oit.current( )->id( ) != nit.current( )->id( ) )
                  same = false;
               ++oit;
               ++nit;
            }
            if( oit.current( ) || nit.current( ) )
               same = false;
            if( same )
            {
               // set the same selection
               oit.toFirst( );
               nit.toFirst( );
               while( oit.current( ) && nit.current( ) )
               {
                  nit.current( )->setSelected( oit.current( )->selected( ) );
                  ++oit;
                  ++nit;
               }
            }
         }
      }

      m_controlPoints.clear( );
      m_controlPoints = newCPs;
      m_controlPointsMenuUpToDate = false;
      emit newControlPoints( m_controlPoints, m_activeTransformation );
      slotControlPointSelectionChanged( this );
   }
   
   emit glViewObjectChanged( obj, mode, sender );
}

void PMGLViewConnector::slotControlPointSelectionChanged( QObject* sender )
{
   int activeControlPoints = 0;
   
   if( m_pActiveObject )
   {
      PMControlPointListIterator it( m_controlPoints );
      for( ; it.current( ); ++it )
         if( it.current( )->selected( ) ||
             ( it.current( )->displayType( ) == PMControlPoint::CPCross ) )
            activeControlPoints++;
   }
   if( m_pSnapToGridAction )
      m_pSnapToGridAction->setEnabled( activeControlPoints > 0 );
   emit controlPointSelectionChanged( sender );
}

void PMGLViewConnector::slotTranslateView( )
{
   PMGLView* glView;
   
   if( m_bTranslate )
   {
      m_bTranslate = false;
      m_bScale = false;
   }
   else
   {
      m_bScale = false;
      m_bTranslate = true;
   }
   m_pTranslateViewAction->setChecked( m_bTranslate );
   m_pScaleViewAction->setChecked( m_bScale );

   for( glView = m_GLViews.first( ); glView; glView = m_GLViews.next( ) )
      glView->enableTranslateMode( m_bTranslate );
}

void PMGLViewConnector::slotScaleView( )
{
   PMGLView* glView;
   
   if( m_bScale )
   {
      m_bScale = false;
      m_bTranslate = false;
   }
   else
   {
      m_bScale = true;
      m_bTranslate = false;
   }
   m_pTranslateViewAction->setChecked( m_bTranslate );
   m_pScaleViewAction->setChecked( m_bScale );

   for( glView = m_GLViews.first( ); glView; glView = m_GLViews.next( ) )
      glView->enableScaleMode( m_bScale );
}

void PMGLViewConnector::slotClear( )
{
   m_pActiveObject = 0;
}

void PMGLViewConnector::slotViewActivated( PMGLView* view )
{
   m_pActiveView = view;
}

void PMGLViewConnector::slotCamerasMenuAboutToShow( )
{
   KPopupMenu* menu = m_pCamerasAction->popupMenu( );
   QPtrListIterator<PMCamera> it = m_pPart->cameras( );
   PMCamera* camera;
   int id;
   QString name;
   
   m_cameraIDs.clear( );
   menu->clear( );
   
   if( !it.current( ) )
      menu->insertItem( i18n( "No Cameras" ) );
   else
   {
      for( ; it.current( ); ++it )
      {
         camera = it.current( );
         name = camera->name( );
         if( name.isEmpty( ) )
            name = i18n( "(unnamed)" );
         id = menu->insertItem( name );
         m_cameraIDs.append( new PMCameraID( camera, id ) );
      }
   }
}

void PMGLViewConnector::slotContextMenuAboutToShow( )
{
   m_pPart->unplugActionList( "cp_commands" );
   QPtrListIterator<PMObjectAction> it( m_objectActions );

   for( ; it.current( ); ++it )
      if( it.current( )->action( ) )
         delete( it.current( )->action( ) );

   m_objectActions.clear( );
   if( m_pActiveObject )
   {
      m_pActiveObject->addObjectActions( m_controlPoints, m_objectActions );
      KActionCollection* ac = m_pPart->actionCollection( );
      
      if( !m_objectActions.isEmpty( ) )
      {
         PMObjectAction* oa;
         KAction* action;
         QPtrList<KAction> actionList;
         
         for( it.toFirst( ); it.current( ); ++it )
         {
            oa = it.current( );
            action = new KAction( oa->description( ), 0,
                                  this, SLOT( slotObjectAction( ) ), ac );
            actionList.append( action );
            oa->setAction( action );
            action->setEnabled( oa->isEnabled( ) );
         }

         m_pPart->plugActionList( "cp_commands", actionList );
      }
   }
}

void PMGLViewConnector::slotObjectAction( )
{
   KAction* action = ( KAction* ) sender( );
   if( action && m_pActiveObject && m_pActiveView )
   {
      // find called action
      QPtrListIterator<PMObjectAction> it( m_objectActions );
      PMObjectAction* oa = 0;
      
      for( ; it.current( ) && !oa; ++it )
         if( it.current( )->action( ) == action )
            oa = it.current( );
      if( oa )
      {
         // execute action
         m_pActiveObject->createMemento( );
         
         m_pActiveObject->objectActionCalled( oa, m_controlPoints,
                                      m_pActiveView->controlPointsPosition( ),
                                      m_pActiveView->contextClickPosition( ) );
         
         PMDataChangeCommand* cmd;
         cmd = new PMDataChangeCommand( m_pActiveObject->takeMemento( ) );
         cmd->setText( oa->description( ) );
         m_pPart->executeCommand( cmd );
      }
   }
}
      
void PMGLViewConnector::slotCamera( int id )
{
   if( m_pActiveView && !m_cameraIDs.isEmpty( ) )
   {
      QPtrListIterator<PMCameraID> it( m_cameraIDs );
      PMCamera* c = 0;
      for( ; it.current( ) && !c; ++it )
         if( it.current( )->id( ) == id )
            c = it.current( )->camera( );
      if( c )
      {
         m_pActiveView->setCamera( c );
         m_pActiveView->setType( PMGLView::PMViewCamera );
      }
   }
}

void PMGLViewConnector::slotPosX( )
{
   if( m_pActiveView )
      m_pActiveView->setType( PMGLView::PMViewPosX );
}

void PMGLViewConnector::slotNegX( )
{
   if( m_pActiveView )
      m_pActiveView->setType( PMGLView::PMViewNegX );
}

void PMGLViewConnector::slotPosY( )
{
   if( m_pActiveView )
      m_pActiveView->setType( PMGLView::PMViewPosY );
}

void PMGLViewConnector::slotNegY( )
{
   if( m_pActiveView )
      m_pActiveView->setType( PMGLView::PMViewNegY );
}

void PMGLViewConnector::slotPosZ( )
{
   if( m_pActiveView )
      m_pActiveView->setType( PMGLView::PMViewPosZ );
}

void PMGLViewConnector::slotNegZ( )
{
   if( m_pActiveView )
      m_pActiveView->setType( PMGLView::PMViewNegZ );
}

void PMGLViewConnector::slotRepaint( )
{
   PMGLView* glView;
   for( glView = m_GLViews.first( ); glView; glView = m_GLViews.next( ) )
      glView->slotRefresh( );
}

void PMGLViewConnector::slotControlPointsMenuAboutToShow( )
{
   if( !m_controlPointsMenuUpToDate )
   {
      KPopupMenu* menu = m_pControlPointsAction->popupMenu( );
      PMControlPointListIterator it( m_controlPoints );
      PMControlPoint* cp;
      int id;
      
      m_controlPointIDs.clear( );
      menu->clear( );
      
      if( !it.current( ) )
         menu->insertItem( i18n( "No Control Points" ) );
      else
      {
         for( ; it.current( ); ++it )
         {
            cp = it.current( );
            id = menu->insertItem( it.current( )->description( ) );
            m_controlPointIDs.append( new PMControlPointID( cp, id ) );
         }
      }
      m_controlPointsMenuUpToDate = true;
   }
}

void PMGLViewConnector::slotControlPoint( int id )
{
   if( !m_controlPointIDs.isEmpty( ) )
   {
      QPtrListIterator<PMControlPointID> it( m_controlPointIDs );
      PMControlPoint* c = 0;
      for( ; it.current( ) && !c; ++it )
         if( it.current( )->id( ) == id )
            c = it.current( )->controlPoint( );
      if( c )
      {
         PMControlPointListIterator cit( m_controlPoints );
         for( ; cit.current( ); ++cit )
            cit.current( )->setSelected( c == cit.current( ) );
      }
   }
   slotControlPointSelectionChanged( this );
}

void PMGLViewConnector::slotSnapToGrid( )
{
   if( m_pActiveObject )
   {
      if( !m_pActiveObject->mementoCreated( ) )
         m_pActiveObject->createMemento( );

      PMControlPointListIterator it( m_controlPoints );
      for( ; it.current( ); ++it )
         if( it.current( )->selected( ) )
            it.current( )->snapToGrid( );
      
      m_pActiveObject->controlPointsChanged( m_controlPoints );

      PMDataChangeCommand* cmd;
      cmd = new PMDataChangeCommand( m_pActiveObject->takeMemento( ) );
      cmd->setText( i18n( "Snap to Grid" ) );
      m_pPart->executeCommand( cmd );
   }
}

#include "pmglviewconnector.moc"
