#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_u.h"
#include "agg_main/agg_scanline_p.h"
#include "agg_main/agg_renderer_scanline.h"
#include "agg_main/agg_span_allocator.h"
#include "agg_main/agg_span_gouraud_rgba.h"
#include "agg_main/agg_span_gouraud_gray.h"
#include "agg_main/agg_span_solid.h"
#include "agg_platform/agg_platform_support.h"

#include "agg_ctrls/agg_slider_ctrl.h"


//#define AGG_GRAY8
#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 };


#include "agg_main/agg_math.h"
#include "agg_main/agg_dda_line.h"


class the_application : public agg::platform_support
{
    double m_x[3];
    double m_y[3];
    double m_dx;
    double m_dy;
    int    m_idx;

    agg::slider_ctrl<agg::rgba> m_dilation;
    agg::slider_ctrl<agg::rgba> m_gamma;
    agg::slider_ctrl<agg::rgba> m_alpha;


public:
    the_application(agg::pix_format_e format, bool flip_y) :
        agg::platform_support(format, flip_y),
        m_idx(-1),
        m_dilation(5, 5,    400-5, 11,    !flip_y),
        m_gamma   (5, 5+15, 400-5, 11+15, !flip_y),
        m_alpha   (5, 5+30, 400-5, 11+30, !flip_y)

    {
        m_x[0] = 57;    m_y[0] = 60;
        m_x[1] = 369;   m_y[1] = 170;
        m_x[2] = 143;   m_y[2] = 310;
       
        add_ctrl(m_dilation);
        add_ctrl(m_gamma);
        add_ctrl(m_alpha);

        m_dilation.label("Dilation=%3.2f");
        m_gamma.label("Linear gamma=%3.2f");
        m_alpha.label("Opacity=%3.2f");

        m_dilation.value(0.175);
        m_gamma.value(0.809);
        m_alpha.value(1.0);
    }


    template<class Scanline, class Ras> 
    void render_gouraud(Scanline& sl, Ras& ras)
    {
        double alpha = m_alpha.value();
        double brc = 1;

        typedef agg::renderer_base<pixfmt> base_ren_type;
#ifdef AGG_GRAY8
        typedef agg::span_gouraud_gray<color_type> span_gen_type;
#else
        typedef agg::span_gouraud_rgba<color_type> span_gen_type;
#endif
        typedef agg::span_allocator<color_type> span_alloc_type;
        
        pixfmt pf(rbuf_window());
        base_ren_type ren_base(pf);

        span_alloc_type span_alloc;
        span_gen_type   span_gen;

        ras.gamma(agg::gamma_linear(0.0, m_gamma.value()));

        double d = m_dilation.value();

        // Single triangle
        //span_gen.colors(agg::rgba(1,   0,   0,  alpha),
        //                agg::rgba(0,   1,   0,  alpha),
        //                agg::rgba(0,   0,   1,  alpha));
        //span_gen.triangle(m_x[0], m_y[0], m_x[1], m_y[1], m_x[2], m_y[2], d);
        //ras.add_path(span_gen);
        //agg::render_scanlines_aa(ras, sl, ren_base, span_alloc, span_gen);


        // Six triangles
        double xc = (m_x[0] + m_x[1] + m_x[2]) / 3.0;
        double yc = (m_y[0] + m_y[1] + m_y[2]) / 3.0;

        double x1 = (m_x[1] + m_x[0]) / 2 - (xc - (m_x[1] + m_x[0]) / 2);
        double y1 = (m_y[1] + m_y[0]) / 2 - (yc - (m_y[1] + m_y[0]) / 2);

        double x2 = (m_x[2] + m_x[1]) / 2 - (xc - (m_x[2] + m_x[1]) / 2);
        double y2 = (m_y[2] + m_y[1]) / 2 - (yc - (m_y[2] + m_y[1]) / 2);

        double x3 = (m_x[0] + m_x[2]) / 2 - (xc - (m_x[0] + m_x[2]) / 2);
        double y3 = (m_y[0] + m_y[2]) / 2 - (yc - (m_y[0] + m_y[2]) / 2);

        span_gen.colors(agg::rgba(1,   0,   0,    alpha),
                        agg::rgba(0,   1,   0,    alpha),
                        agg::rgba(brc, brc, brc,  alpha));
        span_gen.triangle(m_x[0], m_y[0], m_x[1], m_y[1], xc, yc, d);
        ras.add_path(span_gen);
        agg::render_scanlines_aa(ras, sl, ren_base, span_alloc, span_gen);


        span_gen.colors(agg::rgba(0,   1,   0,    alpha),
                        agg::rgba(0,   0,   1,    alpha),
                        agg::rgba(brc, brc, brc,  alpha));
        span_gen.triangle(m_x[1], m_y[1], m_x[2], m_y[2], xc, yc, d);
        ras.add_path(span_gen);
        agg::render_scanlines_aa(ras, sl, ren_base, span_alloc, span_gen);


        span_gen.colors(agg::rgba(0,   0,   1,   alpha),
                        agg::rgba(1,   0,   0,   alpha),
                        agg::rgba(brc, brc, brc, alpha));
        span_gen.triangle(m_x[2], m_y[2], m_x[0], m_y[0], xc, yc, d);
        ras.add_path(span_gen);
        agg::render_scanlines_aa(ras, sl, ren_base, span_alloc, span_gen);


        brc = 1-brc;
        span_gen.colors(agg::rgba(1,   0,   0,    alpha),
                        agg::rgba(0,   1,   0,    alpha),
                        agg::rgba(brc, brc, brc,  alpha));
        span_gen.triangle(m_x[0], m_y[0], m_x[1], m_y[1], x1, y1, d);
        ras.add_path(span_gen);
        agg::render_scanlines_aa(ras, sl, ren_base, span_alloc, span_gen);


        span_gen.colors(agg::rgba(0,   1,   0,    alpha),
                        agg::rgba(0,   0,   1,    alpha),
                        agg::rgba(brc, brc, brc,  alpha));
        span_gen.triangle(m_x[1], m_y[1], m_x[2], m_y[2], x2, y2, d);
        ras.add_path(span_gen);
        agg::render_scanlines_aa(ras, sl, ren_base, span_alloc, span_gen);


        span_gen.colors(agg::rgba(0,   0,   1,    alpha),
                        agg::rgba(1,   0,   0,    alpha),
                        agg::rgba(brc, brc, brc,  alpha));
        span_gen.triangle(m_x[2], m_y[2], m_x[0], m_y[0], x3, y3, d);
        ras.add_path(span_gen);
        agg::render_scanlines_aa(ras, sl, ren_base, span_alloc, span_gen);
    }








