/* This file is part of the KDE project
   Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <KoChild.h>

#include <tqpainter.h>

#include <kdebug.h>

class KoChild::KoChildPrivate
{
public:
  KoChildPrivate()
  {
      m_contentsX = m_contentsY = 0;
  }
  ~KoChildPrivate()
  {
  }

  TQRect m_geometry;

  double m_rotation;
  double m_shearX;
  double m_shearY;
  TQPoint m_rotationPoint;
  double m_scaleX;
  double m_scaleY;
  TQWMatrix m_matrix;
  bool m_lock;
  TQPointArray m_old;
  bool m_transparent;
  int m_contentsX;
  int m_contentsY;
};

KoChild::KoChild( TQObject *parent, const char *name )
: TQObject( parent, name )
{
  d = new KoChildPrivate;

  d->m_scaleX = d->m_scaleY = 1.0;
  d->m_shearX = d->m_shearY = 0.0;
  d->m_rotation = 0.0;
  d->m_lock = false;
  d->m_transparent = false;

  updateMatrix();
}

KoChild::~KoChild()
{
  delete d;
}

void KoChild::setGeometry( const TQRect &rect, bool noEmit )
{
  if ( !d->m_lock )
    d->m_old = framePointArray();

  d->m_geometry = rect;

  // Embedded objects should have a minimum size of 3, so they can be selected
  if( d->m_geometry.width() < 3 )
    d->m_geometry.setWidth( 3 );

  if( d->m_geometry.height() < 3 )
    d->m_geometry.setHeight( 3 );

  updateMatrix();

  if ( !d->m_lock && !noEmit )
    emit changed( this );
}

TQRect KoChild::geometry() const
{
  return d->m_geometry;
}

TQRegion KoChild::region( const TQWMatrix &matrix ) const
{
  return TQRegion( pointArray( matrix ) );
}

TQPointArray KoChild::pointArray( const TQWMatrix &matrix ) const
{
  return pointArray( TQRect( 0, 0, d->m_geometry.width(), d->m_geometry.height() ), matrix );
}

//bool KoChild::contains( const TQPoint &point ) const
//{
//  return region().contains( point );
//}

TQRect KoChild::boundingRect() const
{
  return pointArray().boundingRect();
}

bool KoChild::isRectangle() const
{
  return !( d->m_shearX != 0.0 || d->m_shearY != 0.0 || d->m_rotation != 0.0 );
}

void KoChild::setClipRegion( TQPainter &painter, bool combine )
{
  painter.setClipping( true );
  if ( combine && !painter.clipRegion().isEmpty() )
    painter.setClipRegion( region( painter.worldMatrix() ).intersect( painter.clipRegion() ) );
  else
    painter.setClipRegion( region( painter.worldMatrix() ) );
}

void KoChild::setScaling( double x, double y )
{
  if ( !d->m_lock )
    d->m_old = framePointArray();

  d->m_scaleX = x;
  d->m_scaleY = y;

  // why is that commented out? (Simon)
  // This is commented out, because KoChild::transform() scales
  // the world matrix explicitly and updateMatrix() doesn't even
  // handle scaling (Werner)
  //updateMatrix()

  if ( !d->m_lock )
    emit changed( this );
}

double KoChild::xScaling() const
{
  return d->m_scaleX;
}

double KoChild::yScaling() const
{
  return d->m_scaleY;
}

void KoChild::setShearing( double x, double y )
{
  if ( !d->m_lock )
    d->m_old = framePointArray();

  d->m_shearX = x;
  d->m_shearY = y;

  updateMatrix();

  if ( !d->m_lock )
    emit changed( this );
}

double KoChild::xShearing() const
{
  return d->m_shearX;
}

double KoChild::yShearing() const
{
  return d->m_shearY;
}

void KoChild::setRotation( double rot )
{
  if ( !d->m_lock )
    d->m_old = framePointArray();

  d->m_rotation = rot;
  updateMatrix();

  if ( !d->m_lock )
    emit changed( this );
}

double KoChild::rotation() const
{
  return d->m_rotation;
}

void KoChild::setRotationPoint( const TQPoint &pos )
{
  if ( !d->m_lock )
    d->m_old = framePointArray();

  d->m_rotationPoint = pos;
  updateMatrix();

  if ( !d->m_lock )
    emit changed( this );
}

TQPoint KoChild::rotationPoint() const
{
  return d->m_rotationPoint;
}

void KoChild::transform( TQPainter &painter )
{
    setClipRegion( painter, true );

    TQWMatrix m = painter.worldMatrix();
    m = d->m_matrix * m;
    m.scale( d->m_scaleX, d->m_scaleY );
    painter.setWorldMatrix( m );
}

void KoChild::setContentsPos( int x, int y )
{
    d->m_contentsX = x;
    d->m_contentsY = y;
}

TQRect KoChild::contentRect() const
{
  return TQRect( d->m_contentsX, d->m_contentsY, int(d->m_geometry.width() / d->m_scaleX),
                int(d->m_geometry.height() / d->m_scaleY) );
}

TQPointArray KoChild::framePointArray( const TQWMatrix &matrix ) const
{
  return pointArray( TQRect( -6, -6, d->m_geometry.width() + 12, d->m_geometry.height() + 12 ), matrix );
}

TQRegion KoChild::frameRegion( const TQWMatrix &matrix, bool solid ) const
{
  const TQPointArray arr = framePointArray( matrix );
  const TQRegion frameReg( arr );

  if ( solid )
    return frameReg;

  const TQRegion reg = region( matrix );
  return frameReg.subtract( reg );
}

TQPointArray KoChild::pointArray( const TQRect &r, const TQWMatrix &matrix ) const
{
  TQPoint topleft = d->m_matrix.map( TQPoint( r.left(), r.top() ) );
  TQPoint topright = d->m_matrix.map( TQPoint( r.right(), r.top() ) );
  TQPoint bottomleft = d->m_matrix.map( TQPoint( r.left(), r.bottom() ) );
  TQPoint bottomright = d->m_matrix.map( TQPoint( r.right(), r.bottom() ) );

  TQPointArray arr( 4 );
  arr.setPoint( 0, topleft );
  arr.setPoint( 1, topright );
  arr.setPoint( 2, bottomright );
  arr.setPoint( 3, bottomleft );

  for( int i = 0; i < 4; ++i )
      arr.setPoint( i, matrix.map( arr.point( i ) ) );

  return arr;
}

void KoChild::updateMatrix()
{
  TQWMatrix r;
  r.rotate( - d->m_rotation );
  TQPoint p = r.map( TQPoint( d->m_rotationPoint.x(),
			    d->m_rotationPoint.y() ) );

  TQWMatrix m;
  m.rotate( d->m_rotation );
  m.translate( -d->m_rotationPoint.x() + d->m_geometry.x(), -d->m_rotationPoint.y() + d->m_geometry.y() );
  m.translate( p.x(), p.y() );
  m.shear( d->m_shearX, d->m_shearY );

  d->m_matrix = m;
}

TQWMatrix KoChild::matrix() const
{
  return d->m_matrix;
}

void KoChild::lock()
{
  if ( d->m_lock )
    return;

  d->m_old = framePointArray();
  d->m_lock = true;
}

void KoChild::unlock()
{
  if ( !d->m_lock )
    return;

  d->m_lock = false;
  emit changed( this );
}

bool KoChild::locked() const
{
  return d->m_lock;
}

TQPointArray KoChild::oldPointArray( const TQWMatrix &matrix )
{
  TQPointArray arr = d->m_old;

  for( int i = 0; i < 4; ++i )
      arr.setPoint( i, matrix.map( arr.point( i ) ) );

  return arr;
}

void KoChild::setTransparent( bool transparent )
{
  d->m_transparent = transparent;
}

bool KoChild::isTransparent() const
{
  return d->m_transparent;
}

KoChild::Gadget KoChild::gadgetHitTest( const TQPoint &p )
{
  if ( !frameRegion().contains( p ) )
    return NoGadget;

  if ( TQRegion( pointArray( TQRect( -5, -5, 5, 5 ) ) ).contains( p ) )
      return TopLeft;
  if ( TQRegion( pointArray( TQRect( d->m_geometry.width() / 2 - 3, -5, 5, 5 ) ) ).contains( p ) )
      return TopMid;
  if ( TQRegion( pointArray( TQRect( d->m_geometry.width(), -5, 5, 5 ) ) ).contains( p ) )
      return TopRight;
  if ( TQRegion( pointArray( TQRect( -5, d->m_geometry.height() / 2 - 3, 5, 5 ) ) ).contains( p ) )
      return MidLeft;
  if ( TQRegion( pointArray( TQRect( -5, d->m_geometry.height(), 5, 5 ) ) ).contains( p ) )
      return BottomLeft;
  if ( TQRegion( pointArray( TQRect( d->m_geometry.width() / 2 - 3,
				   d->m_geometry.height(), 5, 5 ) ) ).contains( p ) )
    return BottomMid;
  if ( TQRegion( pointArray( TQRect( d->m_geometry.width(), d->m_geometry.height(), 5, 5 ) ) ).contains( p ) )
      return BottomRight;
  if ( TQRegion( pointArray( TQRect( d->m_geometry.width(),
				   d->m_geometry.height() / 2 - 3, 5, 5 ) ) ).contains( p ) )
    return MidRight;

  return Move;
}

#include <KoChild.moc>