//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://antigrain.com
// 
// AGG 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.
// 
// AGG 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 AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
// MA 02110-1301, USA.
//----------------------------------------------------------------------------

#include "ctrl/agg_scale_ctrl.h"

namespace agg
{

    //------------------------------------------------------------------------
    scale_ctrl_impl::scale_ctrl_impl(double x1, double y1, 
                                     double x2, double y2, bool flip_y) :
        ctrl(x1, y1, x2, y2, flip_y),
        m_border_thickness(1.0),
        m_border_extra((fabs(x2 - x1) > fabs(y2 - y1)) ? (y2 - y1) / 2 : (x2 - x1) / 2),
        m_pdx(0.0),
        m_pdy(0.0),
        m_move_what(move_nothing),
        m_value1(0.3),
        m_value2(0.7),
        m_min_d(0.01)
    {
        calc_box();
    }


    //------------------------------------------------------------------------
    void scale_ctrl_impl::calc_box()
    {
        m_xs1 = m_x1 + m_border_thickness;
        m_ys1 = m_y1 + m_border_thickness;
        m_xs2 = m_x2 - m_border_thickness;
        m_ys2 = m_y2 - m_border_thickness;
    }


    //------------------------------------------------------------------------
    void scale_ctrl_impl::border_thickness(double t, double extra)
    { 
        m_border_thickness = t; 
        m_border_extra = extra;
        calc_box(); 
    }


    //------------------------------------------------------------------------
    void scale_ctrl_impl::resize(double x1, double y1, double x2, double y2)
    {
        m_x1 = x1;
        m_y1 = y1;
        m_x2 = x2;
        m_y2 = y2;
        calc_box(); 
        m_border_extra = (fabs(x2 - x1) > fabs(y2 - y1)) ? 
                            (y2 - y1) / 2 : 
                            (x2 - x1) / 2;
    }


    //------------------------------------------------------------------------
    void scale_ctrl_impl::value1(double value) 
    { 
        if(value < 0.0) value = 0.0;
        if(value > 1.0) value = 1.0;
        if(m_value2 - value < m_min_d) value = m_value2 - m_min_d;
        m_value1 = value; 
    }


    //------------------------------------------------------------------------
    void scale_ctrl_impl::value2(double value) 
    { 
        if(value < 0.0) value = 0.0;
        if(value > 1.0) value = 1.0;
        if(m_value1 + value < m_min_d) value = m_value1 + m_min_d;
        m_value2 = value; 
    }


    //------------------------------------------------------------------------
    void scale_ctrl_impl::move(double d)
    {
        m_value1 += d;
        m_value2 += d;
        if(m_value1 < 0.0)
        {
            m_value2 -= m_value1;
            m_value1 = 0.0;
        }
        if(m_value2 > 1.0)
        {
            m_value1 -= m_value2 - 1.0;
            m_value2 = 1.0;
        }
    }


    //------------------------------------------------------------------------
    void scale_ctrl_impl::rewind(unsigned idx)
    {
        m_idx = idx;

        switch(idx)
        {
        default:

        case 0:                 // Background
            m_vertex = 0;
            m_vx[0] = m_x1 - m_border_extra; 
            m_vy[0] = m_y1 - m_border_extra;
            m_vx[1] = m_x2 + m_border_extra; 
            m_vy[1] = m_y1 - m_border_extra;
            m_vx[2] = m_x2 + m_border_extra; 
            m_vy[2] = m_y2 + m_border_extra;
            m_vx[3] = m_x1 - m_border_extra; 
            m_vy[3] = m_y2 + m_border_extra;
            break;

        case 1:                 // Border
            m_vertex = 0;
            m_vx[0] = m_x1; 
            m_vy[0] = m_y1;
            m_vx[1] = m_x2; 
            m_vy[1] = m_y1;
            m_vx[2] = m_x2; 
            m_vy[2] = m_y2;
            m_vx[3] = m_x1; 
            m_vy[3] = m_y2;
            m_vx[4] = m_x1 + m_border_thickness; 
            m_vy[4] = m_y1 + m_border_thickness; 
            m_vx[5] = m_x1 + m_border_thickness; 
            m_vy[5] = m_y2 - m_border_thickness; 
            m_vx[6] = m_x2 - m_border_thickness; 
            m_vy[6] = m_y2 - m_border_thickness; 
            m_vx[7] = m_x2 - m_border_thickness; 
            m_vy[7] = m_y1 + m_border_thickness; 
            break;

        case 2:                 // pointer1
            if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1))
            {
                m_ellipse.init(m_xs1 + (m_xs2 - m_xs1) * m_value1,
                               (m_ys1 + m_ys2) / 2.0,
                               m_y2 - m_y1,
                               m_y2 - m_y1, 
                               32);
            }
            else
            {
                m_ellipse.init((m_xs1 + m_xs2) / 2.0,
                               m_ys1 + (m_ys2 - m_ys1) * m_value1,
                               m_x2 - m_x1,
                               m_x2 - m_x1, 
                               32);
            }
            m_ellipse.rewind(0);
            break;