    virtual void on_draw()
    {
        typedef agg::renderer_base<pixfmt> base_ren_type;

        pixfmt pf(rbuf_window());
        base_ren_type ren_base(pf);
        ren_base.clear(agg::rgba(1,1,1));

        agg::scanline_u8 sl;
        agg::rasterizer_scanline_aa<> ras;

        render_gouraud(sl, ras);

        ras.gamma(agg::gamma_none());
        agg::render_ctrl(ras, sl, ren_base, m_dilation);
        agg::render_ctrl(ras, sl, ren_base, m_gamma);
        agg::render_ctrl(ras, sl, ren_base, m_alpha);
    }


    virtual void on_mouse_button_down(int x, int y, unsigned flags)
    {
        unsigned i;
        if(flags & agg::mouse_right)
        {
            agg::scanline_u8 sl;
            agg::rasterizer_scanline_aa<> ras;
            start_timer();
            for(i = 0; i < 100; i++)
            {
                render_gouraud(sl, ras);
            }
            char buf[100];
            sprintf(buf, "Time=%2.2f ms", elapsed_time());
            message(buf);
        }

        if(flags & agg::mouse_left)
        {
            for (i = 0; i < 3; i++)
            {
                if(sqrt( (x-m_x[i]) * (x-m_x[i]) + (y-m_y[i]) * (y-m_y[i]) ) < 10.0)
                {
                    m_dx = x - m_x[i];
                    m_dy = y - m_y[i];
                    m_idx = i;
                    break;
                }
            }
            if(i == 3)
            {
                if(agg::point_in_triangle(m_x[0], m_y[0], 
                                          m_x[1], m_y[1],
                                          m_x[2], m_y[2],
                                          x, y))
                {
                    m_dx = x - m_x[0];
                    m_dy = y - m_y[0];
                    m_idx = 3;
                }

            }
        }
    }


    virtual void on_mouse_move(int x, int y, unsigned flags)
    {
        if(flags & agg::mouse_left)
        {
            if(m_idx == 3)
            {
                double dx = x - m_dx;
                double dy = y - m_dy;
                m_x[1] -= m_x[0] - dx;
                m_y[1] -= m_y[0] - dy;
                m_x[2] -= m_x[0] - dx;
                m_y[2] -= m_y[0] - dy;
                m_x[0] = dx;
                m_y[0] = dy;
                force_redraw();
                return;
            }

            if(m_idx >= 0)
            {
                m_x[m_idx] = x - m_dx;
                m_y[m_idx] = y - m_dy;
                force_redraw();
            }
        }
        else
        {
            on_mouse_button_up(x, y, flags);
        }
    }

    virtual void on_mouse_button_up(int x, int y, unsigned flags)
    {
        m_idx = -1;
    }

    
    virtual void on_key(int x, int y, unsigned key, unsigned flags)
    {
        double dx = 0;
        double dy = 0;
        switch(key)
        {
        case agg::key_left:  dx = -0.1; break;
        case agg::key_right: dx =  0.1; break;
        case agg::key_up:    dy =  0.1; break;
        case agg::key_down:  dy = -0.1; break;
        }
        m_x[0] += dx;
        m_y[0] += dy;
        m_x[1] += dx;
        m_y[1] += dy;
        force_redraw();
    }


};


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

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


