/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2001 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 "pmtexturemapedit.h"
#include "pmtexturemap.h"

#include "pmoutputdevice.h"
#include "pmxmlhelper.h"
#include "pmmapmemento.h"

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

#include <qtextstream.h>
#include <klocale.h>

PMTextureMapBase::PMTextureMapBase( )
      : Base( )
{
}

PMTextureMapBase::~PMTextureMapBase( )
{
}

void PMTextureMapBase::serialize( PMOutputDevice& dev ) const
{
   QValueList<double>::ConstIterator it = m_mapValues.begin( );
   PMObject* c = firstChild( );
   double value = 0.0;
   
   /* Take care of a possible link */
   if( linkedObject( ) )
   {
      if( linkedObject( )->firstChild( ) )
         dev.writeLine( linkedObject( )->id( ) );
      else
      {
         QString text;
         text = name( );
         if( text.isEmpty( ) )
            text = description( );
         
         dev.writeComment( QString( "No prototype for %1" ).arg( text ) );
      }
   }

   /* Serialize the map */
   for( ; c; c = c->nextSibling( ) )
   {
      if( c->type( ) == mapType( ) )
      {
         value = 1.0;
         if( it != m_mapValues.end( ) )
            value = *it;
         dev.write( QString( "[ %1 " ).arg( value ) );
         c->serialize( dev );
         dev.writeLine( "]" );
         ++it;
      }  
   }
}

void PMTextureMapBase::serialize( QDomElement& e, QDomDocument& doc ) const
{
   e.setAttribute( "map_values", valuesToString( ) );
   Base::serialize( e, doc );
}

void PMTextureMapBase::readAttributes( const PMXMLHelper& h )
{
   stringToValues( h.stringAttribute( "map_values", "" ) );
   Base::readAttributes( h );
}

QString PMTextureMapBase::valuesToString( ) const
{
   QString str;
   QValueList<double>::ConstIterator it;

   it = m_mapValues.begin( );
   if( it != m_mapValues.end( ) )
   {
      str.setNum( *it );
      ++it;
      for( ; it != m_mapValues.end( ); ++it )
         str += QString( " %1" ).arg( *it );
   }
   return str;
}

void PMTextureMapBase::stringToValues( const QString& str )
{
   m_mapValues.clear( );
   QString tstr = str;
   QTextIStream s( &tstr );
   double d;
   
   while( !s.atEnd( ) )
   {
      s >> d;
      m_mapValues.append( d );
   }
}

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

PMDialogEditBase* PMTextureMapBase::editWidget( QWidget* parent ) const
{
   return new PMTextureMapEdit( parent );
}

void PMTextureMapBase::restoreMemento( PMMemento* s )
{
   PMMapMemento* m = ( PMMapMemento* ) s;
   if( m->mapValuesSaved( ) )
   {
      if( m_pMemento )
         ( ( PMMapMemento* ) m_pMemento )->setMapValues( m_mapValues );
      m_mapValues = m->mapValues( );
   }
   if( m->removedValuesSaved( ) )
   {
      if( m_pMemento )
         ( ( PMMapMemento* ) m_pMemento )->setRemovedValues( m_removedValues );
      m_removedValues = m->removedValues( );
   }
   
   Base::restoreMemento( s );
}

void PMTextureMapBase::createMemento( )
{
   if( m_pMemento )
      delete m_pMemento;
   m_pMemento = new PMMapMemento( this );
}

QValueList<double>::Iterator PMTextureMapBase::valueForChild( PMObject* obj )
{
   PMObject* o = firstChild( );
   QValueList<double>::Iterator it = m_mapValues.begin( );
   
   while( o && ( o != obj ) )
   {
      if( o->type( ) == mapType( ) )
         ++it;
      o = o->nextSibling( );
   }
   return it;
}

double PMTextureMapBase::mapValue( const PMObject* obj ) const
{
   PMObject* o = firstChild( );
   QValueList<double>::ConstIterator it = m_mapValues.begin( );
   
   while( o && ( o != obj ) )
   {
      if( o->type( ) == mapType( ) )
         ++it;
      o = o->nextSibling( );
   }
   return *it;
}

void PMTextureMapBase::childAdded( PMObject* ao )
{
   if( ( unsigned ) countChildren( ) <= m_mapValues.count( ) )
      return;
   
   if( m_pMemento )
      ( ( PMMapMemento* ) m_pMemento )->setMapValues( m_mapValues );
   if( m_removedValues.isEmpty( ) )
   {
      QValueList<double>::Iterator it = valueForChild( ao );
      if( it == m_mapValues.end( ) )
      {
         --it;
         if( it == m_mapValues.end( ) )
            m_mapValues.append( 0.0 );
         else
         {
            double v = *it + 0.1;
            if( v > 1.0 )
               v = 1.0;
            m_mapValues.append( v );
         }
      }
      else if( it == m_mapValues.begin( ) )
         m_mapValues.prepend( 0.0 );
      else
      {
         double va = *it;
         double vb = *( --it );
         m_mapValues.insert( ++it, ( va + vb ) / 2.0 );
      }
   }
   else
   {
      if( m_pMemento )
         ( ( PMMapMemento* ) m_pMemento )->setRemovedValues( m_removedValues );

      QValueList<double>::Iterator it = m_mapValues.begin( );
      bool stop = false;
      double v = m_removedValues.last( );
      m_removedValues.remove( m_removedValues.fromLast( ) );
      
      while( ( it != m_mapValues.end( ) ) && !stop )
      {
         if( ( *it ) > v )
            stop = true;
         else
            ++it;
      }
      m_mapValues.insert( it, v );
   }
}

