#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include "agg_main/agg_basics.h"
#include "agg_main/agg_rendering_buffer.h"
#include "agg_main/agg_rasterizer_scanline_aa.h"
#include "agg_main/agg_scanline_p.h"
#include "agg_main/agg_renderer_scanline.h"
#include "agg_main/agg_path_storage.h"
#include "agg_main/agg_conv_transform.h"
#include "agg_main/agg_conv_clip_polygon.h"
#include "agg_main/agg_bounding_rect.h"
#include "agg_main/agg_ellipse.h"
#include "agg_main/agg_trans_bilinear.h"
#include "agg_main/agg_trans_perspective.h"
#include "agg_ctrls/agg_rbox_ctrl.h"
#include "agg_platform/agg_platform_support.h"
#include "agg_extra/interactive_polygon.h"

#define AGG_BGR24 
//#define AGG_RGB24
//#define AGG_BGRA32 
//#define AGG_RGBA32 
//#define AGG_ARGB32 
//#define AGG_ABGR32
//#define AGG_RGB565
//#define AGG_RGB555
#include "agg_main/pixel_formats.h"

enum flip_y_e { flip_y = true };

agg::rasterizer_scanline_aa<> g_rasterizer;
agg::scanline_p8  g_scanline;
agg::path_storage g_path;
agg::rgba8        g_colors[100];
unsigned          g_path_idx[100];
unsigned          g_npaths = 0;
double            g_x1 = 0;
double            g_y1 = 0;
double            g_x2 = 0;
double            g_y2 = 0;
double            g_base_dx = 0;
double            g_base_dy = 0;
double            g_angle = 0;
double            g_scale = 1.0;
double            g_skew_x = 0;
double            g_skew_y = 0;
int               g_nclick = 0;

unsigned parse_lion(agg::path_storage& ps, agg::rgba8* colors, unsigned* path_idx);
void parse_lion()
{
    g_npaths = parse_lion(g_path, g_colors, g_path_idx);
    agg::pod_array_adaptor<unsigned> path_idx(g_path_idx, 100);
    agg::bounding_rect(g_path, path_idx, 0, g_npaths, &g_x1, &g_y1, &g_x2, &g_y2);
    g_base_dx = (g_x2 - g_x1) / 2.0;
    g_base_dy = (g_y2 - g_y1) / 2.0;
    g_path.flip_x(g_x1, g_x2);
    g_path.flip_y(g_y1, g_y2);
}




namespace agg
{
}







class the_application : public agg::platform_support
{
public:
    typedef agg::renderer_base<pixfmt> renderer_base;
    typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;

    agg::interactive_polygon   m_quad;
    agg::rbox_ctrl<agg::rgba8> m_trans_type;

    the_application(agg::pix_format_e format, bool flip_y) :
        agg::platform_support(format, flip_y),
        m_quad(4, 5.0),
        m_trans_type(420, 5.0, 420+130.0, 55.0, !flip_y)
    {
        parse_lion();
        m_quad.xn(0) = g_x1;
        m_quad.yn(0) = g_y1;
        m_quad.xn(1) = g_x2;
        m_quad.yn(1) = g_y1;
        m_quad.xn(2) = g_x2;
        m_quad.yn(2) = g_y2;
        m_quad.xn(3) = g_x1;
        m_quad.yn(3) = g_y2;

        m_trans_type.add_item("Bilinear");
        m_trans_type.add_item("Perspective");
        m_trans_type.cur_item(0);
        add_ctrl(m_trans_type);
    }


    virtual void on_init()
    {
        double dx = width()  / 2.0 - (m_quad.xn(1) - m_quad.xn(0)) / 2.0;
        double dy = height() / 2.0 - (m_quad.yn(2) - m_quad.yn(0)) / 2.0;
        m_quad.xn(0) += dx;
        m_quad.yn(0) += dy;
        m_quad.xn(1) += dx;
        m_quad.yn(1) += dy;
        m_quad.xn(2) += dx;
        m_quad.yn(2) += dy;
        m_quad.xn(3) += dx;
        m_quad.yn(3) += dy;
    }

