/*
**************************************************************************
                                 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 "pmcsg.h"

#include <klocale.h>
#include "pmoutputdevice.h"
#include "pmxmlhelper.h"
#include "pmcsgedit.h"
#include "pmmemento.h"

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

PMCSG::PMCSG( )
      : Base( )
{
   m_type = CSGUnion;
}

PMCSG::PMCSG( const PMCSGType t )
      : Base( )
{
   m_type = t;
}

PMCSG::~PMCSG( )
{
}

QString PMCSG::description( ) const
{
   switch( m_type )
   {
      case CSGUnion:
         return i18n( "union" );
         break;
      case CSGIntersection:
         return i18n( "intersection" );
         break;
      case CSGDifference:
         return i18n( "difference" );
         break;
      case CSGMerge:
         return i18n( "merge" );
         break;
   }
   return QString( "" );
}

QString PMCSG::pixmap( ) const
{
   switch( m_type )
   {
      case CSGUnion:
         return QString( "pmunion" );
         break;
      case CSGIntersection:
         return QString( "pmintersection" );
         break;
      case CSGDifference:
         return QString( "pmdifference" );
         break;
      case CSGMerge:
         return QString( "pmmerge" );
         break;
   }
   return QString( "" );
}

void PMCSG::countChild( PMObjectType t, bool& modifiersBefore,
                        bool& objectsBehind, bool afterInsertPoint ) const
{
   switch( t )
   {
      case PMTComment:
      case PMTRaw:
         break;
      case PMTTexture:
      case PMTPigment:
      case PMTNormal:
      case PMTFinish:
      case PMTInterior:
      case PMTMaterial:
      case PMTScale:
      case PMTRotate:
      case PMTTranslate:
      case PMTMatrix:
      case PMTBoundedBy:
      case PMTClippedBy:
         if( !afterInsertPoint )
            modifiersBefore = true;
         break;
      default:
         if( afterInsertPoint )
            objectsBehind = true;
         break;
   }
}

bool PMCSG::canInsert( PMObjectType t, bool modifiersBefore,
                       bool objectsBehind ) const
{
   switch( t )
   {
      case PMTComment:
      case PMTRaw:
         return true;
      case PMTBox:
      case PMTSphere:
      case PMTCylinder:
      case PMTCone:
      case PMTTorus:
      case PMTLathe:
      case PMTPrism:
      case PMTBlob:
      case PMTSurfaceOfRevolution:
      case PMTSuperquadricEllipsoid:
      case PMTJuliaFractal:
      case PMTHeightField:
      case PMTText:
      case PMTPlane:
      case PMTPolynom:
      case PMTDisc:
      case PMTBicubicPatch:
      case PMTTriangle:
      case PMTCSG:
      case PMTLight:
      case PMTObjectLink:
         return !modifiersBefore;
      case PMTTexture:
      case PMTPigment:
      case PMTNormal:
      case PMTFinish:
      case PMTInterior:
      case PMTMaterial:
      case PMTScale:
      case PMTRotate:
      case PMTTranslate:
      case PMTMatrix:
      case PMTBoundedBy:
      case PMTClippedBy:
         return !objectsBehind;
      default:
         break;
   }
   return false;
}

bool PMCSG::canInsert( PMObjectType t, const PMObject* after,
                       const PMObjectList* objectsBetween ) const
{
   bool afterInsertPoint = false;
   bool objectsBehind = false, modifiersBefore = false;
   PMObject* o;
   
   if( !after )
      afterInsertPoint = true;
   for( o = firstChild( ); o; o = o->nextSibling( ) )
   {
      countChild( o->type( ), modifiersBefore, objectsBehind,
                  afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }

   if( objectsBetween )
   {
      PMObjectListIterator it( *objectsBetween );
      for( ; it.current( ); ++it )
         countChild( it.current( )->type( ), modifiersBefore, objectsBehind,
                     false );
   }

   return canInsert( t, modifiersBefore, objectsBehind );
}

int PMCSG::canInsert( const QValueList<PMObjectType>& list,
                      const PMObject* after ) const
{
   bool afterInsertPoint = false;
   bool objectsBehind = false, modifiersBefore = false;
   QValueList<PMObjectType>::ConstIterator it;
   PMObject* o;
   PMObjectType t;
   int numInserts = 0;

   if( !after )
      afterInsertPoint = true;
   for( o = firstChild( ); o; o = o->nextSibling( ) )
   {
      countChild( o->type( ), modifiersBefore, objectsBehind,
                  afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }
   for( it = list.begin( ); it != list.end( ); ++it )
   {
      t = *it;
      if( canInsert( t, modifiersBefore, objectsBehind ) )
         numInserts++;
      countChild( t, modifiersBefore, objectsBehind, false );
   }
   return numInserts;
}

int PMCSG::canInsert( const PMObjectList& list,
                      const PMObject* after ) const
{
   bool afterInsertPoint = false;
   bool objectsBehind = false, modifiersBefore = false;
   PMObjectListIterator it( list );
   PMObject* o;
   PMObjectType t;
   int numInserts = 0;

   if( !after )
      afterInsertPoint = true;
   for( o = firstChild( ); o; o = o->nextSibling( ) )
   {
      countChild( o->type( ), modifiersBefore, objectsBehind,
                  afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }
   for( ; it.current( ); ++it )
   {
      t = it.current( )->type( );
      if( canInsert( t, modifiersBefore, objectsBehind ) )
         numInserts++;
      countChild( t, modifiersBefore, objectsBehind, false );
   }
   return numInserts;
}

void PMCSG::serialize( PMOutputDevice& dev ) const
{
   switch( m_type )
   {
      case CSGUnion:
         dev.objectBegin( "union" );
         break;
      case CSGIntersection:
         dev.objectBegin( "intersection" );
         break;
      case CSGDifference:
         dev.objectBegin( "difference" );
         break;
      case CSGMerge:
         dev.objectBegin( "merge" );
         break;
   }
   
   serializeName( dev );   
   Base::serialize( dev );
   dev.objectEnd( );
}

void PMCSG::serialize( QDomElement& e, QDomDocument& doc ) const
{
   switch( m_type )
   {
      case CSGUnion:
         e.setAttribute( "csgtype", "union" );
         break;
      case CSGIntersection:
         e.setAttribute( "csgtype", "intersection" );
         break;
      case CSGDifference:
         e.setAttribute( "csgtype", "difference" );
         break;
      case CSGMerge:
         e.setAttribute( "csgtype", "merge" );
         break;
   }
   
   Base::serialize( e, doc );   
}

void PMCSG::readAttributes( const PMXMLHelper& h )
{
   QString str = h.stringAttribute( "csgtype", "union" );
   if( str == "union" )
      m_type = CSGUnion;
   else if( str == "intersection" )
      m_type = CSGIntersection;
   else if( str == "difference" )
      m_type = CSGDifference;
   else
      m_type = CSGMerge;
   
   Base::readAttributes( h );
}

bool PMCSG::isA( PMObjectType t ) const
{
   if( t == PMTCSG )
      return true;
   return Base::isA( t );
}

void PMCSG::setCSGType( const PMCSGType t )
{
   if( t != m_type )
   {
      if( m_pMemento )
      {
         m_pMemento->addData( PMTCSG, PMTypeID, ( int ) m_type );
         m_pMemento->setDescriptionChanged( );
      }
      m_type = t;
   }
}

PMDialogEditBase* PMCSG::editWidget( QWidget* parent ) const
{
   return new PMCSGEdit( parent );
}

void PMCSG::restoreMemento( PMMemento* s )
{
   PMMementoDataIterator it( s );
   PMMementoData* data;

   for( ; it.current( ); ++it )
   {
      data = it.current( );
      if( data->objectType( ) == PMTCSG )
      {
         switch( data->valueID( ) )
         {
            case PMTypeID:
               setCSGType( ( PMCSGType ) data->intData( ) );
               break;
            default:
               kdError( PMArea ) << "Wrong ID in PMCSG::restoreMemento\n";
               break;
         }
      }
   }
   Base::restoreMemento( s );
}

