/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Basic graphics types.
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef GFX_GRAPHICS_HH
#define GFX_GRAPHICS_HH 1

#include "base/stream.hh"
#include "base/funcs_stl.hh"
using namespace base;
#include "math/module.hh"
#include "math/vertex.hh"
using namespace math;

namespace gfx {

// Basic types must be fast.  Virtual functions incur overhead from C++ vtable.
#define virtual VIRTUAL_METHODS_IN_BASIC_TYPES_IS_TOO_SLOW

////////////////////////////////////////////////////////////////////////////////
/// @brief 2D rectangle.
///
/// PRECONDITION: (X1,Y1) < (X2,Y2).
///
template<typename T>
class Rect
{
public:
    Rect( void )
    :   x1(0), y1(0), x2(0), y2(0)
    {
        // NOP
    }

    Rect( T X1, T Y1, T X2, T Y2 )
//  :   x1(X1), y1(Y1), x2(X2), y2(Y2)  // use Set() which can correct args
    {
        Set( X1, Y1, X2, Y2 );
    }

    void Set( T X1, T Y1, T X2, T Y2 )
    {
        // Try to maintain assumption that (x1,y1) < (x2,y2).
        x1 = MIN( X1, X2 );
        y1 = MIN( Y1, Y2 );
        x2 = MAX( X1, X2 );
        y2 = MAX( Y1, Y2 );
    }

    bool IfZero( void ) const
    {
        return (x1 == 0)
           and (y1 == 0)
           and (x2 == 0)
           and (y2 == 0);
    }

    /// @return True if a 2D point is inside this rectangle.
    bool IfInside( T x, T y ) const
    {
        return x >= x1   // assumes (x1,y1) < (x2,y2)
            && x <= x2   // this could be recoded to not assume
            && y >= y1   // but it'd be slower
            && y <= y2;
    }

    bool IfInside( const Vector2& v ) const
    {
        return IfInside( T(v.x), T(v.y) );  // cast to suppress warning
    }

    /// @return True if the passed rectangle is ENTIRELY outside this one.
    bool IfOutside( const Rect<T>& rect ) const
    {
        return x2 < rect.x1     // if this right side is left of other
            || x1 > rect.x2     // if this left side is right of other
            || y1 > rect.y2     // if top of this is below other
            || y2 < rect.y1;    // if bottom of this is above other
    }

    /// If any part of another rectangle overlaps this one.
    bool IfOverlaps( const Rect& rect ) const
    {
        // Not outside means entirely inside or partially inside (overlap).
        return not IfOutside( rect );
    }

    /// Grow rectangle (expand a corner) to enclose a point.
    void Grow( T x, T y )
    {
        // Try to maintain assumption that (x1,y1) < (x2,y2).
        x1 = MIN(x1,x);
        y1 = MIN(y1,y);
        x2 = MAX(x2,x);
        y2 = MAX(y2,y);
    }

    T GetWidth( void ) const
    {
        return x2 - x1;  // right - left
    }

    T GetHeight( void ) const
    {
        return y2 - y1;  // bottom - top
    }

public:
    T   x1;  // smallest x (NW)
    T   y1;  // smallest y
    T   x2;  // greatest x (SE)
    T   y2;  // greatest y
};

template<typename T>
bool operator==( const Rect<T>& a, const Rect<T>& b )
{
    return a.x1 == b.x1
        && a.y1 == b.y1
        && a.x2 == b.x2
        && a.y2 == b.y2;
}

template<typename T>
bool operator!=( const Rect<T>& a, const Rect<T>& b )
{
    return ! (a == b);  // this operator!=() is based on operator==()
}

template<typename T>
bool operator<( const Rect<T>& a, const Rect<T>& b )
{
    RETURN_LT_TRUE_GT_FALSE( a.x1, b.x1 )
    RETURN_LT_TRUE_GT_FALSE( a.y1, b.y1 )
    RETURN_LT_TRUE_GT_FALSE( a.x2, b.x2 )
    RETURN_LT_TRUE_GT_FALSE( a.y2, b.y2 )
    return false;  // equal
}

template<typename T>
std::ostream& operator<<( std::ostream& strm, const Rect<T>& o )
{
    std::ios::fmtflags savedFlags = strm.flags();
    strm << '{'
         << "x1=" << OMANIP_FIELD(0) << o.x1 << ','
         << "y1=" << OMANIP_FIELD(0) << o.y1 << ','
         << "x2=" << OMANIP_FIELD(0) << o.x2 << ','
         << "y2=" << OMANIP_FIELD(0) << o.y2 << '}';
    strm.flags(savedFlags);
    return strm;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief (width,height) tuple.
///
template<typename T=int>
struct WidthHeight
{
                WidthHeight( void ) : w(0), h(0) { }
                WidthHeight( T W, T H ) : w(W), h(H) { }
    explicit    WidthHeight( const Rect<T>& r ) : w(r.x2 - r.x1), h(r.y2 - r.y1) { }
    T   w, h;
};

template<typename T>
std::ostream& operator<<( std::ostream& strm, const WidthHeight<T>& o )
{
    std::ios::fmtflags savedFlags = strm.flags();
    strm << "(w=" << OMANIP_FIELD(0) << o.w << ','
         <<  "h=" << OMANIP_FIELD(0) << o.h << ')';
    strm.flags(savedFlags);
    return strm;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief Defines a volume in 3D space.
///
struct BoxVolume
{
    // Ensure that all coordinates of min vertex <= max vertex.
    // A side-effect is that corners could be redefined.
    BoxVolume( const WorldVertex& vmin, const WorldVertex& vmax )
    {
        mMin[XX] = MIN( vmin[XX], vmax[XX] );
        mMin[YY] = MIN( vmin[YY], vmax[YY] );
        mMin[ZZ] = MIN( vmin[ZZ], vmax[ZZ] );

        mMax[XX] = MAX( vmax[XX], vmax[XX] );
        mMax[YY] = MAX( vmax[YY], vmax[YY] );
        mMax[ZZ] = MAX( vmax[ZZ], vmax[ZZ] );
    }

    WorldVertex     mMin;
    WorldVertex     mMax;
};

#undef virtual

} // namespace gfx

#endif // GFX_GRAPHICS_HH