    virtual void on_draw()
    {
        pixfmt pixf(rbuf_window());
        renderer_base rb(pixf);
        renderer_solid r(rb);
        rb.clear(agg::rgba(1, 1, 1));

        g_rasterizer.clip_box(0, 0, width(), height());

        if(m_trans_type.cur_item() == 0)
        {
            agg::trans_bilinear tr(g_x1, g_y1, g_x2, g_y2, m_quad.polygon());
            if(tr.is_valid())
            {
                //--------------------------
                // Render transformed lion
                //
                agg::conv_transform<agg::path_storage, 
                                    agg::trans_bilinear> trans(g_path, tr);

                agg::render_all_paths(g_rasterizer, g_scanline, r, trans, g_colors, g_path_idx, g_npaths);
                //--------------------------



                //--------------------------
                // Render transformed ellipse
                //
                agg::ellipse ell((g_x1 + g_x2) * 0.5, (g_y1 + g_y2) * 0.5, 
                                 (g_x2 - g_x1) * 0.5, (g_y2 - g_y1) * 0.5,
                                 200);
                agg::conv_stroke<agg::ellipse> ell_stroke(ell);
                ell_stroke.width(3.0);
                agg::conv_transform<agg::ellipse, 
                                    agg::trans_bilinear> trans_ell(ell, tr);

                agg::conv_transform<agg::conv_stroke<agg::ellipse>, 
                                    agg::trans_bilinear> trans_ell_stroke(ell_stroke, tr);

                g_rasterizer.add_path(trans_ell);
                r.color(agg::rgba(0.5, 0.3, 0.0, 0.3));
                agg::render_scanlines(g_rasterizer, g_scanline, r);

                g_rasterizer.add_path(trans_ell_stroke);
                r.color(agg::rgba(0.0, 0.3, 0.2, 1.0));
                agg::render_scanlines(g_rasterizer, g_scanline, r);
                //--------------------------

            }
        }
        else
        {
            agg::trans_perspective tr(g_x1, g_y1, g_x2, g_y2, m_quad.polygon());
            if(tr.is_valid())
            {

                //--------------------------
                // Render transformed lion
                //
                agg::conv_transform<agg::path_storage, 
                                    agg::trans_perspective> trans(g_path, tr);

                agg::render_all_paths(g_rasterizer, g_scanline, r, trans, g_colors, g_path_idx, g_npaths);

                //--------------------------



                //--------------------------
                // Render transformed ellipse
                //
                agg::ellipse ell((g_x1 + g_x2) * 0.5, (g_y1 + g_y2) * 0.5, 
                                 (g_x2 - g_x1) * 0.5, (g_y2 - g_y1) * 0.5,
                                 200);

                agg::conv_stroke<agg::ellipse> ell_stroke(ell);
                ell_stroke.width(3.0);
                agg::conv_transform<agg::ellipse, 
                                    agg::trans_perspective> trans_ell(ell, tr);


                agg::conv_transform<agg::conv_stroke<agg::ellipse>, 
                                    agg::trans_perspective> trans_ell_stroke(ell_stroke, tr);


                g_rasterizer.add_path(trans_ell);
                r.color(agg::rgba(0.5, 0.3, 0.0, 0.3));
                agg::render_scanlines(g_rasterizer, g_scanline, r);

                g_rasterizer.add_path(trans_ell_stroke);
                r.color(agg::rgba(0.0, 0.3, 0.2, 1.0));
                agg::render_scanlines(g_rasterizer, g_scanline, r);
                //--------------------------


                // Testing the reverse transformations
                //agg::trans_perspective tr2(m_quad.polygon(), g_x1, g_y1, g_x2, g_y2);
                //if(tr2.is_valid())
                //{
                //    double x, y;
                //    x = m_quad.xn(0); y = m_quad.yn(0); tr2.transform(&x, &y);
                //    g_rasterizer.move_to_d(x, y);
                //    x = m_quad.xn(1); y = m_quad.yn(1); tr2.transform(&x, &y);
                //    g_rasterizer.line_to_d(x, y);
                //    x = m_quad.xn(2); y = m_quad.yn(2); tr2.transform(&x, &y);
                //    g_rasterizer.line_to_d(x, y);
                //    x = m_quad.xn(3); y = m_quad.yn(3); tr2.transform(&x, &y);
                //    g_rasterizer.line_to_d(x, y);
                //    r.color(agg::rgba(0.5, 0.0, 0.0, 0.5));
                //    agg::render_scanlines(g_rasterizer, g_scanline, r);
                //}
                //else
                //{
                //    message("Singularity...");
                //}
            }
        }




        //--------------------------
        // Render the "quad" tool and controls
        g_rasterizer.add_path(m_quad);
        r.color(agg::rgba(0, 0.3, 0.5, 0.6));
        agg::render_scanlines(g_rasterizer, g_scanline, r);
        agg::render_ctrl(g_rasterizer, g_scanline, rb, m_trans_type);
        //--------------------------
    }



    virtual void on_mouse_button_down(int x, int y, unsigned flags)
    {
        if(flags & agg::mouse_left)
        {
            if(m_quad.on_mouse_button_down(x, y))
            {
                force_redraw();
            }
        }
    }


    virtual void on_mouse_move(int x, int y, unsigned flags)
    {
        if(flags & agg::mouse_left)
        {
            if(m_quad.on_mouse_move(x, y))
            {
                force_redraw();
            }
        }
        if((flags & agg::mouse_left) == 0)
        {
            on_mouse_button_up(x, y, flags);
        }
    }


    virtual void on_mouse_button_up(int x, int y, unsigned flags)
    {
        if(m_quad.on_mouse_button_up(x, y))
        {
            force_redraw();
        }
    }

};






int agg_main(int argc, char* argv[])
{
    the_application app(pix_format, flip_y);
    app.caption("AGG Example. Perspective Transformations");

    if(app.init(600, 600, agg::window_resize))
    {
        return app.run();
    }
    return 1;
}






