while(i--)
        {
            if(m_specific->m_buf_img[i])
            {
                delete [] m_specific->m_buf_img[i];
            }
        }

// Contact: example@nnthink.com
//          example@nnthink.com

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <ctime>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include "agg_basics.h"
#include "agg_pixfmt_gray.h"
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "util/agg_color_conv_rgb8.h"
#include "platform/agg_platform_support.h"

namespace agg
{
    class platform_specific
    {
    public:
        platform_specific(pix_format_e format, bool flip_y);
        ~platform_specific();

        void caption(const char* capt);
        void put_image(const rendering_buffer* src);

        pix_format_e         m_format;
        pix_format_e         m_sys_format;
        int                  m_byte_order;
        bool                 m_flip_y;
        unsigned             m_bpp;
        unsigned             m_sys_bpp;
        Display*             m_display;
        int                  m_screen;
        int                  m_depth;
        Visual*              m_visual;
        Window               m_window;
        GC                   m_gc;
        XImage*              m_ximg_window;
        XSetWindowAttributes m_window_attributes;
        Atom                 m_close_atom;
        unsigned char*       m_buf_window;
        unsigned char*       m_buf_img[platform_support::max_images];
        unsigned             m_keymap[256];

        bool m_update_flag;
        bool m_resize_flag;
        bool m_initialized;
        //bool m_wait_mode;
        std::clock_t m_sw_start;
    };

    platform_specific::platform_specific(pix_format_e format, bool flip_y) :
        m_format(format),
        m_sys_format(pix_format_undefined),
        m_byte_order(LSBFirst),
        m_flip_y(flip_y),
        m_bpp(0),
        m_sys_bpp(0),
        m_display(0),
        m_screen(0),
        m_depth(0),
        m_visual(0),
        m_window(0),
        m_gc(0),
        m_ximg_window(0),
        m_close_atom(0),

        m_buf_window(0),

        m_update_flag(true),
        m_resize_flag(true),
        m_initialized(false)
    {
        std::memset(m_buf_img, 0, sizeof(m_buf_img));

        unsigned i;
        for(i = 0; i < 256; i++)
        {
            m_keymap[i] = i;
        }

        m_keymap[XK_Pause&0xFF] = key_pause;
        m_keymap[XK_Clear&0xFF] = key_clear;

        m_keymap[XK_KP_0&0xFF] = key_kp0;
        m_keymap[XK_KP_1&0xFF] = key_kp1;
        m_keymap[XK_KP_2&0xFF] = key_kp2;
        m_keymap[XK_KP_3&0xFF] = key_kp3;
        m_keymap[XK_KP_4&0xFF] = key_kp4;
        m_keymap[XK_KP_5&0xFF] = key_kp5;
        m_keymap[XK_KP_6&0xFF] = key_kp6;
        m_keymap[XK_KP_7&0xFF] = key_kp7;
        m_keymap[XK_KP_8&0xFF] = key_kp8;
        m_keymap[XK_KP_9&0xFF] = key_kp9;

        m_keymap[XK_KP_Insert&0xFF]    = key_kp0;
        m_keymap[XK_KP_End&0xFF]       = key_kp1;
        m_keymap[XK_KP_Down&0xFF]      = key_kp2;
        m_keymap[XK_KP_Page_Down&0xFF] = key_kp3;
        m_keymap[XK_KP_Left&0xFF]      = key_kp4;
        m_keymap[XK_KP_Begin&0xFF]     = key_kp5;
        m_keymap[XK_KP_Right&0xFF]     = key_kp6;
        m_keymap[XK_KP_Home&0xFF]      = key_kp7;
        m_keymap[XK_KP_Up&0xFF]        = key_kp8;
        m_keymap[XK_KP_Page_Up&0xFF]   = key_kp9;
        m_keymap[XK_KP_Delete&0xFF]    = key_kp_period;
        m_keymap[XK_KP_Decimal&0xFF]   = key_kp_period;
        m_keymap[XK_KP_Divide&0xFF]    = key_kp_divide;
        m_keymap[XK_KP_Multiply&0xFF]  = key_kp_multiply;
        m_keymap[XK_KP_Subtract&0xFF]  = key_kp_minus;
        m_keymap[XK_KP_Add&0xFF]       = key_kp_plus;
        m_keymap[XK_KP_Enter&0xFF]     = key_kp_enter;
        m_keymap[XK_KP_Equal&0xFF]     = key_kp_equals;

        m_keymap[XK_Up&0xFF]           = key_up;
        m_keymap[XK_Down&0xFF]         = key_down;
        m_keymap[XK_Right&0xFF]        = key_right;
        m_keymap[XK_Left&0xFF]         = key_left;
        m_keymap[XK_Insert&0xFF]       = key_insert;
        m_keymap[XK_Home&0xFF]         = key_delete;
        m_keymap[XK_End&0xFF]          = key_end;
        m_keymap[XK_Page_Up&0xFF]      = key_page_up;
        m_keymap[XK_Page_Down&0xFF]    = key_page_down;

        m_keymap[XK_F1&0xFF]           = key_f1;
        m_keymap[XK_F2&0xFF]           = key_f2;
        m_keymap[XK_F3&0xFF]           = key_f3;
        m_keymap[XK_F4&0xFF]           = key_f4;
        m_keymap[XK_F5&0xFF]           = key_f5;
        m_keymap[XK_F6&0xFF]           = key_f6;
        m_keymap[XK_F7&0xFF]           = key_f7;
        m_keymap[XK_F8&0xFF]           = key_f8;
        m_keymap[XK_F9&0xFF]           = key_f9;
        m_keymap[XK_F10&0xFF]          = key_f10;
        m_keymap[XK_F11&0xFF]          = key_f11;
        m_keymap[XK_F12&0xFF]          = key_f12;
        m_keymap[XK_F13&0xFF]          = key_f13;
        m_keymap[XK_F14&0xFF]          = key_f14;
        m_keymap[XK_F15&0xFF]          = key_f15;

        m_keymap[XK_Num_Lock&0xFF]     = key_numlock;
        m_keymap[XK_Caps_Lock&0xFF]    = key_capslock;
        m_keymap[XK_Scroll_Lock&0xFF]  = key_scrollock;

        switch(m_format)
        {
        default: break;
        case pix_format_gray8:
        case pix_format_sgray8:
            m_bpp = 8;
            break;

        case pix_format_gray16:
            m_bpp = 16;
            break;

        case pix_format_gray32:
            m_bpp = 32;
            break;

        case pix_format_rgb565:
        case pix_format_rgb555:
            m_bpp = 16;
            break;

        case pix_format_rgb24:
        case pix_format_bgr24:
        case pix_format_srgb24:
        case pix_format_sbgr24:
            m_bpp = 24;
            break;

        case pix_format_bgra32:
        case pix_format_abgr32:
        case pix_format_argb32:
        case pix_format_rgba32:
        case pix_format_sbgra32:
        case pix_format_sabgr32:
        case pix_format_sargb32:
        case pix_format_srgba32:
            m_bpp = 32;
            break;

        case pix_format_rgb48:
        case pix_format_bgr48:
            m_bpp = 48;
            break;

        case pix_format_bgra64:
        case pix_format_abgr64:
        case pix_format_argb64:
        case pix_format_rgba64:
            m_bpp = 64;
            break;

        case pix_format_rgb96:
        case pix_format_bgr96:
            m_bpp = 96;
            break;

        case pix_format_bgra128:
        case pix_format_abgr128:
        case pix_format_argb128:
        case pix_format_rgba128:
            m_bpp = 128;
            break;
        }
        m_sw_start = std::clock();
    }

    platform_specific::~platform_specific()
    {
    }

    void platform_specific::caption(const char* capt)
    {
        XTextProperty tp;
        tp.value = (unsigned char *)capt;
        tp.encoding = XA_WM_NAME;
        tp.format = 8;
        tp.nitems = std::strlen(capt);
        XSetWMName(m_display, m_window, &tp);
        XStoreName(m_display, m_window, capt);
        XSetIconName(m_display, m_window, capt);
        XSetWMIconName(m_display, m_window, &tp);
    }

