//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// Viewport transformer - simple orthogonal conversions from world coordinates
//                        to screen (device) ones.
//
//----------------------------------------------------------------------------

#ifndef AGG_TRANS_VIEWPORT_INCLUDED
#define AGG_TRANS_VIEWPORT_INCLUDED

#include <string.h>
#include "agg_basics.h"


namespace agg
{

    enum aspect_ratio_e
    {
        aspect_ratio_stretch,
        aspect_ratio_meet,
        aspect_ratio_slice
    };


    //----------------------------------------------------------trans_viewport
    class trans_viewport
    {
    public:
        //-------------------------------------------------------------------
        trans_viewport() : 
            m_world_x1(0.0),
            m_world_y1(0.0),
            m_world_x2(1.0),
            m_world_y2(1.0),
            m_device_x1(0.0),
            m_device_y1(0.0),
            m_device_x2(1.0),
            m_device_y2(1.0),
            m_aspect(aspect_ratio_stretch),
            m_align_x(0.5),
            m_align_y(0.5),
            m_wx1(0.0),
            m_wy1(0.0),
            m_wx2(1.0),
            m_wy2(1.0),
            m_dx1(0.0),
            m_dy1(0.0),
            m_kx(1.0),
            m_ky(1.0)
        {}

        //-------------------------------------------------------------------
        void preserve_aspect_ratio(double alignx, 
                                   double aligny, 
                                   aspect_ratio_e aspect)
        {
            m_align_x = alignx;
            m_align_y = aligny;
            m_aspect  = aspect;
            update();
        }

        //-------------------------------------------------------------------
        void device_viewport(double x1, double y1, double x2, double y2)
        {
            m_device_x1 = x1;
            m_device_y1 = y1;
            m_device_x2 = x2;
            m_device_y2 = y2;
            update();
        }

        //-------------------------------------------------------------------
        void world_viewport(double x1, double y1, double x2, double y2)
        {
            m_world_x1 = x1;
            m_world_y1 = y1;
            m_world_x2 = x2;
            m_world_y2 = y2;
            update();
        }

        //-------------------------------------------------------------------
        void device_viewport(double* x1, double* y1, double* x2, double* y2) const
        {
            *x1 = m_device_x1;
            *y1 = m_device_y1;
            *x2 = m_device_x2;
            *y2 = m_device_y2;
        }

        //-------------------------------------------------------------------
        void world_viewport(double* x1, double* y1, double* x2, double* y2) const
        {
            *x1 = m_world_x1;
            *y1 = m_world_y1;
            *x2 = m_world_x2;
            *y2 = m_world_y2;
        }

        //-------------------------------------------------------------------
        void world_viewport_actual(double* x1, double* y1, 
                                   double* x2, double* y2) const
        {
            *x1 = m_wx1;
            *y1 = m_wy1;
            *x2 = m_wx2;
            *y2 = m_wy2;
        }

        //-------------------------------------------------------------------
        double align_x()              const { return m_align_x; }
        double align_y()              const { return m_align_y; }
        aspect_ratio_e aspect_ratio() const { return m_aspect; }

        //-------------------------------------------------------------------
        void transform(double* x, double* y) const
        {
            *x = (*x - m_wx1) * m_kx + m_dx1;
            *y = (*y - m_wy1) * m_ky + m_dy1;
        }

        //-------------------------------------------------------------------
        void inverse_transform(double* x, double* y) const
        {
            *x = (*x - m_dx1) / m_kx + m_wx1;
            *y = (*y - m_dy1) / m_ky + m_wy1;
        }

        //-------------------------------------------------------------------
        double scale() const
        {
            return (m_kx + m_ky) * 0.5;
        }

        //-------------------------------------------------------------------
        unsigned byte_size() const
        {
            return 
                sizeof(m_world_x1) +
                sizeof(m_world_y1) +
                sizeof(m_world_x2) +
                sizeof(m_world_y2) +
                sizeof(m_device_x1) +
                sizeof(m_device_y1) +
                sizeof(m_device_x2) +
                sizeof(m_device_y2) +
                sizeof(m_aspect) +
                sizeof(m_align_x) +
                sizeof(m_align_y) +
                sizeof(m_wx1) +
                sizeof(m_wy1) +
                sizeof(m_wx2) +
                sizeof(m_wy2) +
                sizeof(m_dx1) +
                sizeof(m_dy1) +
                sizeof(m_kx) +
                sizeof(m_ky);
        }

