/***************************************************************************
                          qgsrectangle.cpp  -  description
                             -------------------
    begin                : Sat Jun 22 2002
    copyright            : (C) 2002 by Gary E.Sherman
    email                : sherman at mrcc.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
/* $Id: qgsrectangle.cpp 11572 2009-09-05 19:49:47Z jef $ */

#include <algorithm>
#include <cmath>
#include <limits>
#include <QString>
#include <QTextStream>

#include "qgspoint.h"
#include "qgsrectangle.h"
#include "qgslogger.h"

QgsRectangle::QgsRectangle( double newxmin, double newymin, double newxmax, double newymax )
    : xmin( newxmin ), ymin( newymin ), xmax( newxmax ), ymax( newymax )
{
  normalize();
}

QgsRectangle::QgsRectangle( QgsPoint const & p1, QgsPoint const & p2 )
{
  set( p1, p2 );
}

QgsRectangle::QgsRectangle( const QgsRectangle &r )
{
  xmin = r.xMinimum();
  ymin = r.yMinimum();
  xmax = r.xMaximum();
  ymax = r.yMaximum();
}

void QgsRectangle::set( const QgsPoint& p1, const QgsPoint& p2 )
{
  xmin = p1.x();
  xmax = p2.x();
  ymin = p1.y();
  ymax = p2.y();
  normalize();
}

void QgsRectangle::set( double xmin_, double ymin_, double xmax_, double ymax_ )
{
  xmin = xmin_;
  ymin = ymin_;
  xmax = xmax_;
  ymax = ymax_;
  normalize();
}

void QgsRectangle::normalize()
{
  if ( xmin > xmax )
  {
    std::swap( xmin, xmax );
  }
  if ( ymin > ymax )
  {
    std::swap( ymin, ymax );
  }
} // QgsRectangle::normalize()


void QgsRectangle::setMinimal()
{
  xmin = std::numeric_limits<double>::max();
  ymin = std::numeric_limits<double>::max();
  xmax = -std::numeric_limits<double>::max();
  ymax = -std::numeric_limits<double>::max();
}

void QgsRectangle::scale( double scaleFactor, const QgsPoint * cp )
{
  // scale from the center
  double centerX, centerY;
  if ( cp )
  {
    centerX = cp->x();
    centerY = cp->y();
  }
  else
  {
    centerX = xmin + width() / 2;
    centerY = ymin + height() / 2;
  }
  double newWidth = width() * scaleFactor;
  double newHeight = height() * scaleFactor;
  xmin = centerX - newWidth / 2.0;
  xmax = centerX + newWidth / 2.0;
  ymin = centerY - newHeight / 2.0;
  ymax = centerY + newHeight / 2.0;
}

void QgsRectangle::expand( double scaleFactor, const QgsPoint * cp )
{
  // scale from the center
  double centerX, centerY;
  if ( cp )
  {
    centerX = cp->x();
    centerY = cp->y();
  }
  else
  {
    centerX = xmin + width() / 2;
    centerY = ymin + height() / 2;
  }

  double newWidth = width() * scaleFactor;
  double newHeight = height() * scaleFactor;
  xmin = centerX - newWidth;
  xmax = centerX + newWidth;
  ymin = centerY - newHeight;
  ymax = centerY + newHeight;
}

QgsRectangle QgsRectangle::intersect( const QgsRectangle * rect ) const
{
  QgsRectangle intersection = QgsRectangle();
  //If they don't actually intersect an empty QgsRectangle should be returned
  if ( !rect || !intersects( *rect ) )
  {
    return intersection;
  }

  intersection.setXMinimum( xmin > rect->xMinimum() ? xmin : rect->xMinimum() );
  intersection.setXMaximum( xmax < rect->xMaximum() ? xmax : rect->xMaximum() );
  intersection.setYMinimum( ymin > rect->yMinimum() ? ymin : rect->yMinimum() );
  intersection.setYMaximum( ymax < rect->yMaximum() ? ymax : rect->yMaximum() );
  return intersection;
}

bool QgsRectangle::intersects( const QgsRectangle& rect ) const
{
  double x1 = ( xmin > rect.xmin ? xmin : rect.xmin );
  double x2 = ( xmax < rect.xmax ? xmax : rect.xmax );
  if ( x1 > x2 ) return FALSE;
  double y1 = ( ymin > rect.ymin ? ymin : rect.ymin );
  double y2 = ( ymax < rect.ymax ? ymax : rect.ymax );
  if ( y1 > y2 ) return FALSE;
  return TRUE;
}

bool QgsRectangle::contains( const QgsRectangle& rect ) const
{
  return ( rect.xmin >= xmin && rect.xmax <= xmax && rect.ymin >= ymin && rect.ymax <= ymax );
}

bool QgsRectangle::contains( const QgsPoint &p ) const
{
  return xmin <= p.x() && p.x() <= xmax &&
         ymin <= p.y() && p.y() <= ymax;
}