    void platform_specific::put_image(const rendering_buffer* src)
    {
        if(m_ximg_window == 0) return;
        m_ximg_window->data = (char*)m_buf_window;

        if(m_format == m_sys_format)
        {
            XPutImage(m_display,
                      m_window,
                      m_gc,
                      m_ximg_window,
                      0, 0, 0, 0,
                      src->width(),
                      src->height());
        }
        else
        {
            int row_len = src->width() * m_sys_bpp / 8;
            unsigned char* buf_tmp =
                new unsigned char[row_len * src->height()];

            rendering_buffer rbuf_tmp;
            rbuf_tmp.attach(buf_tmp,
                            src->width(),
                            src->height(),
                            m_flip_y ? -row_len : row_len);

            switch(m_sys_format)
            {
                default: break;
                case pix_format_rgb555:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_rgb555()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgb555()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_rgb555());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_rgb555());  break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_rgb555()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_rgb555()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_rgb555()); break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_rgb555()); break;
                    }
                    break;

                case pix_format_rgb565:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_rgb565()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgb565()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_rgb565());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_rgb565());  break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_rgb565()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_rgb565()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_rgb565()); break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_rgb565()); break;
                    }
                    break;

                case pix_format_rgba32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_sgray8:   convert<pixfmt_srgba32, pixfmt_sgray8>(&rbuf_tmp, src);    break;
                        case pix_format_gray8:    convert<pixfmt_srgba32, pixfmt_gray8>(&rbuf_tmp, src);     break;
                        case pix_format_gray16:   convert<pixfmt_srgba32, pixfmt_gray16>(&rbuf_tmp, src);    break;
                        case pix_format_gray32:   convert<pixfmt_srgba32, pixfmt_gray32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb555:   color_conv(&rbuf_tmp, src, color_conv_rgb555_to_rgba32()); break;
                        case pix_format_rgb565:   color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgba32()); break;
                        case pix_format_srgb24:   convert<pixfmt_srgba32, pixfmt_srgb24>(&rbuf_tmp, src);    break;
                        case pix_format_sbgr24:   convert<pixfmt_srgba32, pixfmt_sbgr24>(&rbuf_tmp, src);    break;
                        case pix_format_rgb24:    convert<pixfmt_srgba32, pixfmt_rgb24>(&rbuf_tmp, src);     break;
                        case pix_format_bgr24:    convert<pixfmt_srgba32, pixfmt_bgr24>(&rbuf_tmp, src);     break;
                        case pix_format_srgba32:  convert<pixfmt_srgba32, pixfmt_srgba32>(&rbuf_tmp, src);   break;
                        case pix_format_sargb32:  convert<pixfmt_srgba32, pixfmt_sargb32>(&rbuf_tmp, src);   break;
                        case pix_format_sabgr32:  convert<pixfmt_srgba32, pixfmt_sabgr32>(&rbuf_tmp, src);   break;
                        case pix_format_sbgra32:  convert<pixfmt_srgba32, pixfmt_sbgra32>(&rbuf_tmp, src);   break;
                        case pix_format_rgba32:   convert<pixfmt_srgba32, pixfmt_rgba32>(&rbuf_tmp, src);    break;
                        case pix_format_argb32:   convert<pixfmt_srgba32, pixfmt_argb32>(&rbuf_tmp, src);    break;
                        case pix_format_abgr32:   convert<pixfmt_srgba32, pixfmt_abgr32>(&rbuf_tmp, src);    break;
                        case pix_format_bgra32:   convert<pixfmt_srgba32, pixfmt_bgra32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb48:    convert<pixfmt_srgba32, pixfmt_rgb48>(&rbuf_tmp, src);     break;
                        case pix_format_bgr48:    convert<pixfmt_srgba32, pixfmt_bgr48>(&rbuf_tmp, src);     break;
                        case pix_format_rgba64:   convert<pixfmt_srgba32, pixfmt_rgba64>(&rbuf_tmp, src);    break;
                        case pix_format_argb64:   convert<pixfmt_srgba32, pixfmt_argb64>(&rbuf_tmp, src);    break;
                        case pix_format_abgr64:   convert<pixfmt_srgba32, pixfmt_abgr64>(&rbuf_tmp, src);    break;
                        case pix_format_bgra64:   convert<pixfmt_srgba32, pixfmt_bgra64>(&rbuf_tmp, src);    break;
                        case pix_format_rgb96:    convert<pixfmt_srgba32, pixfmt_rgb96>(&rbuf_tmp, src);     break;
                        case pix_format_bgr96:    convert<pixfmt_srgba32, pixfmt_bgr96>(&rbuf_tmp, src);     break;
                        case pix_format_rgba128:  convert<pixfmt_srgba32, pixfmt_rgba128>(&rbuf_tmp, src);   break;
                        case pix_format_argb128:  convert<pixfmt_srgba32, pixfmt_argb128>(&rbuf_tmp, src);   break;
                        case pix_format_abgr128:  convert<pixfmt_srgba32, pixfmt_abgr128>(&rbuf_tmp, src);   break;
                        case pix_format_bgra128:  convert<pixfmt_srgba32, pixfmt_bgra128>(&rbuf_tmp, src);   break;
                    }
                    break;

                case pix_format_abgr32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_sgray8:   convert<pixfmt_sabgr32, pixfmt_sgray8>(&rbuf_tmp, src);    break;
                        case pix_format_gray8:    convert<pixfmt_sabgr32, pixfmt_gray8>(&rbuf_tmp, src);     break;
                        case pix_format_gray16:   convert<pixfmt_sabgr32, pixfmt_gray16>(&rbuf_tmp, src);    break;
                        case pix_format_gray32:   convert<pixfmt_sabgr32, pixfmt_gray32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_abgr32()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_abgr32()); break;
                        case pix_format_srgb24:   convert<pixfmt_sabgr32, pixfmt_srgb24>(&rbuf_tmp, src);    break;
                        case pix_format_sbgr24:   convert<pixfmt_sabgr32, pixfmt_sbgr24>(&rbuf_tmp, src);    break;
                        case pix_format_rgb24:    convert<pixfmt_sabgr32, pixfmt_rgb24>(&rbuf_tmp, src);     break;
                        case pix_format_bgr24:    convert<pixfmt_sabgr32, pixfmt_bgr24>(&rbuf_tmp, src);     break;
                        case pix_format_srgba32:  convert<pixfmt_sabgr32, pixfmt_srgba32>(&rbuf_tmp, src);   break;
                        case pix_format_sargb32:  convert<pixfmt_sabgr32, pixfmt_sargb32>(&rbuf_tmp, src);   break;
                        case pix_format_sabgr32:  convert<pixfmt_sabgr32, pixfmt_sabgr32>(&rbuf_tmp, src);   break;
                        case pix_format_sbgra32:  convert<pixfmt_sabgr32, pixfmt_sbgra32>(&rbuf_tmp, src);   break;
                        case pix_format_rgba32:   convert<pixfmt_sabgr32, pixfmt_rgba32>(&rbuf_tmp, src);    break;
                        case pix_format_argb32:   convert<pixfmt_sabgr32, pixfmt_argb32>(&rbuf_tmp, src);    break;
                        case pix_format_abgr32:   convert<pixfmt_sabgr32, pixfmt_abgr32>(&rbuf_tmp, src);    break;
                        case pix_format_bgra32:   convert<pixfmt_sabgr32, pixfmt_bgra32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb48:    convert<pixfmt_sabgr32, pixfmt_rgb48>(&rbuf_tmp, src);     break;
                        case pix_format_bgr48:    convert<pixfmt_sabgr32, pixfmt_bgr48>(&rbuf_tmp, src);     break;
                        case pix_format_rgba64:   convert<pixfmt_sabgr32, pixfmt_rgba64>(&rbuf_tmp, src);    break;
                        case pix_format_argb64:   convert<pixfmt_sabgr32, pixfmt_argb64>(&rbuf_tmp, src);    break;
                        case pix_format_abgr64:   convert<pixfmt_sabgr32, pixfmt_abgr64>(&rbuf_tmp, src);    break;
                        case pix_format_bgra64:   convert<pixfmt_sabgr32, pixfmt_bgra64>(&rbuf_tmp, src);    break;
                        case pix_format_rgb96:    convert<pixfmt_sabgr32, pixfmt_rgb96>(&rbuf_tmp, src);     break;
                        case pix_format_bgr96:    convert<pixfmt_sabgr32, pixfmt_bgr96>(&rbuf_tmp, src);     break;
                        case pix_format_rgba128:  convert<pixfmt_sabgr32, pixfmt_rgba128>(&rbuf_tmp, src);   break;
                        case pix_format_argb128:  convert<pixfmt_sabgr32, pixfmt_argb128>(&rbuf_tmp, src);   break;
                        case pix_format_abgr128:  convert<pixfmt_sabgr32, pixfmt_abgr128>(&rbuf_tmp, src);   break;
                        case pix_format_bgra128:  convert<pixfmt_sabgr32, pixfmt_bgra128>(&rbuf_tmp, src);   break;
                    }
                    break;

                case pix_format_argb32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_sgray8:   convert<pixfmt_sargb32, pixfmt_sgray8>(&rbuf_tmp, src);    break;
                        case pix_format_gray8:    convert<pixfmt_sargb32, pixfmt_gray8>(&rbuf_tmp, src);     break;
                        case pix_format_gray16:   convert<pixfmt_sargb32, pixfmt_gray16>(&rbuf_tmp, src);    break;
                        case pix_format_gray32:   convert<pixfmt_sargb32, pixfmt_gray32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_argb32()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_argb32()); break;
                        case pix_format_srgb24:   convert<pixfmt_sargb32, pixfmt_srgb24>(&rbuf_tmp, src);    break;
                        case pix_format_sbgr24:   convert<pixfmt_sargb32, pixfmt_sbgr24>(&rbuf_tmp, src);    break;
                        case pix_format_rgb24:    convert<pixfmt_sargb32, pixfmt_rgb24>(&rbuf_tmp, src);     break;
                        case pix_format_bgr24:    convert<pixfmt_sargb32, pixfmt_bgr24>(&rbuf_tmp, src);     break;
                        case pix_format_srgba32:  convert<pixfmt_sargb32, pixfmt_srgba32>(&rbuf_tmp, src);   break;
                        case pix_format_sargb32:  convert<pixfmt_sargb32, pixfmt_sargb32>(&rbuf_tmp, src);   break;
                        case pix_format_sabgr32:  convert<pixfmt_sargb32, pixfmt_sabgr32>(&rbuf_tmp, src);   break;
                        case pix_format_sbgra32:  convert<pixfmt_sargb32, pixfmt_sbgra32>(&rbuf_tmp, src);   break;
                        case pix_format_rgba32:   convert<pixfmt_sargb32, pixfmt_rgba32>(&rbuf_tmp, src);    break;
                        case pix_format_argb32:   convert<pixfmt_sargb32, pixfmt_argb32>(&rbuf_tmp, src);    break;
                        case pix_format_abgr32:   convert<pixfmt_sargb32, pixfmt_abgr32>(&rbuf_tmp, src);    break;
                        case pix_format_bgra32:   convert<pixfmt_sargb32, pixfmt_bgra32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb48:    convert<pixfmt_sargb32, pixfmt_rgb48>(&rbuf_tmp, src);     break;
                        case pix_format_bgr48:    convert<pixfmt_sargb32, pixfmt_bgr48>(&rbuf_tmp, src);     break;
                        case pix_format_rgba64:   convert<pixfmt_sargb32, pixfmt_rgba64>(&rbuf_tmp, src);    break;
                        case pix_format_argb64:   convert<pixfmt_sargb32, pixfmt_argb64>(&rbuf_tmp, src);    break;
                        case pix_format_abgr64:   convert<pixfmt_sargb32, pixfmt_abgr64>(&rbuf_tmp, src);    break;
                        case pix_format_bgra64:   convert<pixfmt_sargb32, pixfmt_bgra64>(&rbuf_tmp, src);    break;
                        case pix_format_rgb96:    convert<pixfmt_sargb32, pixfmt_rgb96>(&rbuf_tmp, src);     break;
                        case pix_format_bgr96:    convert<pixfmt_sargb32, pixfmt_bgr96>(&rbuf_tmp, src);     break;
                        case pix_format_rgba128:  convert<pixfmt_sargb32, pixfmt_rgba128>(&rbuf_tmp, src);   break;
                        case pix_format_argb128:  convert<pixfmt_sargb32, pixfmt_argb128>(&rbuf_tmp, src);   break;
                        case pix_format_abgr128:  convert<pixfmt_sargb32, pixfmt_abgr128>(&rbuf_tmp, src);   break;
                        case pix_format_bgra128:  convert<pixfmt_sargb32, pixfmt_bgra128>(&rbuf_tmp, src);   break;
                    }
                    break;

                case pix_format_bgra32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_sgray8:   convert<pixfmt_sbgra32, pixfmt_sgray8>(&rbuf_tmp, src);    break;
                        case pix_format_gray8:    convert<pixfmt_sbgra32, pixfmt_gray8>(&rbuf_tmp, src);     break;
                        case pix_format_gray16:   convert<pixfmt_sbgra32, pixfmt_gray16>(&rbuf_tmp, src);    break;
                        case pix_format_gray32:   convert<pixfmt_sbgra32, pixfmt_gray32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb555:   color_conv(&rbuf_tmp, src, color_conv_rgb555_to_bgra32()); break;
                        case pix_format_rgb565:   color_conv(&rbuf_tmp, src, color_conv_rgb565_to_bgra32()); break;
                        case pix_format_srgb24:   convert<pixfmt_sbgra32, pixfmt_srgb24>(&rbuf_tmp, src);    break;
                        case pix_format_sbgr24:   convert<pixfmt_sbgra32, pixfmt_sbgr24>(&rbuf_tmp, src);    break;
                        case pix_format_rgb24:    convert<pixfmt_sbgra32, pixfmt_rgb24>(&rbuf_tmp, src);     break;
                        case pix_format_bgr24:    convert<pixfmt_sbgra32, pixfmt_bgr24>(&rbuf_tmp, src);     break;
                        case pix_format_srgba32:  convert<pixfmt_sbgra32, pixfmt_srgba32>(&rbuf_tmp, src);   break;
                        case pix_format_sargb32:  convert<pixfmt_sbgra32, pixfmt_sargb32>(&rbuf_tmp, src);   break;
                        case pix_format_sabgr32:  convert<pixfmt_sbgra32, pixfmt_sabgr32>(&rbuf_tmp, src);   break;
                        case pix_format_sbgra32:  convert<pixfmt_sbgra32, pixfmt_sbgra32>(&rbuf_tmp, src);   break;
                        case pix_format_rgba32:   convert<pixfmt_sbgra32, pixfmt_rgba32>(&rbuf_tmp, src);    break;
                        case pix_format_argb32:   convert<pixfmt_sbgra32, pixfmt_argb32>(&rbuf_tmp, src);    break;
                        case pix_format_abgr32:   convert<pixfmt_sbgra32, pixfmt_abgr32>(&rbuf_tmp, src);    break;
                        case pix_format_bgra32:   convert<pixfmt_sbgra32, pixfmt_bgra32>(&rbuf_tmp, src);    break;
                        case pix_format_rgb48:    convert<pixfmt_sbgra32, pixfmt_rgb48>(&rbuf_tmp, src);     break;
                        case pix_format_bgr48:    convert<pixfmt_sbgra32, pixfmt_bgr48>(&rbuf_tmp, src);     break;
                        case pix_format_rgba64:   convert<pixfmt_sbgra32, pixfmt_rgba64>(&rbuf_tmp, src);    break;
                        case pix_format_argb64:   convert<pixfmt_sbgra32, pixfmt_argb64>(&rbuf_tmp, src);    break;
                        case pix_format_abgr64:   convert<pixfmt_sbgra32, pixfmt_abgr64>(&rbuf_tmp, src);    break;
                        case pix_format_bgra64:   convert<pixfmt_sbgra32, pixfmt_bgra64>(&rbuf_tmp, src);    break;
                        case pix_format_rgb96:    convert<pixfmt_sbgra32, pixfmt_rgb96>(&rbuf_tmp, src);     break;
                        case pix_format_bgr96:    convert<pixfmt_sbgra32, pixfmt_bgr96>(&rbuf_tmp, src);     break;
                        case pix_format_rgba128:  convert<pixfmt_sbgra32, pixfmt_rgba128>(&rbuf_tmp, src);   break;
                        case pix_format_argb128:  convert<pixfmt_sbgra32, pixfmt_argb128>(&rbuf_tmp, src);   break;
                        case pix_format_abgr128:  convert<pixfmt_sbgra32, pixfmt_abgr128>(&rbuf_tmp, src);   break;
                        case pix_format_bgra128:  convert<pixfmt_sbgra32, pixfmt_bgra128>(&rbuf_tmp, src);   break;
                    }
                    break;
            }

            m_ximg_window->data = (char*)buf_tmp;
            XPutImage(m_display,
                      m_window,
                      m_gc,
                      m_ximg_window,
                      0, 0, 0, 0,
                      src->width(),
                      src->height());

            delete [] buf_tmp;
        }
    }

    platform_support::platform_support(pix_format_e format, bool flip_y) :
        m_specific(new platform_specific(format, flip_y)),
        m_format(format),
        m_bpp(m_specific->m_bpp),
        m_window_flags(0),
        m_wait_mode(true),
        m_flip_y(flip_y),
        m_initial_width(10),
        m_initial_height(10)
    {
        std::strcpy(m_caption, "AGG Application");
    }

    platform_support::~platform_support()
    {
        delete m_specific;
    }

    void platform_support::caption(const char* cap)
    {
        std::strcpy(m_caption, cap);
        if(m_specific->m_initialized)
        {
            m_specific->caption(cap);
        }
    }

    enum xevent_mask_e
    {
        xevent_mask =
            PointerMotionMask|
            ButtonPressMask|
            ButtonReleaseMask|
            ExposureMask|
            KeyPressMask|
            StructureNotifyMask
    };

    bool platform_support::init(unsigned width, unsigned height, unsigned flags)
    {
        m_window_flags = flags;

        m_specific->m_display = XOpenDisplay(NULL);
        if(m_specific->m_display == 0)
        {
            std::fprintf(stderr, "Unable to open DISPLAY!\n");
            return false;
        }

        m_specific->m_screen = XDefaultScreen(m_specific->m_display);
        m_specific->m_depth  = XDefaultDepth(m_specific->m_display,
                                             m_specific->m_screen);
        m_specific->m_visual = XDefaultVisual(m_specific->m_display,
                                              m_specific->m_screen);
        unsigned long r_mask = m_specific->m_visual->red_mask;
        unsigned long g_mask = m_specific->m_visual->green_mask;
        unsigned long b_mask = m_specific->m_visual->blue_mask;

//         // NOT COMPLETED YET!
//         // Try to find an appropriate Visual if the default doesn't fit.
//             // This is an attempt to find an appropriate Visual if
//             // the default one doesn't match the minumum requirements
//                 XVisualInfo vi;

        if(m_specific->m_depth < 15 ||
           r_mask == 0 || g_mask == 0 || b_mask == 0)
        {
            std::fprintf(stderr,
                   "There's no Visual compatible with minimal AGG requirements:\n"
                   "At least 15-bit color depth and True- or DirectColor class.\n\n");
            XCloseDisplay(m_specific->m_display);
            return false;
        }

        int t = 1;
        int hw_byte_order = LSBFirst;
        if(*(char*)&t == 0) hw_byte_order = MSBFirst;

        // Perceive SYS-format by mask
        switch(m_specific->m_depth)
        {
            case 15:
                m_specific->m_sys_bpp = 16;
                if(r_mask == 0x7C00 && g_mask == 0x3E0 && b_mask == 0x1F)
                {
                    m_specific->m_sys_format = pix_format_rgb555;
                    m_specific->m_byte_order = hw_byte_order;
                }
                break;

            case 16:
                m_specific->m_sys_bpp = 16;
                if(r_mask == 0xF800 && g_mask == 0x7E0 && b_mask == 0x1F)
                {
                    m_specific->m_sys_format = pix_format_rgb565;
                    m_specific->m_byte_order = hw_byte_order;
                }
                break;

            case 24:
            case 32:
                m_specific->m_sys_bpp = 32;
                if(g_mask == 0xFF00)
                {
                    if(r_mask == 0xFF && b_mask == 0xFF0000)
                    {
                        switch(m_specific->m_format)
                        {
                            case pix_format_rgba32:
                                m_specific->m_sys_format = pix_format_rgba32;
                                m_specific->m_byte_order = LSBFirst;
                                break;

                            case pix_format_abgr32:
                                m_specific->m_sys_format = pix_format_abgr32;
                                m_specific->m_byte_order = MSBFirst;
                                break;

                            default:
                                m_specific->m_byte_order = hw_byte_order;
                                m_specific->m_sys_format =
                                    (hw_byte_order == LSBFirst) ?
                                    pix_format_rgba32 :
                                    pix_format_abgr32;
                                break;
                        }
                    }

                    if(r_mask == 0xFF0000 && b_mask == 0xFF)
                    {
                        switch(m_specific->m_format)
                        {
                            case pix_format_argb32:
                                m_specific->m_sys_format = pix_format_argb32;
                                m_specific->m_byte_order = MSBFirst;
                                break;

                            case pix_format_bgra32:
                                m_specific->m_sys_format = pix_format_bgra32;
                                m_specific->m_byte_order = LSBFirst;
                                break;

                            default:
                                m_specific->m_byte_order = hw_byte_order;
                                m_specific->m_sys_format =
                                    (hw_byte_order == MSBFirst) ?
                                    pix_format_argb32 :
                                    pix_format_bgra32;
                                break;
                        }
                    }
                }
                break;
        }

        if(m_specific->m_sys_format == pix_format_undefined)
        {
            std::fprintf(stderr,
                   "RGB masks are not compatible with AGG pixel formats:\n"
                   "R=%08lx, R=%08lx, B=%08lx\n", r_mask, g_mask, b_mask);
            XCloseDisplay(m_specific->m_display);
            return false;
        }

        std::memset(&m_specific->m_window_attributes,
               0,
               sizeof(m_specific->m_window_attributes));

        m_specific->m_window_attributes.border_pixel =
            XBlackPixel(m_specific->m_display, m_specific->m_screen);

        m_specific->m_window_attributes.background_pixel =
            XWhitePixel(m_specific->m_display, m_specific->m_screen);

        m_specific->m_window_attributes.override_redirect = 0;

        unsigned long window_mask = CWBackPixel | CWBorderPixel;

        m_specific->m_window =
            XCreateWindow(m_specific->m_display,
                          XDefaultRootWindow(m_specific->m_display),
                          0, 0,
                          width,
                          height,
                          0,
                          m_specific->m_depth,
                          InputOutput,
                          CopyFromParent,
                          window_mask,
                          &m_specific->m_window_attributes);

        m_specific->m_gc = XCreateGC(m_specific->m_display,
                                     m_specific->m_window,
                                     0, 0);
        m_specific->m_buf_window =
            new unsigned char[width * height * (m_bpp / 8)];

        std::memset(m_specific->m_buf_window, 255, width * height * (m_bpp / 8));

        m_rbuf_window.attach(m_specific->m_buf_window,
                             width,
                             height,
                             m_flip_y ? -width * (m_bpp / 8) : width * (m_bpp / 8));

        m_specific->m_ximg_window =
            XCreateImage(m_specific->m_display,
                         m_specific->m_visual, //CopyFromParent,
                         m_specific->m_depth,
                         ZPixmap,
                         0,
                         (char*)m_specific->m_buf_window,
                         width,
                         height,
                         m_specific->m_sys_bpp,
                         width * (m_specific->m_sys_bpp / 8));
        m_specific->m_ximg_window->byte_order = m_specific->m_byte_order;

        m_specific->caption(m_caption);
        m_initial_width = width;
        m_initial_height = height;

        if(!m_specific->m_initialized)
        {
            on_init();
            m_specific->m_initialized = true;
        }

        trans_affine_resizing(width, height);
        on_resize(width, height);
        m_specific->m_update_flag = true;

        XSizeHints *hints = XAllocSizeHints();
        if(hints)
        {
            if(flags & window_resize)
            {
                hints->min_width = 32;
                hints->min_height = 32;
                hints->max_width = 4096;
                hints->max_height = 4096;
            }
            else
            {
                hints->min_width  = width;
                hints->min_height = height;
                hints->max_width  = width;
                hints->max_height = height;
            }
            hints->flags = PMaxSize | PMinSize;

            XSetWMNormalHints(m_specific->m_display,
                              m_specific->m_window,
                              hints);

            XFree(hints);
        }

        XMapWindow(m_specific->m_display,
                   m_specific->m_window);

        XSelectInput(m_specific->m_display,
                     m_specific->m_window,
                     xevent_mask);

        m_specific->m_close_atom = XInternAtom(m_specific->m_display,
                                               "WM_DELETE_WINDOW",
                                               false);

        XSetWMProtocols(m_specific->m_display,
                        m_specific->m_window,
                        &m_specific->m_close_atom,
                        1);

        return true;
    }

    void platform_support::update_window()
    {
        m_specific->put_image(&m_rbuf_window);

        // When m_wait_mode is true we can discard all the events
        // came while the image is being drawn. In this case
        // the X server does not accumulate mouse motion events.
        // When m_wait_mode is false, i.e. we have some idle drawing
        // we cannot afford to miss any events
        XSync(m_specific->m_display, m_wait_mode);
    }

    int platform_support::run()
    {
        XFlush(m_specific->m_display);

        bool quit = false;
        unsigned flags;
        int cur_x;
        int cur_y;

        while(!quit)
        {
            if(m_specific->m_update_flag)
            {
                on_draw();
                update_window();
                m_specific->m_update_flag = false;
            }

            if(!m_wait_mode)
            {
                if(XPending(m_specific->m_display) == 0)
                {
                    on_idle();
                    continue;
                }
            }

            XEvent x_event;
            XNextEvent(m_specific->m_display, &x_event);

            // In the Idle mode discard all intermediate MotionNotify events
            if(!m_wait_mode && x_event.type == MotionNotify)
            {
                XEvent te = x_event;
                for(;;)
                {
                    if(XPending(m_specific->m_display) == 0) break;
                    XNextEvent(m_specific->m_display, &te);
                    if(te.type != MotionNotify) break;
                }
                x_event = te;
            }

            switch(x_event.type)
            {
            case ConfigureNotify:
                {
                    if(x_event.xconfigure.width  != int(m_rbuf_window.width()) ||
                       x_event.xconfigure.height != int(m_rbuf_window.height()))
                    {
                        int width  = x_event.xconfigure.width;
                        int height = x_event.xconfigure.height;

                        delete [] m_specific->m_buf_window;
                        m_specific->m_ximg_window->data = 0;
                        XDestroyImage(m_specific->m_ximg_window);

                        m_specific->m_buf_window =
                            new unsigned char[width * height * (m_bpp / 8)];

                        m_rbuf_window.attach(m_specific->m_buf_window,
                                             width,
                                             height,
                                             m_flip_y ?
                                             -width * (m_bpp / 8) :
                                             width * (m_bpp / 8));

                        m_specific->m_ximg_window =
                            XCreateImage(m_specific->m_display,
                                         m_specific->m_visual, //CopyFromParent,
                                         m_specific->m_depth,
                                         ZPixmap,
                                         0,
                                         (char*)m_specific->m_buf_window,
                                         width,
                                         height,
                                         m_specific->m_sys_bpp,
                                         width * (m_specific->m_sys_bpp / 8));
                        m_specific->m_ximg_window->byte_order = m_specific->m_byte_order;

                        trans_affine_resizing(width, height);
                        on_resize(width, height);
                        on_draw();
                        update_window();
                    }
                }
                break;

            case Expose:
                m_specific->put_image(&m_rbuf_window);
                XFlush(m_specific->m_display);
                XSync(m_specific->m_display, false);
                break;

            case KeyPress:
                {
                    KeySym key = XLookupKeysym(&x_event.xkey, 0);
                    flags = 0;
                    if(x_event.xkey.state & Button1Mask) flags |= mouse_left;
                    if(x_event.xkey.state & Button3Mask) flags |= mouse_right;
                    if(x_event.xkey.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xkey.state & ControlMask) flags |= kbd_ctrl;

                    bool left  = false;
                    bool up    = false;
                    bool right = false;
                    bool down  = false;

                    switch(m_specific->m_keymap[key & 0xFF])
                    {
                    case key_left:
                        left = true;
                        break;

                    case key_up:
                        up = true;
                        break;

                    case key_right:
                        right = true;
                        break;

                    case key_down:
                        down = true;
                        break;

                    case key_f2:
                        copy_window_to_img(max_images - 1);
                        save_img(max_images - 1, "screenshot");
                        break;
                    }

                    if(m_ctrls.on_arrow_keys(left, right, down, up))
                    {
                        on_ctrl_change();
                        force_redraw();
                    }
                    else
                    {
                        on_key(x_event.xkey.x,
                               m_flip_y ?
                                   m_rbuf_window.height() - x_event.xkey.y :
                                   x_event.xkey.y,
                               m_specific->m_keymap[key & 0xFF],
                               flags);
                    }
                }
                break;

            case ButtonPress:
                {
                    flags = 0;
                    if(x_event.xbutton.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xbutton.state & ControlMask) flags |= kbd_ctrl;
                    if(x_event.xbutton.button == Button1)   flags |= mouse_left;
                    if(x_event.xbutton.button == Button3)   flags |= mouse_right;

                    cur_x = x_event.xbutton.x;
                    cur_y = m_flip_y ? m_rbuf_window.height() - x_event.xbutton.y :
                                       x_event.xbutton.y;

                    if(flags & mouse_left)
                    {
                        if(m_ctrls.on_mouse_button_down(cur_x, cur_y))
                        {
                            m_ctrls.set_cur(cur_x, cur_y);
                            on_ctrl_change();
                            force_redraw();
                        }
                        else
                        {
                            if(m_ctrls.in_rect(cur_x, cur_y))
                            {
                                if(m_ctrls.set_cur(cur_x, cur_y))
                                {
                                    on_ctrl_change();
                                    force_redraw();
                                }
                            }
                            else
                            {
                                on_mouse_button_down(cur_x, cur_y, flags);
                            }
                        }
                    }
                    if(flags & mouse_right)
                    {
                        on_mouse_button_down(cur_x, cur_y, flags);
                    }
                }
                break;

            case MotionNotify:
                {
                    flags = 0;
                    if(x_event.xmotion.state & Button1Mask) flags |= mouse_left;
                    if(x_event.xmotion.state & Button3Mask) flags |= mouse_right;
                    if(x_event.xmotion.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xmotion.state & ControlMask) flags |= kbd_ctrl;

                    cur_x = x_event.xbutton.x;
                    cur_y = m_flip_y ? m_rbuf_window.height() - x_event.xbutton.y :
                                       x_event.xbutton.y;

                    if(m_ctrls.on_mouse_move(cur_x, cur_y, (flags & mouse_left) != 0))
                    {
                        on_ctrl_change();
                        force_redraw();
                    }
                    else
                    {
                        if(!m_ctrls.in_rect(cur_x, cur_y))
                        {
                            on_mouse_move(cur_x, cur_y, flags);
                        }
                    }
                }
                break;

            case ButtonRelease:
                {
                    flags = 0;
                    if(x_event.xbutton.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xbutton.state & ControlMask) flags |= kbd_ctrl;
                    if(x_event.xbutton.button == Button1)   flags |= mouse_left;
                    if(x_event.xbutton.button == Button3)   flags |= mouse_right;

                    cur_x = x_event.xbutton.x;
                    cur_y = m_flip_y ? m_rbuf_window.height() - x_event.xbutton.y :
                                       x_event.xbutton.y;

                    if(flags & mouse_left)
                    {
                        if(m_ctrls.on_mouse_button_up(cur_x, cur_y))
                        {
                            on_ctrl_change();
                            force_redraw();
                        }
                    }
                    if(flags & (mouse_left | mouse_right))
                    {
                        on_mouse_button_up(cur_x, cur_y, flags);
                    }
                }
                break;

            case ClientMessage:
                if((x_event.xclient.format == 32) &&
                (x_event.xclient.data.l[0] == int(m_specific->m_close_atom)))
                {
                    quit = true;
                }
                break;
            }
        }

        unsigned i = platform_support::max_images;

        delete [] m_specific->m_buf_window;
        m_specific->m_ximg_window->data = 0;
        XDestroyImage(m_specific->m_ximg_window);
        XFreeGC(m_specific->m_display, m_specific->m_gc);
        XDestroyWindow(m_specific->m_display, m_specific->m_window);
        XCloseDisplay(m_specific->m_display);

        return 0;
    }

    const char* platform_support::img_ext() const { return ".ppm"; }

    const char* platform_support::full_file_name(const char* file_name)
    {
        return file_name;
    }

    bool platform_support::load_img(unsigned idx, const char* file)
    {
        if(idx < max_images)
        {
            char buf[1024];
            std::strcpy(buf, file);
            int len = std::strlen(buf);
            if(len < 4 || strcasecmp(buf + len - 4, ".ppm") != 0)
            {
                std::strcat(buf, ".ppm");
            }

            FILE* fd = std::fopen(buf, "rb");
            if(fd == 0) return false;

            if((len = std::fread(buf, 1, 1022, fd)) == 0)
            {
                fclose(fd);
                return false;
            }
            buf[len] = 0;

            if(buf[0] != 'P' && buf[1] != '6')
            {
                std::fclose(fd);
                return false;
            }

            char* ptr = buf + 2;

            while(*ptr && !std::isdigit((unsigned char)(*ptr))) ptr++;
            if(*ptr == 0)
            {
                std::fclose(fd);
                return false;
            }

            unsigned width = std::atoi(ptr);
            if(width == 0 || width > 4096)
            {
                std::fclose(fd);
                return false;
            }
            while(*ptr && isdigit((unsigned char)(*ptr))) ptr++;
            while(*ptr && !isdigit((unsigned char)(*ptr))) ptr++;
            if(*ptr == 0)
            {
                std::fclose(fd);
                return false;
            }
            unsigned height = std::atoi(ptr);
            if(height == 0 || height > 4096)
            {
                std::fclose(fd);
                return false;
            }
            while(*ptr && isdigit((unsigned char)(*ptr))) ptr++;
            while(*ptr && !isdigit((unsigned char)(*ptr))) ptr++;
            if(std::atoi(ptr) != 255)
            {
                std::fclose(fd);
                return false;
            }
            while(*ptr && isdigit((unsigned char)(*ptr))) ptr++;
            if(*ptr == 0)
            {
                std::fclose(fd);
                return false;
            }
            ptr++;
            std::fseek(fd, long(ptr - buf), SEEK_SET);

            create_img(idx, width, height);
            bool ret = true;

            if(m_format == pix_format_rgb24)
            {
                std::size_t sz = std::fread(m_specific->m_buf_img[idx], 1, width * height * 3, fd);
                if (sz != width * height * 3)
                    ret = false;
            }
            else
            {
                unsigned char* buf_img = new unsigned char [width * height * 3];
                rendering_buffer rbuf_img;
                rbuf_img.attach(buf_img,
                                width,
                                height,
                                m_flip_y ?
                                  -width * 3 :
                                   width * 3);

                std::size_t sz = std::fread(buf_img, 1, width * height * 3, fd);
                if (sz != width * height * 3)
                    ret = false;

                switch(m_format)
                {
                    case pix_format_sgray8:
                        convert<pixfmt_sgray8, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_gray8:
                        convert<pixfmt_gray8, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_gray16:
                        convert<pixfmt_gray16, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_gray32:
                        convert<pixfmt_gray32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_rgb555:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_rgb555());
                        break;

                    case pix_format_rgb565:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_rgb565());
                        break;

                    case pix_format_sbgr24:
                        convert<pixfmt_sbgr24, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_rgb24:
                        convert<pixfmt_rgb24, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_bgr24:
                        convert<pixfmt_bgr24, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_srgba32:
                        convert<pixfmt_srgba32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_sargb32:
                        convert<pixfmt_sargb32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_sbgra32:
                        convert<pixfmt_sbgra32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_sabgr32:
                        convert<pixfmt_sabgr32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_rgba32:
                        convert<pixfmt_rgba32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_argb32:
                        convert<pixfmt_argb32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_bgra32:
                        convert<pixfmt_bgra32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_abgr32:
                        convert<pixfmt_abgr32, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_rgb48:
                        convert<pixfmt_rgb48, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_bgr48:
                        convert<pixfmt_bgr48, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_rgba64:
                        convert<pixfmt_rgba64, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_argb64:
                        convert<pixfmt_argb64, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_bgra64:
                        convert<pixfmt_bgra64, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_abgr64:
                        convert<pixfmt_abgr64, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_rgb96:
                        convert<pixfmt_rgb96, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_bgr96:
                        convert<pixfmt_bgr96, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_rgba128:
                        convert<pixfmt_rgba128, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_argb128:
                        convert<pixfmt_argb128, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_bgra128:
                        convert<pixfmt_bgra128, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    case pix_format_abgr128:
                        convert<pixfmt_abgr128, pixfmt_srgb24>(m_rbuf_img+idx, &rbuf_img);
                        break;

                    default:
                        ret = false;
                }
                delete [] buf_img;
            }

            std::fclose(fd);
            return ret;
        }
        return false;
    }

    bool platform_support::save_img(unsigned idx, const char* file)
    {
        if(idx < max_images &&  rbuf_img(idx).buf())
        {
            char buf[1024];
            std::strcpy(buf, file);
            int len = std::strlen(buf);
            if(len < 4 || strcasecmp(buf + len - 4, ".ppm") != 0)
            {
                std::strcat(buf, ".ppm");
            }

            FILE* fd = std::fopen(buf, "wb");
            if(fd == 0) return false;

            unsigned w = rbuf_img(idx).width();
            unsigned h = rbuf_img(idx).height();

            std::fprintf(fd, "P6\n%d %d\n255\n", w, h);

            unsigned y;
            unsigned char* tmp_buf = new unsigned char [w * 3];
            for(y = 0; y < rbuf_img(idx).height(); y++)
            {
                const unsigned char* src = rbuf_img(idx).row_ptr(m_flip_y ? h - 1 - y : y);
                switch(m_format)
                {
                    case pix_format_sgray8:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_sgray8>());
                        break;

                    case pix_format_gray8:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_gray8>());
                        break;

                    case pix_format_gray16:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_gray16>());
                        break;

                    case pix_format_gray32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_gray32>());
                        break;

                    default: break;
                    case pix_format_rgb555:
                        color_conv_row(tmp_buf, src, w, color_conv_rgb555_to_rgb24());
                        break;

                    case pix_format_rgb565:
                        color_conv_row(tmp_buf, src, w, color_conv_rgb565_to_rgb24());
                        break;

                    case pix_format_sbgr24:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_sbgr24>());
                        break;

                    case pix_format_srgb24:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_srgb24>());
                        break;

                    case pix_format_bgr24:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_bgr24>());
                        break;

                    case pix_format_rgb24:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_rgb24>());
                        break;

                    case pix_format_srgba32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_srgba32>());
                        break;

                    case pix_format_sargb32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_sargb32>());
                        break;

                    case pix_format_sbgra32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_sbgra32>());
                        break;

                    case pix_format_sabgr32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_sabgr32>());
                        break;

                    case pix_format_rgba32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_rgba32>());
                        break;

                    case pix_format_argb32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_argb32>());
                        break;

                    case pix_format_bgra32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_bgra32>());
                        break;

                    case pix_format_abgr32:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_abgr32>());
                        break;

                    case pix_format_bgr48:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_bgr48>());
                        break;

                    case pix_format_rgb48:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_rgb48>());
                        break;

                    case pix_format_rgba64:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_rgba64>());
                        break;

                    case pix_format_argb64:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_argb64>());
                        break;

                    case pix_format_bgra64:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_bgra64>());
                        break;

                    case pix_format_abgr64:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_abgr64>());
                        break;

                    case pix_format_bgr96:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_bgr96>());
                        break;

                    case pix_format_rgb96:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_rgb96>());
                        break;

                    case pix_format_rgba128:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_rgba128>());
                        break;

                    case pix_format_argb128:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_argb128>());
                        break;

                    case pix_format_bgra128:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_bgra128>());
                        break;

                    case pix_format_abgr128:
                        color_conv_row(tmp_buf, src, w, conv_row<pixfmt_srgb24, pixfmt_abgr128>());
                        break;
                }
                std::fwrite(tmp_buf, 1, w * 3, fd);
            }
            delete [] tmp_buf;
            std::fclose(fd);
            return true;
        }
        return false;
    }

    bool platform_support::create_img(unsigned idx, unsigned width, unsigned height)
    {
        if(idx < max_images)
        {
            if(width  == 0) width  = rbuf_window().width();
            if(height == 0) height = rbuf_window().height();
            delete [] m_specific->m_buf_img[idx];
            m_specific->m_buf_img[idx] =
                new unsigned char[width * height * (m_bpp / 8)];

            m_rbuf_img[idx].attach(m_specific->m_buf_img[idx],
                                   width,
                                   height,
                                   m_flip_y ?
                                       -width * (m_bpp / 8) :
                                        width * (m_bpp / 8));
            return true;
        }
        return false;
    }

    void platform_support::force_redraw()
    {
        m_specific->m_update_flag = true;
    }

    void platform_support::message(const char* msg)
    {
        std::fprintf(stderr, "%s\n", msg);
    }

    void platform_support::start_timer()
    {
        m_specific->m_sw_start = clock();
    }

    double platform_support::elapsed_time() const
    {
        std::clock_t stop = std::clock();
        return double(stop - m_specific->m_sw_start) * 1000.0 / CLOCKS_PER_SEC;
    }

    void platform_support::on_init() {}
    void platform_support::on_resize(int sx, int sy) {}
    void platform_support::on_idle() {}
    void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
    void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
    void platform_support::on_ctrl_change() {}
    void platform_support::on_draw() {}
    void platform_support::on_post_draw(void* raw_handler) {}

}