        void serialize(int8u* ptr) const
        {
            memcpy(ptr, &m_world_x1,  sizeof(m_world_x1));  ptr += sizeof(m_world_x1); 
            memcpy(ptr, &m_world_y1,  sizeof(m_world_y1));  ptr += sizeof(m_world_y1);
            memcpy(ptr, &m_world_x2,  sizeof(m_world_x2));  ptr += sizeof(m_world_x2);
            memcpy(ptr, &m_world_y2,  sizeof(m_world_y2));  ptr += sizeof(m_world_y2);
            memcpy(ptr, &m_device_x1, sizeof(m_device_x1)); ptr += sizeof(m_device_x1);
            memcpy(ptr, &m_device_y1, sizeof(m_device_y1)); ptr += sizeof(m_device_y1);
            memcpy(ptr, &m_device_x2, sizeof(m_device_x2)); ptr += sizeof(m_device_x2);
            memcpy(ptr, &m_device_y2, sizeof(m_device_y2)); ptr += sizeof(m_device_y2);
            memcpy(ptr, &m_aspect,    sizeof(m_aspect));    ptr += sizeof(m_aspect);
            memcpy(ptr, &m_align_x,   sizeof(m_align_x));   ptr += sizeof(m_align_x);
            memcpy(ptr, &m_align_y,   sizeof(m_align_y));   ptr += sizeof(m_align_y);
            memcpy(ptr, &m_wx1,       sizeof(m_wx1));       ptr += sizeof(m_wx1);
            memcpy(ptr, &m_wy1,       sizeof(m_wy1));       ptr += sizeof(m_wy1);
            memcpy(ptr, &m_wx2,       sizeof(m_wx2));       ptr += sizeof(m_wx2);
            memcpy(ptr, &m_wy2,       sizeof(m_wy2));       ptr += sizeof(m_wy2);
            memcpy(ptr, &m_dx1,       sizeof(m_dx1));       ptr += sizeof(m_dx1);
            memcpy(ptr, &m_dy1,       sizeof(m_dy1));       ptr += sizeof(m_dy1);
            memcpy(ptr, &m_kx,        sizeof(m_kx));        ptr += sizeof(m_kx);
            memcpy(ptr, &m_ky,        sizeof(m_ky));        ptr += sizeof(m_ky);
        }

        void deserialize(const int8u* ptr)
        {
            memcpy(&m_world_x1,  ptr, sizeof(m_world_x1));  ptr += sizeof(m_world_x1); 
            memcpy(&m_world_y1,  ptr, sizeof(m_world_y1));  ptr += sizeof(m_world_y1);
            memcpy(&m_world_x2,  ptr, sizeof(m_world_x2));  ptr += sizeof(m_world_x2);
            memcpy(&m_world_y2,  ptr, sizeof(m_world_y2));  ptr += sizeof(m_world_y2);
            memcpy(&m_device_x1, ptr, sizeof(m_device_x1)); ptr += sizeof(m_device_x1);
            memcpy(&m_device_y1, ptr, sizeof(m_device_y1)); ptr += sizeof(m_device_y1);
            memcpy(&m_device_x2, ptr, sizeof(m_device_x2)); ptr += sizeof(m_device_x2);
            memcpy(&m_device_y2, ptr, sizeof(m_device_y2)); ptr += sizeof(m_device_y2);
            memcpy(&m_aspect,    ptr, sizeof(m_aspect));    ptr += sizeof(m_aspect);
            memcpy(&m_align_x,   ptr, sizeof(m_align_x));   ptr += sizeof(m_align_x);
            memcpy(&m_align_y,   ptr, sizeof(m_align_y));   ptr += sizeof(m_align_y);
            memcpy(&m_wx1,       ptr, sizeof(m_wx1));       ptr += sizeof(m_wx1);
            memcpy(&m_wy1,       ptr, sizeof(m_wy1));       ptr += sizeof(m_wy1);
            memcpy(&m_wx2,       ptr, sizeof(m_wx2));       ptr += sizeof(m_wx2);
            memcpy(&m_wy2,       ptr, sizeof(m_wy2));       ptr += sizeof(m_wy2);
            memcpy(&m_dx1,       ptr, sizeof(m_dx1));       ptr += sizeof(m_dx1);
            memcpy(&m_dy1,       ptr, sizeof(m_dy1));       ptr += sizeof(m_dy1);
            memcpy(&m_kx,        ptr, sizeof(m_kx));        ptr += sizeof(m_kx);
            memcpy(&m_ky,        ptr, sizeof(m_ky));        ptr += sizeof(m_ky);
        }

    private:
        void update();

        double         m_world_x1;
        double         m_world_y1;
        double         m_world_x2;
        double         m_world_y2;
        double         m_device_x1;
        double         m_device_y1;
        double         m_device_x2;
        double         m_device_y2;
        aspect_ratio_e m_aspect;
        double         m_align_x;
        double         m_align_y;
        double         m_wx1;
        double         m_wy1;
        double         m_wx2;
        double         m_wy2;
        double         m_dx1;
        double         m_dy1;
        double         m_kx;
        double         m_ky;
    };



    //-----------------------------------------------------------------------
    inline void trans_viewport::update()
    {
        double world_x1  = m_world_x1;
        double world_y1  = m_world_y1;
        double world_x2  = m_world_x2;
        double world_y2  = m_world_y2;
        double device_x1 = m_device_x1;
        double device_y1 = m_device_y1;
        double device_x2 = m_device_x2;
        double device_y2 = m_device_y2;
        if(m_aspect != aspect_ratio_stretch)
        {
            double d;
            m_kx = (device_x2 - device_x1) / (world_x2 - world_x1);
            m_ky = (device_y2 - device_y1) / (world_y2 - world_y1);

            if((m_aspect == aspect_ratio_meet) == (m_kx < m_ky))
            {
                d         = (world_y2 - world_y1) * m_ky / m_kx;
                world_y1 += (world_y2 - world_y1 - d) * m_align_y;
                world_y2  =  world_y1 + d;
            }
            else
            {
                d         = (world_x2 - world_x1) * m_kx / m_ky;
                world_x1 += (world_x2 - world_x1 - d) * m_align_x;
                world_x2  =  world_x1 + d;
            }
        }
        m_wx1 = world_x1;
        m_wy1 = world_y1;
        m_wx2 = world_x2;
        m_wy2 = world_y2;
        m_dx1 = device_x1;
        m_dy1 = device_y1;
        m_kx  = (device_x2 - device_x1) / (world_x2 - world_x1);
        m_ky  = (device_y2 - device_y1) / (world_y2 - world_y1);
    }


}


#endif
