/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2002 by Luis Carvalho
    email                : lpassos@mail.telepac.tp
**************************************************************************

**************************************************************************
*                                                                        *
*  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 "pmdensity.h"
#include "pmoutputdevice.h"
#include "pmxmlhelper.h"
#include "pmmemento.h"
#include "pmdensityedit.h"

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

bool PMDensity::s_linkPossibilitiesCreated = false;
QValueList<PMDeclare::PMDeclareType> PMDensity::s_linkPossibilities;

PMDensity::PMDensity( )
      : Base( )
{
}

PMDensity::~PMDensity( )
{
}

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

QString PMDensity::description( ) const
{
   return i18n( "density" );
}


void PMDensity::serialize( PMOutputDevice& dev ) const
{
   bool object = true;
   if( parent( ) )
      if( parent( )->type( ) == PMTDensityMap )
         object = false;
   
   if( object )
      dev.objectBegin( "density" );
   Base::serialize( dev );
   if( object )
      dev.objectEnd( );
}


void PMDensity::countChild( PMObjectType t,
                            int& color, int& listpattern, int& pattern,
                            int& map, int& warp, int& blend,
                            int& summap, int& sum,
                            bool& afterPattern, bool& afterMap,
                            bool& afterWarp, bool& afterBlend,
                            bool& afterTransform, bool afterInsertPoint ) const
{
   switch( t )
   {
      case PMTSolidColor:
         color++;
         break;
      case PMTColorList:
      case PMTDensityList:
         listpattern++;
         break;
      case PMTPattern:
         if( !afterInsertPoint )
            afterPattern = true;
         pattern++;
         break;
      case PMTWarp:
         if( !afterInsertPoint )
            afterWarp = true;
         warp++;
         break;
      case PMTColorMap:
      case PMTDensityMap:
         if( !afterInsertPoint )
            afterMap = true;
         map++;
         break;
      case PMTBlendMapModifiers:
         if( !afterInsertPoint )
            afterBlend = true;
         blend++;
         break;
      case PMTScale:
      case PMTRotate:
      case PMTTranslate:
      case PMTMatrix:
         if( !afterInsertPoint )
            afterTransform = true;
         break;
      default:
         break;
   }
   summap = pattern + map + warp;
   sum = summap + color + listpattern;
}

bool PMDensity::canInsert( PMObjectType t,
                           int color, int listpattern, int pattern,
                           int map, int /*warp*/, int blend,
                           int /*sumap*/, int sum,
                           bool afterPattern, bool afterMap,
                           bool afterWarp, bool afterBlend, 
                           bool afterTransform ) const
{
   switch( t )
   {
      case PMTSolidColor:
      case PMTColorList:
      case PMTDensityList:
         if( sum == 0 )
            return true;
         break;
      case PMTPattern:
         if( ( ( pattern + listpattern ) == 0 )
             && !afterMap && !afterWarp && !afterBlend 
             && !afterTransform )
            return true;
         break;
      case PMTWarp:
         if( pattern && afterPattern )
            return true;
         break;
      case PMTColorMap:
      case PMTDensityMap:
         if( ( ( map + listpattern + color ) == 0 )
             && ( !pattern || afterPattern ) 
             && !afterBlend )
            return true;
         break;
      case PMTBlendMapModifiers:
         if( !blend && ( !map || afterMap )
             && ( !pattern || afterPattern )
             && !afterTransform )
            return true;
         break;
      case PMTQuickColor:
      case PMTScale:
      case PMTRotate:
      case PMTTranslate:
      case PMTMatrix:
         if( !pattern || afterPattern )
            return true;
      case PMTComment:
      case PMTRaw:
         return true;
      default:
         break;
   }
   return false;
}