int agg_main(int argc, char* argv[]);

int main(int argc, char* argv[])
{
    return agg_main(argc, argv);
}


void ModelEditor::ResetInputPort(const InputPortBase* port, const OutputPortBase& newInput)

//  Project:  Embedded Learning Library (ELL)
//  Authors:  Kern Handa

#include "ModelEditor.h"

namespace ell
{
namespace model
{

{
        // Luckily nothing is ever really const in the codebase. If that changes, this is UB
        const_cast<InputPortBase*>(port)->SetReferencedPort(&newInput);
    }

} // namespace model
} // namespace ell


{
        // Luckily nothing is ever really const in the codebase. If that changes, this is UB
        const_cast<InputPortBase*>(port)->SetReferencedPort(&newInput);
    }

//  Project:  Embedded Learning Library (ELL)
//  Authors:  Kern Handa

#include "ModelEditor.h"

namespace ell
{
namespace model
{

    void ModelEditor::ResetInputPort(const InputPortBase* port, const OutputPortBase& newInput)


//  Project:  Embedded Learning Library (ELL)
//  Authors:  Kern Handa

#include "ModelEditor.h"

namespace ell
{
namespace model
{

    void ModelEditor::ResetInputPort(const InputPortBase* port, const OutputPortBase& newInput)
    {
        // Luckily nothing is ever really const in the codebase. If that changes, this is UB
        const_cast<InputPort

    }

} // namespace model
} // namespace ell


S3ControlARNOutcome S3ControlARN::Validate(const char* clientRegion) const

/**
 */

#include <cassert>
#include <aws/core/Region.h>
#include <aws/core/utils/DNS.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/s3control/S3ControlARN.h>

namespace Aws
{
    namespace S3Control
    {
        S3ControlARN::S3ControlARN(const Aws::String& arn) : Utils::ARN(arn)
        {
            ParseARNResource();
        }

{
            // Take pseudo region into consideration here.
            Aws::String region = clientRegion ? clientRegion : "";
            Aws::StringStream ss;
            if (this->GetResourceType() == ARNResourceType::OUTPOST && Aws::Region::IsFipsRegion(region))
            {
                ss.str("");
                ss << "Outposts ARN do not support fips regions right now.";
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false));
            }
            else if (this->GetRegion() != Aws::Region::ComputeSignerRegion(clientRegion))
            {
                ss.str("");
                ss << "Region mismatch between \"" << this->GetRegion() << "\" defined in ARN and \""
                    << clientRegion << "\" defined in client configuration. "
                    << "You can specify AWS_S3_USE_ARN_REGION to ignore region defined in client configuration.";
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false));
            }
            else
            {
                return Validate();
            }
        }

        S3ControlARNOutcome S3ControlARN::Validate() const
        {
            Aws::String errorMessage;
            bool success = false;
            Aws::StringStream ss;

            if (!*this)
            {
                errorMessage = "Invalid ARN.";
            }
            // Validation on partition.
            else if (this->GetPartition().find("aws") != 0)
            {
                ss.str("");
                ss << "Invalid partition in ARN: " << this->GetPartition() << ". Valid options: aws, aws-cn, and etc.";
            }
            // Validation on service.
            else if (this->GetService() != "s3" && this->GetService() != "s3-outposts")
            {
                ss.str("");
                ss << "Invalid service in ARN: " << this->GetService() << ". Valid options: s3, s3-outposts";
                errorMessage = ss.str();
            }
            // Validation on region.
            // TODO: Failure on different partitions.
            else if (this->GetRegion().empty())
            {
                errorMessage = "Invalid ARN with empty region.";
            }
            else if (!Utils::IsValidDnsLabel(this->GetRegion()))
            {
                ss.str("");
                ss << "Invalid region in ARN: " << this->GetRegion() << ". Region should be a RFC 3986 Host label.";
                errorMessage = ss.str();
            }
            else if (Aws::Region::IsFipsRegion(this->GetRegion()))
            {
                ss.str("");
                ss << "Invalid region in ARN: " << this->GetRegion() << ". FIPS region is not allowed in ARN.";
                errorMessage = ss.str();
            }
            // Validation on account ID
            else if (!Utils::IsValidDnsLabel(this->GetAccountId()))
            {
                ss.str("");
                ss << "Invalid account ID in ARN: " << this->GetAccountId() << ". Account ID should be a RFC 3986 Host label.";
                errorMessage = ss.str();
            }
            // Validation on Outposts ARN:
            else if (this->GetResourceType() == ARNResourceType::OUTPOST)
            {
                if (!Utils::IsValidDnsLabel(this->GetResourceId()))
                {
                    ss.str("");
                    ss << "Invalid outpost ID in Outposts ARN: " << this->GetResourceId() << ". Outpost ID should be a RFC 3986 Host label.";
                    errorMessage = ss.str();
                }
                else if (this->GetSubResourceType() != ARNResourceType::ACCESSPOINT && this->GetSubResourceType() != ARNResourceType::BUCKET)
                {
                    ss.str("");
                    ss << "Invalid sub resource type in Outposts ARN: " << this->GetSubResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::BUCKET;
                    errorMessage = ss.str();
                }
                else if (!Utils::IsValidDnsLabel(this->GetSubResourceId()))
                {
                    ss.str("");
                    ss << "Invalid " << this->GetSubResourceType() << " name in Outposts ARN: " << this->GetSubResourceId() << ", " << this->GetSubResourceType() << " name should be a RFC 3986 Host label.";
                    errorMessage = ss.str();
                }
                else
                {
                    success = true;
                }
            }
            // Neither Access Point ARN, Bucket ARN nor Outposts ARN.
            else
            {
                ss.str("");
                ss << "Invalid resource type in ARN: " << this->GetResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::OUTPOST;
                errorMessage = ss.str();
            }

            if (success)
            {
                return S3ControlARNOutcome(success);
            }
            else
            {
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", errorMessage, false));
            }
        }

        void S3ControlARN::ParseARNResource()
        {
            if (!*this) return;

            Aws::String resource = this->GetResource();
            Aws::Vector<Aws::String> resourceSegments;
            if (resource.find(':') != std::string::npos)
            {
                resourceSegments = Utils::StringUtils::Split(resource, ':', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES);
            }
            else if (resource.find('/') != std::string::npos)
            {
                resourceSegments = Utils::StringUtils::Split(resource, '/', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES);
            }
            else
            {
                resourceSegments.emplace_back(resource);
            }

            switch (resourceSegments.size())
            {
            case 1:
                m_resourceId = resourceSegments[0];
                break;
            case 2:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                break;
            case 3:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                m_resourceQualifier = resourceSegments[2];
                break;
            case 4:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                m_subResourceType = resourceSegments[2];
                m_subResourceId = resourceSegments[3];
                break;
            default:
                assert(false);
                break;
            }
        }
    }
}