        case 3:                 // pointer2
            if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1))
            {
                m_ellipse.init(m_xs1 + (m_xs2 - m_xs1) * m_value2,
                               (m_ys1 + m_ys2) / 2.0,
                               m_y2 - m_y1,
                               m_y2 - m_y1, 
                               32);
            }
            else
            {
                m_ellipse.init((m_xs1 + m_xs2) / 2.0,
                               m_ys1 + (m_ys2 - m_ys1) * m_value2,
                               m_x2 - m_x1,
                               m_x2 - m_x1, 
                               32);
            }
            m_ellipse.rewind(0);
            break;

        case 4:                 // slider
            m_vertex = 0;
            if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1))
            {
                m_vx[0] = m_xs1 + (m_xs2 - m_xs1) * m_value1;
                m_vy[0] = m_y1 - m_border_extra / 2.0;
                m_vx[1] = m_xs1 + (m_xs2 - m_xs1) * m_value2; 
                m_vy[1] = m_vy[0];
                m_vx[2] = m_vx[1]; 
                m_vy[2] = m_y2 + m_border_extra / 2.0;
                m_vx[3] = m_vx[0]; 
                m_vy[3] = m_vy[2];
            }
            else
            {
                m_vx[0] = m_x1 - m_border_extra / 2.0;
                m_vy[0] = m_ys1 + (m_ys2 - m_ys1) * m_value1;
                m_vx[1] = m_vx[0];
                m_vy[1] = m_ys1 + (m_ys2 - m_ys1) * m_value2; 
                m_vx[2] = m_x2 + m_border_extra / 2.0;
                m_vy[2] = m_vy[1]; 
                m_vx[3] = m_vx[2];
                m_vy[3] = m_vy[0]; 
            }
            break;
        }
    }


    //------------------------------------------------------------------------
    unsigned scale_ctrl_impl::vertex(double* x, double* y)
    {
        unsigned cmd = path_cmd_line_to;
        switch(m_idx)
        {
        case 0:
        case 4:
            if(m_vertex == 0) cmd = path_cmd_move_to;
            if(m_vertex >= 4) cmd = path_cmd_stop;
            *x = m_vx[m_vertex];
            *y = m_vy[m_vertex];
            m_vertex++;
            break;

        case 1:
            if(m_vertex == 0 || m_vertex == 4) cmd = path_cmd_move_to;
            if(m_vertex >= 8) cmd = path_cmd_stop;
            *x = m_vx[m_vertex];
            *y = m_vy[m_vertex];
            m_vertex++;
            break;

        case 2:
        case 3:
            cmd = m_ellipse.vertex(x, y);
            break;

        default:
            cmd = path_cmd_stop;
            break;
        }

        if(!is_stop(cmd))
        {
            transform_xy(x, y);
        }

        return cmd;
    }



    //------------------------------------------------------------------------
    bool scale_ctrl_impl::in_rect(double x, double y) const
    {
        inverse_transform_xy(&x, &y);
        return x >= m_x1 && x <= m_x2 && y >= m_y1 && y <= m_y2;
    }


    //------------------------------------------------------------------------
    bool scale_ctrl_impl::on_mouse_button_down(double x, double y)
    {
        inverse_transform_xy(&x, &y);

        double xp1;
        double xp2;
        double ys1;
        double ys2;
        double xp;
        double yp;

        if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1))
        {
            xp1 = m_xs1 + (m_xs2 - m_xs1) * m_value1;
            xp2 = m_xs1 + (m_xs2 - m_xs1) * m_value2;
            ys1 = m_y1  - m_border_extra / 2.0;
            ys2 = m_y2  + m_border_extra / 2.0;
            yp = (m_ys1 + m_ys2) / 2.0;

            if(x > xp1 && y > ys1 && x < xp2 && y < ys2)
            {
                m_pdx = xp1 - x;
                m_move_what = move_slider;
                return true;
            }

            //if(x < xp1 && calc_distance(x, y, xp1, yp) <= m_y2 - m_y1)
            if(calc_distance(x, y, xp1, yp) <= m_y2 - m_y1)
            {
                m_pdx = xp1 - x;
                m_move_what = move_value1;
                return true;
            }

            //if(x > xp2 && calc_distance(x, y, xp2, yp) <= m_y2 - m_y1)
            if(calc_distance(x, y, xp2, yp) <= m_y2 - m_y1)
            {
                m_pdx = xp2 - x;
                m_move_what = move_value2;
                return true;
            }
        }
        else
        {
            xp1 = m_x1  - m_border_extra / 2.0;
            xp2 = m_x2  + m_border_extra / 2.0;
            ys1 = m_ys1 + (m_ys2 - m_ys1) * m_value1;
            ys2 = m_ys1 + (m_ys2 - m_ys1) * m_value2;
            xp = (m_xs1 + m_xs2) / 2.0;

            if(x > xp1 && y > ys1 && x < xp2 && y < ys2)
            {
                m_pdy = ys1 - y;
                m_move_what = move_slider;
                return true;
            }

            //if(y < ys1 && calc_distance(x, y, xp, ys1) <= m_x2 - m_x1)
            if(calc_distance(x, y, xp, ys1) <= m_x2 - m_x1)
            {
                m_pdy = ys1 - y;
                m_move_what = move_value1;
                return true;
            }

            //if(y > ys2 && calc_distance(x, y, xp, ys2) <= m_x2 - m_x1)
            if(calc_distance(x, y, xp, ys2) <= m_x2 - m_x1)
            {
                m_pdy = ys2 - y;
                m_move_what = move_value2;
                return true;
            }
        }

        return false;
    }


    //------------------------------------------------------------------------
    bool scale_ctrl_impl::on_mouse_move(double x, double y, bool button_flag)
    {
        inverse_transform_xy(&x, &y);
        if(!button_flag)
        {
            return on_mouse_button_up(x, y);
        }

        double xp = x + m_pdx;
        double yp = y + m_pdy;
        double dv;

        switch(m_move_what)
        {
        case move_value1:
            if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1))
            {
                m_value1 = (xp - m_xs1) / (m_xs2 - m_xs1);
            }
            else
            {
                m_value1 = (yp - m_ys1) / (m_ys2 - m_ys1);
            }
            if(m_value1 < 0.0) m_value1 = 0.0;
            if(m_value1 > m_value2 - m_min_d) m_value1 = m_value2 - m_min_d;
            return true;

        case move_value2:
            if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1))
            {
                m_value2 = (xp - m_xs1) / (m_xs2 - m_xs1);
            }
            else
            {
                m_value2 = (yp - m_ys1) / (m_ys2 - m_ys1);
            }
            if(m_value2 > 1.0) m_value2 = 1.0;
            if(m_value2 < m_value1 + m_min_d) m_value2 = m_value1 + m_min_d;
            return true;

        case move_slider:
            dv = m_value2 - m_value1;
            if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1))
            {
                m_value1 = (xp - m_xs1) / (m_xs2 - m_xs1);
            }
            else
            {
                m_value1 = (yp - m_ys1) / (m_ys2 - m_ys1);
            }
            m_value2 = m_value1 + dv;
            if(m_value1 < 0.0)
            {
                dv = m_value2 - m_value1;
                m_value1 = 0.0;
                m_value2 = m_value1 + dv;
            }
            if(m_value2 > 1.0)
            {
                dv = m_value2 - m_value1;
                m_value2 = 1.0;
                m_value1 = m_value2 - dv;
            }
            return true;
        }

        return false;
    }


    //------------------------------------------------------------------------
    bool scale_ctrl_impl::on_mouse_button_up(double, double)
    {
        m_move_what = move_nothing;
        return false;
    }


    //------------------------------------------------------------------------
    bool scale_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up)
    {
/*
        if(right || up)
        {
            m_value += 0.005;
            if(m_value > 1.0) m_value = 1.0;
            return true;
        }

        if(left || down)
        {
            m_value -= 0.005;
            if(m_value < 0.0) m_value = 0.0;
            return true;
        }
*/
        return false;
    }

}

