// Copyright (C)  2002  Dominique Devriese <devriese@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.

// This program 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.

#include "rect.h"
#include "common.h"

Rect& Rect::operator=( const Rect& other )
{
  if (this != &other)
  {
    setBottomLeft( other.bottomLeft() );
    setWidth( other.width() );
    setHeight( other.height() );
  }
  normalize();
  return *this;
}

bool operator==( const Rect& r, const Rect& s )
{
  return ( r.bottomLeft() == s.bottomLeft()
           && r.width() == s.width()
           && r.height() == s.height() );
}

QDebug& operator<<( QDebug& s, const Rect& t )
{
  s << "left: " << t.left()
    << "bottom: " << t.bottom()
    << "right: " << t.right()
    << "top: " << t.top();
  return s;
}

Rect::Rect( const Coordinate &bottomLeft, const Coordinate &topRight )
  : mBottomLeft(bottomLeft)
{
  mwidth = topRight.x - bottomLeft.x;
  mheight = topRight.y - bottomLeft.y;
  normalize();
}

Rect::Rect( const Coordinate &p, const double width, const double height )
  : mBottomLeft(p),
    mwidth(width),
    mheight(height)
{
  normalize();
}

Rect::Rect( double xa, double ya, double width, double height )
  : mBottomLeft( xa, ya ),
    mwidth( width ),
    mheight( height )
{
  normalize();
}

Rect::Rect( const Rect& r )
  : mBottomLeft (r.mBottomLeft),
    mwidth(r.mwidth),
    mheight(r.mheight)
{
  normalize();
}

Rect::Rect()
  : mwidth(0),
    mheight(0)
{
}

void Rect::setBottomLeft( const Coordinate &p )
{
  mBottomLeft = p;
}

void Rect::setBottomRight( const Coordinate &p )
{
  mBottomLeft = p - Coordinate(mwidth,0);
}

void Rect::setTopRight( const Coordinate &p )
{
  mBottomLeft = p - Coordinate(mwidth, mheight);
}

void Rect::setCenter( const Coordinate &p )
{
  mBottomLeft = p - Coordinate(mwidth, mheight)/2;
}

void Rect::setLeft( const double p )
{
  double r = right();
  mBottomLeft.x = p;
  setRight( r );
}

void Rect::setRight( const double p )
{
  mwidth = p - left();
}

void Rect::setBottom( const double p )
{
  double t = top();
  mBottomLeft.y = p;
  setTop( t );
}

void Rect::setTop( const double p )
{
  mheight = p - bottom();
}

void Rect::setWidth( const double w )
{
  mwidth = w;
}

void Rect::setHeight( const double h )
{
  mheight = h;
}

void Rect::normalize()
{
  if ( mwidth < 0 )
    {
      mBottomLeft.x += mwidth;
      mwidth = -mwidth;
    };
  if ( mheight < 0 )
    {
      mBottomLeft.y += mheight;
      mheight = -mheight;
    };
}

void Rect::moveBy( const Coordinate &p )
{
  mBottomLeft += p;
}

void Rect::scale( const double r )
{
  mwidth *= r;
  mheight *= r;
}


QRect Rect::toQRect() const
{
  return QRect(mBottomLeft.toQPoint(), topRight().toQPoint());
}

Coordinate Rect::bottomLeft() const
{
  return mBottomLeft;
}

Coordinate Rect::bottomRight() const
{
  return mBottomLeft + Coordinate(mwidth, 0);
}

Coordinate Rect::topLeft() const
{
  return mBottomLeft + Coordinate(0, mheight);
}

Coordinate Rect::topRight() const
{
  return mBottomLeft + Coordinate(mwidth, mheight);
}

Coordinate Rect::center() const
{
  return mBottomLeft + Coordinate(mwidth, mheight)/2;
}

double Rect::left() const
{
  return mBottomLeft.x;
}
double Rect::right() const
{
  return left() + mwidth;
}
double Rect::bottom() const
{
  return mBottomLeft.y;
}

double Rect::top() const
{
  return bottom() + mheight;
}

double Rect::width() const
{
  return mwidth;
}

double Rect::height() const
{
  return mheight;
}

bool Rect::contains( const Coordinate& p, double allowed_miss ) const
{
  return p.x - left() >= - allowed_miss &&
    p.y - bottom() >= - allowed_miss &&
    p.x - left() - width() <= allowed_miss &&
    p.y - bottom() - height() <= allowed_miss;
}

bool Rect::contains( const Coordinate& p ) const
{
  return p.x >= left() &&
    p.y >= bottom() &&
    p.x - left() <= width() &&
    p.y - bottom() <= height();
}

bool Rect::intersects( const Rect& p ) const
{
  // never thought it was this simple :)
  if( p.left() < left() && p.right() < left()) return false;
  if( p.left() > right() && p.right() > right()) return false;
  if( p.bottom() < bottom() && p.top() < bottom()) return false;
  if( p.bottom() > top() && p.top() > top()) return false;
  return true;
}

void Rect::setContains( const Coordinate &p )
{
  normalize();
  if( p.x < left() ) setLeft( p.x );
  if( p.x > right() ) setRight(p.x);
  if( p.y < bottom() ) setBottom( p.y );
  if( p.y > top() ) setTop( p.y );
}

Rect Rect::normalized() const
{
  Rect t = *this;
  (void) t.normalize();
  return t;
}

Rect Rect::fromQRect( const QRect& r )
{
  return Rect( r.left(), r.top(), r.right(), r.bottom() );
}

void Rect::setTopLeft( const Coordinate &p )
{
  Coordinate bl = Coordinate( p.x, p.y - mheight );
  setBottomLeft( bl );
}

Rect operator|( const Rect& lhs, const Rect& rhs )
{
  Rect r( lhs );
  r |= rhs;
  return r;
}

void Rect::eat( const Rect& r )
{
  setLeft( kigMin( left(), r.left() ) );
  setRight( kigMax( right(), r.right() ) );
  setBottom( kigMin( bottom(), r.bottom() ) );
  setTop( kigMax( top(), r.top() ) );
}

Rect Rect::matchShape( const Rect& rhs, bool shrink ) const
{
  Rect ret = *this;
  Coordinate c = center();
  double v = width()/height(); // current ratio
  double w = rhs.width()/rhs.height(); // wanted ratio

  // we don't show less than r, if the dimensions don't match, we
  // extend r into some dimension...
  if( ( v > w ) ^ shrink )
    ret.setHeight( ret.width() / w );
  else
    ret.setWidth( ret.height() * w );

  ret.setCenter(c);
  return ret.normalized();
}

bool Rect::valid()
{
  return mBottomLeft.valid() && mwidth != double_inf && mheight != double_inf;
}

Rect Rect::invalidRect()
{
  return Rect( Coordinate::invalidCoord(), double_inf, double_inf );
}