{
            ParseARNResource();
        }

/**
 */

#include <cassert>
#include <aws/core/Region.h>
#include <aws/core/utils/DNS.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/s3control/S3ControlARN.h>

namespace Aws
{
    namespace S3Control
    {
        S3ControlARN::S3ControlARN(const Aws::String& arn) : Utils::ARN(arn)

        S3ControlARNOutcome S3ControlARN::Validate(const char* clientRegion) const
        {
            // Take pseudo region into consideration here.
            Aws::String region = clientRegion ? clientRegion : "";
            Aws::StringStream ss;
            if (this->GetResourceType() == ARNResourceType::OUTPOST && Aws::Region::IsFipsRegion(region))
            {
                ss.str("");
                ss << "Outposts ARN do not support fips regions right now.";
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false));
            }
            else if (this->GetRegion() != Aws::Region::ComputeSignerRegion(clientRegion))
            {
                ss.str("");
                ss << "Region mismatch between \"" << this->GetRegion() << "\" defined in ARN and \""
                    << clientRegion << "\" defined in client configuration. "
                    << "You can specify AWS_S3_USE_ARN_REGION to ignore region defined in client configuration.";
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false));
            }
            else
            {
                return Validate();
            }
        }

        S3ControlARNOutcome S3ControlARN::Validate() const
        {
            Aws::String errorMessage;
            bool success = false;
            Aws::StringStream ss;

            if (!*this)
            {
                errorMessage = "Invalid ARN.";
            }
            // Validation on partition.
            else if (this->GetPartition().find("aws") != 0)
            {
                ss.str("");
                ss << "Invalid partition in ARN: " << this->GetPartition() << ". Valid options: aws, aws-cn, and etc.";
            }
            // Validation on service.
            else if (this->GetService() != "s3" && this->GetService() != "s3-outposts")
            {
                ss.str("");
                ss << "Invalid service in ARN: " << this->GetService() << ". Valid options: s3, s3-outposts";
                errorMessage = ss.str();
            }
            // Validation on region.
            // TODO: Failure on different partitions.
            else if (this->GetRegion().empty())
            {
                errorMessage = "Invalid ARN with empty region.";
            }
            else if (!Utils::IsValidDnsLabel(this->GetRegion()))
            {
                ss.str("");
                ss << "Invalid region in ARN: " << this->GetRegion() << ". Region should be a RFC 3986 Host label.";
                errorMessage = ss.str();
            }
            else if (Aws::Region::IsFipsRegion(this->GetRegion()))
            {
                ss.str("");
                ss << "Invalid region in ARN: " << this->GetRegion() << ". FIPS region is not allowed in ARN.";
                errorMessage = ss.str();
            }
            // Validation on account ID
            else if (!Utils::IsValidDnsLabel(this->GetAccountId()))
            {
                ss.str("");
                ss << "Invalid account ID in ARN: " << this->GetAccountId() << ". Account ID should be a RFC 3986 Host label.";
                errorMessage = ss.str();
            }
            // Validation on Outposts ARN:
            else if (this->GetResourceType() == ARNResourceType::OUTPOST)
            {
                if (!Utils::IsValidDnsLabel(this->GetResourceId()))
                {
                    ss.str("");
                    ss << "Invalid outpost ID in Outposts ARN: " << this->GetResourceId() << ". Outpost ID should be a RFC 3986 Host label.";
                    errorMessage = ss.str();
                }
                else if (this->GetSubResourceType() != ARNResourceType::ACCESSPOINT && this->GetSubResourceType() != ARNResourceType::BUCKET)
                {
                    ss.str("");
                    ss << "Invalid sub resource type in Outposts ARN: " << this->GetSubResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::BUCKET;
                    errorMessage = ss.str();
                }
                else if (!Utils::IsValidDnsLabel(this->GetSubResourceId()))
                {
                    ss.str("");
                    ss << "Invalid " << this->GetSubResourceType() << " name in Outposts ARN: " << this->GetSubResourceId() << ", " << this->GetSubResourceType() << " name should be a RFC 3986 Host label.";
                    errorMessage = ss.str();
                }
                else
                {
                    success = true;
                }
            }
            // Neither Access Point ARN, Bucket ARN nor Outposts ARN.
            else
            {
                ss.str("");
                ss << "Invalid resource type in ARN: " << this->GetResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::OUTPOST;
                errorMessage = ss.str();
            }

            if (success)
            {
                return S3ControlARNOutcome(success);
            }
            else
            {
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", errorMessage, false));
            }
        }

        void S3ControlARN::ParseARNResource()
        {
            if (!*this) return;

            Aws::String resource = this->GetResource();
            Aws::Vector<Aws::String> resourceSegments;
            if (resource.find(':') != std::string::npos)
            {
                resourceSegments = Utils::StringUtils::Split(resource, ':', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES);
            }
            else if (resource.find('/') != std::string::npos)
            {
                resourceSegments = Utils::StringUtils::Split(resource, '/', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES);
            }
            else
            {
                resourceSegments.emplace_back(resource);
            }

            switch (resourceSegments.size())
            {
            case 1:
                m_resourceId = resourceSegments[0];
                break;
            case 2:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                break;
            case 3:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                m_resourceQualifier = resourceSegments[2];
                break;
            case 4:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                m_subResourceType = resourceSegments[2];
                m_subResourceId = resourceSegments[3];
                break;
            default:
                assert(false);
                break;
            }
        }
    }
}