bool PMDensity::canInsert( PMObjectType t, const PMObject* after,
                           const PMObjectList* objectsBetween ) const
{
   int color = 0, listpattern = 0, pattern = 0, map = 0, warp = 0;
   int summap = 0, sum = 0, blend = 0;
   bool afterPattern = false, afterMap = false, afterWarp = false;
   bool afterBlend = false, afterTransform = false;
   bool afterInsertPoint = false;
   PMObject* o;
   
   // count child objects
   if( !after )
      afterInsertPoint = true;
   for( o = firstChild( ); o; o = o->nextSibling( ) )
   {
      countChild( o->type( ), color, listpattern, pattern, map,
                  warp, blend, summap, sum, afterPattern, afterMap,
                  afterWarp, afterBlend, afterTransform, afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }
   if( objectsBetween )
   {
      PMObjectListIterator it( *objectsBetween );
      for( ; it.current( ); ++it )
         countChild( it.current( )->type( ), color, listpattern,
                     pattern, map, warp, blend, summap, sum, afterPattern,
                     afterMap, afterWarp, afterBlend, afterTransform, false );
   }
   
   return canInsert( t, color, listpattern, pattern, map,
                     warp, blend, summap, sum, afterPattern, afterMap,
                     afterWarp, afterBlend, afterTransform );
}

int PMDensity::canInsert( const QValueList<PMObjectType>& list,
                          const PMObject* after ) const
{
   int color = 0, listpattern = 0, pattern = 0, map = 0, warp = 0;
   int summap = 0, sum = 0, blend = 0;
   bool afterPattern = false, afterMap = false, afterWarp = false;
   bool afterBlend = false, afterTransform = false;
   bool afterInsertPoint = false;

   QValueList<PMObjectType>::ConstIterator it;
   PMObject* o = firstChild( );
   PMObjectType t;
   int numInserts = 0;

   // count child objects
   if( !after )
      afterInsertPoint = true;
   for( o = firstChild( ); o; o = o->nextSibling( ) )
   {
      countChild( o->type( ), color, listpattern, pattern, map,
                  warp, blend, summap, sum, afterPattern, afterMap,
                  afterWarp, afterBlend, afterTransform, afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }

   for( it = list.begin( ); it != list.end( ); ++it )
   {
      t = *it;
      if( canInsert( t, color, listpattern, pattern, map, warp, blend, summap, 
                     sum, afterPattern, afterMap, afterWarp, afterBlend,
                     afterTransform ) )
         numInserts++;
      
      countChild( t, color, listpattern, pattern, map,
                  warp, blend, summap, sum, afterPattern, afterMap,
                  afterWarp, afterBlend, afterTransform, false );
   }
   
   return numInserts;
}

int PMDensity::canInsert( const PMObjectList& list,
                          const PMObject* after ) const
{
   int color = 0, listpattern = 0, pattern = 0, map = 0, warp = 0;
   int summap = 0, sum = 0, blend = 0;
   bool afterPattern = false, afterMap = false, afterWarp = false;
   bool afterBlend = false, afterTransform = false;
   bool afterInsertPoint = false;
   PMObjectListIterator it( list );
   PMObject* o;
   PMObjectType t;
   int numInserts = 0;
   
   // count child objects
   if( !after )
      afterInsertPoint = true;
   for( o = firstChild( ); o; o = o->nextSibling( ) )
   {
      countChild( o->type( ), color, listpattern, pattern, map,
                  warp, blend, summap, sum, afterPattern, afterMap,
                  afterWarp, afterBlend, afterTransform, afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }
   
   for( ; it.current( ); ++it )
   {
      t = it.current( )->type( );
      if( canInsert( t, color, listpattern, pattern, map, warp, blend, summap, 
                     sum, afterPattern, afterMap, afterWarp, afterBlend, 
                     afterTransform ) )
         numInserts++;
      
      countChild( t, color, listpattern, pattern, map,
                  warp, blend, summap, sum, afterPattern, afterMap,
                  afterWarp, afterBlend, afterTransform, false );
   }
   
   return numInserts;
}


QValueList<PMDeclare::PMDeclareType> PMDensity::linkPossibilities( ) const
{
   if( !s_linkPossibilitiesCreated )
   {
      s_linkPossibilities.append( PMDeclare::DensityDeclare );
      s_linkPossibilitiesCreated = true;
   }
   return s_linkPossibilities;
}

PMDialogEditBase* PMDensity::editWidget( QWidget* parent ) const
{
   return new PMDensityEdit( parent );
}

