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

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

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

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

PMTexture::PMTexture( )
      : Base( )
{
}

PMTexture::~PMTexture( )
{
}

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

QString PMTexture::description( ) const
{
   return i18n( "texture" );
}


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


void PMTexture::countChild( PMObjectType t,
                            int& materialmap, int& pigment, int& finish, 
                            int& pattern, int& listpattern, int& map, 
                            int& summap, int& normal, int& warp, int& blend, 
                            int& sumpigment, bool& afterPattern, bool& afterMap,
                            bool& afterWarp, bool& afterBlend, 
                            bool& afterTransform, bool afterInsertPoint ) const
{
   switch( t )
   {
      case PMTMaterialMap:
         materialmap++;
         break;
      case PMTPigment:
         pigment++;
         break;
      case PMTNormal:
         normal++;
         break;
      case PMTFinish:
         finish++;
         break;
      case PMTPattern:
         if( !afterInsertPoint )
            afterPattern = true;
         pattern++;
         break;
      case PMTWarp:
         if( !afterInsertPoint )
            afterWarp = true;
         warp++;
         break;
      case PMTTextureList:
         listpattern++;
         break;
      case PMTTextureMap:
         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;
   sumpigment = pigment + normal + finish;
}

bool PMTexture::canInsert( PMObjectType t,
                           int materialmap, int pigment, int finish, 
                           int pattern, int listpattern, int map, int summap, 
                           int normal, int /*warp*/, int blend, int sumpigment, 
                           bool afterPattern, bool afterMap, bool afterWarp,
                           bool afterBlend, bool afterTransform ) const
{
   switch( t )
   {
      case PMTMaterialMap:
         //  Only one of these and nothing else can be there
         if( ( listpattern + summap + materialmap + pigment + 
               normal + finish ) == 0 )
            return true;
         break;
      case PMTPigment:
         //  Only one of this is valid and you cannot have patterns defined.
         if( ( listpattern + summap + pigment + materialmap ) == 0 )
            return true;
         break;
      case PMTNormal:
         //  Only one of this is valid and you cannot have patterns defined.
         if( ( listpattern + summap + normal + materialmap ) == 0 )
            return true;
         break;
      case PMTFinish:
         //  Only one of this is valid and you cannot have patterns defined.
         if( ( listpattern + summap + finish + materialmap ) == 0 )
            return true;
         break;
      case PMTPattern:
         // Only one of this is valid and must always be the first object
         if( ( ( pattern + listpattern + sumpigment + materialmap ) == 0 )
             && !afterMap && !afterWarp && !afterBlend && !afterTransform )
            return true;
         break;
      case PMTTextureMap:
         // Only one of this is valid and if there is a pattern
         // it has to be after a pattern
         if( ( ( map + listpattern + sumpigment + materialmap ) == 0 )
             && ( !pattern || afterPattern )
             && !afterBlend )
            return true;
         break;
      case PMTTextureList:
         // Only one of this is valid
         if( ( listpattern + summap + sumpigment + materialmap ) == 0 )
            return true;
         break;
      case PMTWarp:
         // Only if there is a pattern and has to be after a pattern
         if( pattern && afterPattern )
            return true;
         break;
      case PMTTranslate:
      case PMTRotate:
      case PMTScale:
      case PMTMatrix:
         // These can be inserted only after a pattern
         if( !pattern || afterPattern )
            return true;
      case PMTBlendMapModifiers:
         // This can only be inserted after a texture map
         if( !materialmap && !blend 
             && ( !map || afterMap )
             && ( !pattern || afterPattern )
             && !afterTransform )
            return true;
         break;
      case PMTComment:
      case PMTRaw:
         return true;
         break;
      default:
         break;
   }
   return false;
}

bool PMTexture::canInsert( PMObjectType t, const PMObject* after,
                           const PMObjectList* objectsBetween ) const
{
   int materialmap = 0, pigment = 0, finish = 0, pattern = 0, listpattern = 0;
   int map = 0, summap = 0, normal = 0, warp = 0, sumpigment = 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( ), materialmap, pigment, finish, pattern, 
                  listpattern, map, summap, normal, warp, blend, sumpigment,
                  afterPattern, afterMap, afterWarp, afterBlend,
                  afterTransform, afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }
   if( objectsBetween )
   {
      PMObjectListIterator it( *objectsBetween );
      for( ; it.current( ); ++it )
      countChild( it.current( )->type( ), materialmap, pigment, finish, pattern,
                  listpattern, map, summap, normal, warp, blend, sumpigment,
                  afterPattern, afterMap, afterWarp, afterBlend, 
                  afterTransform, false );
   }
   
   return canInsert( t, materialmap, pigment, finish, pattern,
                     listpattern, map, summap, normal, warp, blend, 
                     sumpigment, afterPattern, afterMap, afterWarp,
                     afterBlend, afterTransform );
}

int PMTexture::canInsert( const QValueList<PMObjectType>& list,
                          const PMObject* after ) const
{
   int materialmap = 0, pigment = 0, finish = 0, pattern = 0, listpattern = 0;
   int map = 0, summap = 0, normal = 0, warp = 0, sumpigment = 0, blend = 0;
   bool afterPattern = false, afterMap = false, afterWarp = false;
   bool afterBlend = false, afterTransform = false;
   bool afterInsertPoint = 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( ), materialmap, pigment, finish, pattern, 
                  listpattern, map, summap, normal, warp, blend, sumpigment, 
                  afterPattern, afterMap, afterWarp, afterBlend,
                  afterTransform, afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }

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

   return numInserts;
}

int PMTexture::canInsert( const PMObjectList& list,
                          const PMObject* after ) const
{
   int materialmap = 0, pigment = 0, finish = 0, pattern = 0, listpattern = 0;
   int map = 0, summap = 0, normal = 0, warp = 0, sumpigment = 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;
   
   if( !after )
      afterInsertPoint = true;
   for( o = firstChild( ); o; o = o->nextSibling( ) )
   {
      countChild( o->type( ), materialmap, pigment, finish, pattern, 
                  listpattern, map, summap, normal, warp, blend, sumpigment,
                  afterPattern, afterMap, afterWarp, afterBlend,
                  afterTransform, afterInsertPoint );
      if( o == after )
         afterInsertPoint = true;
   }
   
   for( ; it.current( ); ++it )
   {
      t = it.current( )->type( );
      if( canInsert( t, materialmap, pigment, finish, pattern,
                     listpattern, map, summap, normal, warp, blend, 
                     sumpigment, afterPattern, afterMap, afterWarp,
                     afterBlend, afterTransform ) )
         numInserts++;
      countChild( t, materialmap, pigment, finish, pattern, listpattern,
                  map, summap, normal, warp, blend, sumpigment,
                  afterPattern, afterMap, afterWarp, afterBlend,
                  afterTransform, false );
   }

   return numInserts;
}

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

PMDialogEditBase* PMTexture::editWidget( QWidget* parent ) const
{
   return new PMTextureEdit( parent );
}