/**
 */

#include <cassert>
#include <aws/core/Region.h>
#include <aws/core/utils/DNS.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/s3control/S3ControlARN.h>

namespace Aws
{
    namespace S3Control
    {
        S3ControlARN::S3ControlARN(const Aws::String& arn) : Utils::ARN(arn)
        {
            ParseARNResource();
        }

        S3ControlARNOutcome S3ControlARN::Validate(const char* clientRegion) const
        {
            // Take pseudo region into consideration here.
            Aws::String region = clientRegion ? clientRegion : "";
            Aws::StringStream ss;
            if (this->GetResourceType() == ARNResourceType::OUTPOST && Aws::Region::IsFipsRegion(region))
            {
                ss.str("");
                ss << "Outposts ARN do not support fips regions right now.";
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false));
            }
            else if (this->GetRegion() != Aws::Region::ComputeSignerRegion(clientRegion))
            {
                ss.str("");
                ss << "Region mismatch between \"" << this->GetRegion() << "\" defined in ARN and \""
                    << clientRegion << "\" defined in client configuration. "
                    << "You can specify AWS_S3_USE_ARN_REGION to ignore region defined in client configuration.";
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false));
            }
            else
            {
                return Validate();
            }
        }

        S3ControlARNOutcome S3ControlARN::Validate() const
        {
            Aws::String errorMessage;
            bool success = false;
            Aws::StringStream ss;

            if (!*this)
            {
                errorMessage = "Invalid ARN.";
            }
            // Validation on partition.
            else if (this->GetPartition().find("aws") != 0)
            {
                ss.str("");
                ss << "Invalid partition in ARN: " << this->GetPartition() << ". Valid options: aws, aws-cn, and etc.";
            }
            // Validation on service.
            else if (this->GetService() != "s3" && this->GetService() != "s3-outposts")
            {
                ss.str("");
                ss << "Invalid service in ARN: " << this->GetService() << ". Valid options: s3, s3-outposts";
                errorMessage = ss.str();
            }
            // Validation on region.
            // TODO: Failure on different partitions.
            else if (this->GetRegion().empty())
            {
                errorMessage = "Invalid ARN with empty region.";
            }
            else if (!Utils::IsValidDnsLabel(this->GetRegion()))
            {
                ss.str("");
                ss << "Invalid region in ARN: " << this->GetRegion() << ". Region should be a RFC 3986 Host label.";
                errorMessage = ss.str();
            }
            else if (Aws::Region::IsFipsRegion(this->GetRegion()))
            {
                ss.str("");
                ss << "Invalid region in ARN: " << this->GetRegion() << ". FIPS region is not allowed in ARN.";
                errorMessage = ss.str();
            }
            // Validation on account ID
            else if (!Utils::IsValidDnsLabel(this->GetAccountId()))
            {
                ss.str("");
                ss << "Invalid account ID in ARN: " << this->GetAccountId() << ". Account ID should be a RFC 3986 Host label.";
                errorMessage = ss.str();
            }
            // Validation on Outposts ARN:
            else if (this->GetResourceType() == ARNResourceType::OUTPOST)
            {
                if (!Utils::IsValidDnsLabel(this->GetResourceId()))
                {
                    ss.str("");
                    ss << "Invalid outpost ID in Outposts ARN: " << this->GetResourceId() << ". Outpost ID should be a RFC 3986 Host label.";
                    errorMessage = ss.str();
                }
                else if (this->GetSubResourceType() != ARNResourceType::ACCESSPOINT && this->GetSubResourceType() != ARNResourceType::BUCKET)
                {
                    ss.str("");
                    ss << "Invalid sub resource type in Outposts ARN: " << this->GetSubResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::BUCKET;
                    errorMessage = ss.str();
                }
                else if (!Utils::IsValidDnsLabel(this->GetSubResourceId()))
                {
                    ss.str("");
                    ss << "Invalid " << this->GetSubResourceType() << " name in Outposts ARN: " << this->GetSubResourceId() << ", " << this->GetSubResourceType() << " name should be a RFC 3986 Host label.";
                    errorMessage = ss.str();
                }
                else
                {
                    success = true;
                }
            }
            // Neither Access Point ARN, Bucket ARN nor Outposts ARN.
            else
            {
                ss.str("");
                ss << "Invalid resource type in ARN: " << this->GetResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::OUTPOST;
                errorMessage = ss.str();
            }

            if (success)
            {
                return S3ControlARNOutcome(success);
            }
            else
            {
                return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", errorMessage, false));
            }
        }

        void S3ControlARN::ParseARNResource()
        {
            if (!*this) return;

            Aws::String resource = this->GetResource();
            Aws::Vecto

            if (resource.find(':') != std::string::npos)
            {
                resourceSegments = Utils::StringUtils::Split(resource, ':', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES);
            }
            else if (resource.find('/') != std::string::npos)
            {
                resourceSegments = Utils::StringUtils::Split(resource, '/', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES);
            }
            else
            {
                resourceSegments.emplace_back(resource);
            }

            switch (resourceSegments.size())
            {
            case 1:
                m_resourceId = resourceSegments[0];
                break;
            case 2:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                break;
            case 3:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                m_resourceQualifier = resourceSegments[2];
                break;
            case 4:
                m_resourceType = resourceSegments[0];
                m_resourceId = resourceSegments[1];
                m_subResourceType = resourceSegments[2];
                m_subResourceId = resourceSegments[3];
                break;
            default:
                assert(false);
                break;
            }
        }
    }
}