bool PMTextureMapBase::takeChild( PMObject* o )
{
   if( m_pMemento )
   {
      ( ( PMMapMemento* ) m_pMemento )->setMapValues( m_mapValues );
      ( ( PMMapMemento* ) m_pMemento )->setRemovedValues( m_removedValues );
   }
   
   QValueList<double>::Iterator it = valueForChild( o );
   if( it != m_mapValues.end( ) )
   {
      m_removedValues.append( *it );
      m_mapValues.remove( it );
   }

   return Base::takeChild( o );
}

void PMTextureMapBase::setMapValues( const QValueList<double>& v )
{
   if( m_pMemento )
   {
      ( ( PMMapMemento* ) m_pMemento )->setMapValues( m_mapValues );
      ( ( PMMapMemento* ) m_pMemento )->setRemovedValues( m_removedValues );
   }
   m_removedValues.clear( );
   m_mapValues = v;
}

PMObject* PMTextureMapBase::nextMapEntry( PMObject* o )
{
   bool stop = false;
   PMObject* result = o;

   do
   {
      if( result == 0 )
         result = firstChild( );
      else
         result = result->nextSibling( );
      
      if( !result )
         stop = true;
      else if( result->type( ) == mapType( ) )
         stop = true;
   }
   while( !stop );

   return result;
}


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

PMTextureMap::PMTextureMap( )
      : Base( )
{
}

PMTextureMap::~PMTextureMap( )
{
}

bool PMTextureMap::canInsert( PMObjectType t, const PMObject*,
                              const PMObjectList* ) const
{
   switch( t )
   {
      case PMTTexture:
         if( !linkedObject( ) )
            return true;
         break;
      case PMTComment:
      case PMTRaw:
         return true;
         break;
      default:
         break;
   }
   return false;
}

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

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

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

void PMTextureMap::serialize( PMOutputDevice& dev ) const
{
   dev.objectBegin( "texture_map" );
   Base::serialize( dev );
   dev.objectEnd( );
}


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

PMPigmentMap::PMPigmentMap( )
      : Base( )
{
}

PMPigmentMap::~PMPigmentMap( )
{
}

bool PMPigmentMap::canInsert( PMObjectType t, const PMObject*,
                              const PMObjectList* ) const
{
   switch( t )
   {
      case PMTPigment:
         if( !linkedObject( ) )
            return true;
         break;
      case PMTComment:
      case PMTRaw:
         return true;
         break;
      default:
         break;
   }
   return false;
}

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

QString PMPigmentMap::description( ) const
{
   return i18n( "pigment map" );
}

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

void PMPigmentMap::serialize( PMOutputDevice& dev ) const
{
   dev.objectBegin( "pigment_map" );
   Base::serialize( dev );
   dev.objectEnd( );
}

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

PMColorMap::PMColorMap( )
      : Base( )
{
}

PMColorMap::~PMColorMap( )
{
}

bool PMColorMap::canInsert( PMObjectType t, const PMObject*,
                              const PMObjectList* ) const
{
   switch( t )
   {
      case PMTSolidColor:
         if( !linkedObject( ) )
            return true;
         break;
      case PMTComment:
      case PMTRaw:
         return true;
         break;
      default:
         break;
   }
   return false;
}

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

QString PMColorMap::description( ) const
{
   return i18n( "color map" );
}

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

void PMColorMap::serialize( PMOutputDevice& dev ) const
{
   dev.objectBegin( "color_map" );
   Base::serialize( dev );
   dev.objectEnd( );
}

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

PMNormalMap::PMNormalMap( )
      : Base( )
{
}

PMNormalMap::~PMNormalMap( )
{
}

bool PMNormalMap::canInsert( PMObjectType t, const PMObject*,
                              const PMObjectList* ) const
{
   switch( t )
   {
      case PMTNormal:
         if( !linkedObject( ) )
            return true;
         break;
      case PMTComment:
      case PMTRaw:
         return true;
         break;
      default:
         break;
   }
   return false;
}

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

QString PMNormalMap::description( ) const
{
   return i18n( "normal map" );
}

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

void PMNormalMap::serialize( PMOutputDevice& dev ) const
{
   dev.objectBegin( "normal_map" );
   Base::serialize( dev );
   dev.objectEnd( );
}

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

PMSlopeMap::PMSlopeMap( )
      : Base( )
{
}

PMSlopeMap::~PMSlopeMap( )
{
}

bool PMSlopeMap::canInsert( PMObjectType t, const PMObject*,
                              const PMObjectList* ) const
{
   switch( t )
   {
      case PMTSlope:
         if( !linkedObject( ) )
            return true;
         break;
      case PMTComment:
      case PMTRaw:
         return true;
         break;
      default:
         break;
   }
   return false;
}

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

QString PMSlopeMap::description( ) const
{
   return i18n( "slope map" );
}

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

void PMSlopeMap::serialize( PMOutputDevice& dev ) const
{
   dev.objectBegin( "slope_map" );
   Base::serialize( dev );
   dev.objectEnd( );
}

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

PMDensityMap::PMDensityMap( )
      : Base( )
{
}

PMDensityMap::~PMDensityMap( )
{
}

bool PMDensityMap::canInsert( PMObjectType t, const PMObject*,
                              const PMObjectList* ) const
{
   switch( t )
   {
      case PMTDensity:
         if( !linkedObject( ) )
            return true;
         break;
      case PMTComment:
      case PMTRaw:
         return true;
         break;
      default:
         break;
   }
   return false;
}

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

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

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

void PMDensityMap::serialize( PMOutputDevice& dev ) const
{
   dev.objectBegin( "density_map" );
   Base::serialize( dev );
   dev.objectEnd( );
}