void QgsRectangle::combineExtentWith( QgsRectangle * rect )
{

  xmin = (( xmin < rect->xMinimum() ) ? xmin : rect->xMinimum() );
  xmax = (( xmax > rect->xMaximum() ) ? xmax : rect->xMaximum() );

  ymin = (( ymin < rect->yMinimum() ) ? ymin : rect->yMinimum() );
  ymax = (( ymax > rect->yMaximum() ) ? ymax : rect->yMaximum() );

}

void QgsRectangle::combineExtentWith( double x, double y )
{

  xmin = (( xmin < x ) ? xmin : x );
  xmax = (( xmax > x ) ? xmax : x );

  ymin = (( ymin < y ) ? ymin : y );
  ymax = (( ymax > y ) ? ymax : y );

}

bool QgsRectangle::isEmpty() const
{
  if ( xmax <= xmin || ymax <= ymin )
  {
    return TRUE;
  }
  else
  {
    return FALSE;
  }
}

QString QgsRectangle::asWktCoordinates() const
{
  QString rep =
    QString::number( xmin, 'f', 16 ) + " " +
    QString::number( ymin, 'f', 16 ) + ", " +
    QString::number( xmax, 'f', 16 ) + " " +
    QString::number( ymax, 'f', 16 );

  return rep;
}

// Return a string representation of the rectangle with automatic or high precision
QString QgsRectangle::toString( bool automaticPrecision ) const
{
  if ( automaticPrecision )
  {
    int precision = 0;
    if (( width() < 1 || height() < 1 ) && ( width() > 0 && height() > 0 ) )
    {
      precision = static_cast<int>( ceil( -1.0 * log10( std::min( width(), height() ) ) ) ) + 1;
      // sanity check
      if ( precision > 20 )
        precision = 20;
    }
    return toString( precision );
  }
  else
    return toString( 16 );
}

// overloaded version of above fn to allow precision to be set
// Return a string representation of the rectangle with high precision
QString QgsRectangle::toString( int thePrecision ) const
{

  QString rep = QString::number( xmin, 'f', thePrecision ) +
                QString( "," ) +
                QString::number( ymin, 'f', thePrecision ) +
                QString( " : " ) +
                QString::number( xmax, 'f', thePrecision ) +
                QString( "," ) +
                QString::number( ymax, 'f', thePrecision ) ;
#ifdef QGISDEBUG
// QgsDebugMsg(QString("Extents : %1").arg(rep));
#endif
  return rep;
}


// Return the rectangle as a set of polygon coordinates
QString QgsRectangle::asPolygon() const
{
//   QString rep = tmp.sprintf("%16f %16f,%16f %16f,%16f %16f,%16f %16f,%16f %16f",
//     xmin, ymin, xmin, ymax, xmax, ymax, xmax, ymin, xmin, ymin);
  QString rep;

  QTextStream foo( &rep );

  foo.setRealNumberPrecision( 8 );
  foo.setRealNumberNotation( QTextStream::FixedNotation );
  // NOTE: a polygon isn't a polygon unless its closed. In the case of
  //       a rectangle, that means 5 points (last == first)
  foo <<  xmin << " " <<  ymin << ", "
  <<  xmin << " " <<  ymax << ", "
  <<  xmax << " " <<  ymax << ", "
  <<  xmax << " " <<  ymin << ", "
  <<  xmin << " " <<  ymin;

  return rep;

} // QgsRectangle::asPolygon() const


bool QgsRectangle::operator==( const QgsRectangle & r1 ) const
{
  return ( r1.xMaximum() == xMaximum() &&
           r1.xMinimum() == xMinimum() &&
           r1.yMaximum() == yMaximum() &&
           r1.yMinimum() == yMinimum() );
}


bool QgsRectangle::operator!=( const QgsRectangle & r1 ) const
{
  return ( ! operator==( r1 ) );
}


QgsRectangle & QgsRectangle::operator=( const QgsRectangle & r )
{
  if ( &r != this )
  {
    xmax = r.xMaximum();
    xmin = r.xMinimum();
    ymax = r.yMaximum();
    ymin = r.yMinimum();
  }

  return *this;
}


void QgsRectangle::unionRect( const QgsRectangle& r )
{
  if ( r.xMinimum() < xMinimum() ) setXMinimum( r.xMinimum() );
  if ( r.xMaximum() > xMaximum() ) setXMaximum( r.xMaximum() );
  if ( r.yMinimum() < yMinimum() ) setYMinimum( r.yMinimum() );
  if ( r.yMaximum() > yMaximum() ) setYMaximum( r.yMaximum() );
}

bool QgsRectangle::isFinite() const
{
  if ( std::numeric_limits<double>::has_infinity )
  {
    if ( xmin == std::numeric_limits<double>::infinity() ||
         xmax == std::numeric_limits<double>::infinity() ||
         ymin == std::numeric_limits<double>::infinity() ||
         ymax == std::numeric_limits<double>::infinity() )
      return false;
  }
  // By design, if a variable is nan, it won't equal itself, so that's
  // how we test for nan
  if ( xmin != xmin || xmax != xmax || ymin != ymin || ymax != ymax )
    return false;

  return true;
}
