/* GG is a GUI for SDL and OpenGL.
   Copyright (C) 2003 T. Zachary Laine

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1
   of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
    
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA

   If you do not wish to comply with the terms of the LGPL please
   contact the author as other terms are available for a fee.
    
   Zach Laine
   whatwasthataddress@hotmail.com */

#include <GG/dialogs/ColorDlg.h>

#include <GG/GUI.h>
#include <GG/DrawUtil.h>
#include <GG/Font.h>
#include <GG/Layout.h>
#include <GG/Slider.h>
#include <GG/StyleFactory.h>

using namespace GG;

namespace {
    const double EPSILON = 0.0001;

    HSVClr Convert(const Clr& color)
    {
        HSVClr retval;
        retval.a = color.a;
        double r = (color.r / 255.0), g = (color.g / 255.0), b = (color.b / 255.0);

        double min_channel = std::min(r, std::min(g, b));
        double max_channel = std::max(r, std::max(g, b));
        double channel_range = max_channel - min_channel;

        retval.v = max_channel;

        if (max_channel < EPSILON) {
            retval.h = 0.0;
            retval.s = 0.0;
        } else {
            retval.s = channel_range / max_channel;

            if (channel_range) {
                double delta_r = (((max_channel - r) / 6.0) + (channel_range / 2.0)) / channel_range;
                double delta_g = (((max_channel - g) / 6.0) + (channel_range / 2.0)) / channel_range;
                double delta_b = (((max_channel - b) / 6.0) + (channel_range / 2.0)) / channel_range;

                if (r == max_channel)
                    retval.h = delta_b - delta_g;
                else if (g == max_channel)
                    retval.h = (1.0 / 3.0) + delta_r - delta_b;
                else if (b == max_channel)
                    retval.h = (2.0 / 3.0) + delta_g - delta_r;

                if (retval.h < 0.0)
                    retval.h += 1.0;
                if (1.0 < retval.h)
                    retval.h -= 1.0;
            } else {
                retval.h = 0.0;
            }
        }
        return retval;
    }

    Clr Convert(const HSVClr& hsv_color)
    {
        Clr retval;
        retval.a = hsv_color.a;

        if (hsv_color.s < EPSILON) {
            retval.r = static_cast<Uint8>(hsv_color.v * 255);
            retval.g = static_cast<Uint8>(hsv_color.v * 255);
            retval.b = static_cast<Uint8>(hsv_color.v * 255);
        } else {
            double tmph = hsv_color.h * 6.0;
            int tmpi = static_cast<int>(tmph);
            double tmp1 = hsv_color.v * (1 - hsv_color.s);
            double tmp2 = hsv_color.v * (1 - hsv_color.s * (tmph - tmpi));
            double tmp3 = hsv_color.v * (1 - hsv_color.s * (1 - (tmph - tmpi)));
            switch (tmpi) {
            case 0:
                retval.r = static_cast<Uint8>(hsv_color.v * 255);
                retval.g = static_cast<Uint8>(tmp3 * 255);
                retval.b = static_cast<Uint8>(tmp1 * 255);
                break;
            case 1:
                retval.r = static_cast<Uint8>(tmp2 * 255);
                retval.g = static_cast<Uint8>(hsv_color.v * 255);
                retval.b = static_cast<Uint8>(tmp1 * 255);
                break;
            case 2:
                retval.r = static_cast<Uint8>(tmp1 * 255);
                retval.g = static_cast<Uint8>(hsv_color.v * 255);
                retval.b = static_cast<Uint8>(tmp3 * 255);
                break;
            case 3:
                retval.r = static_cast<Uint8>(tmp1 * 255);
                retval.g = static_cast<Uint8>(tmp2 * 255);
                retval.b = static_cast<Uint8>(hsv_color.v * 255);
                break;
            case 4:
                retval.r = static_cast<Uint8>(tmp3 * 255);
                retval.g = static_cast<Uint8>(tmp1 * 255);
                retval.b = static_cast<Uint8>(hsv_color.v * 255);
                break;
            default:
                retval.r = static_cast<Uint8>(hsv_color.v * 255);
                retval.g = static_cast<Uint8>(tmp1 * 255);
                retval.b = static_cast<Uint8>(tmp2 * 255);
                break;
            }
        }

        return retval;
    }
}


////////////////////////////////////////////////
// HSVClr
////////////////////////////////////////////////
HSVClr::HSVClr() :
    h(0.0),
    s(0.0),
    v(0.0),
    a(0)
{}

HSVClr::HSVClr(double h_, double s_, double v_, Uint8 a_/* = 255*/) :
    h(h_),
    s(s_),
    v(v_),
    a(a_)
{}

////////////////////////////////////////////////
// HueSaturationPicker
////////////////////////////////////////////////
HueSaturationPicker::HueSaturationPicker() :
    Control(),
    m_hue(0.0),
    m_saturation(0.0)
{}

HueSaturationPicker::HueSaturationPicker(int x, int y, int w, int h) :
    Control(x, y, w, h, CLICKABLE),
    m_hue(0.0),
    m_saturation(0.0)
{
    const int SAMPLES = 100;
    const double INCREMENT = 1.0 / (SAMPLES + 1);
    const double VALUE = 1.0;
    m_vertices.resize(SAMPLES, std::vector<std::pair<double, double> >(2 * (SAMPLES + 1)));
    m_colors.resize(SAMPLES, std::vector<Clr>(2 * (SAMPLES + 1)));
    for (int col = 0; col < SAMPLES; ++col) {
        for (int row = 0; row < SAMPLES + 1; ++row) {
            m_vertices[col][2 * row] = std::make_pair(col * INCREMENT, row * INCREMENT);
            m_vertices[col][2 * row + 1] = std::make_pair((col + 1) * INCREMENT, row * INCREMENT);
            m_colors[col][2 * row] = Convert(HSVClr(col * INCREMENT, 1.0 - row * INCREMENT, VALUE));
            m_colors[col][2 * row + 1] = Convert(HSVClr((col + 1) * INCREMENT, 1.0 - row * INCREMENT, VALUE));
        }
    }
}

void HueSaturationPicker::Render()
{
    Pt ul = UpperLeft(), lr = LowerRight();
    Pt size = Size();
    glDisable(GL_TEXTURE_2D);
    glPushMatrix();
    glTranslated(ul.x, ul.y, 0.0);
    glScaled(size.x, size.y, 1.0);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    for (unsigned int i = 0; i < m_vertices.size(); ++i) {
        glVertexPointer(2, GL_DOUBLE, 0, &m_vertices[i][0]);
        glColorPointer(4, GL_UNSIGNED_BYTE, 0, &m_colors[i][0]);
        glDrawArrays(GL_QUAD_STRIP, 0, m_vertices[i].size());
    }
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glPopMatrix();
    Pt color_position(static_cast<int>(ul.x + size.x * m_hue),
                      static_cast<int>(ul.y + size.y * (1.0 - m_saturation)));
    glColor(CLR_SHADOW);
    glBegin(GL_LINES);
    glVertex2i(color_position.x, ul.y);
    glVertex2i(color_position.x, lr.y);
    glVertex2i(ul.x, color_position.y);
    glVertex2i(lr.x, color_position.y);
    glEnd();
    glEnable(GL_TEXTURE_2D);
}

void HueSaturationPicker::LButtonDown(const Pt& pt, Uint32 keys)
{
    SetHueSaturationFromPt(pt);
}

void HueSaturationPicker::LDrag(const Pt& pt, const Pt& move, Uint32 keys)
{
    SetHueSaturationFromPt(pt);
}

void HueSaturationPicker::SetHueSaturation(double hue, double saturation)
{
    m_hue = hue;
    m_saturation = saturation;
}

void HueSaturationPicker::SetHueSaturationFromPt(Pt pt)
{
    Pt ul = UpperLeft(), lr = LowerRight();
    if (pt.x < ul.x)
        pt.x = ul.x;
    if (pt.y < ul.y)
        pt.y = ul.y;
    if (lr.x < pt.x)
        pt.x = lr.x;
    if (lr.y < pt.y)
        pt.y = lr.y;
    Pt size = Size();
    m_hue = (pt.x - ul.x) / static_cast<double>(size.x);
    m_saturation = 1.0 - (pt.y - ul.y) / static_cast<double>(size.y);
    ChangedSignal(m_hue, m_saturation);
}


////////////////////////////////////////////////
// ValuePicker
////////////////////////////////////////////////
ValuePicker::ValuePicker() :
    Control(),
    m_hue(0.0),
    m_saturation(0.0),
    m_value(0.0)
{}

ValuePicker::ValuePicker(int x, int y, int w, int h, Clr arrow_color) :
    Control(x, y, w, h, CLICKABLE),
    m_hue(0.0),
    m_saturation(0.0),
    m_value(0.0),
    m_arrow_color(arrow_color)
{}

void ValuePicker::Render()
{
    Pt eff_ul = UpperLeft(), eff_lr = LowerRight() - Pt(4, 0);
    int h = Height();
    glDisable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
    glColor(Convert(HSVClr(m_hue, m_saturation, 1.0)));
    glVertex2i(eff_lr.x, eff_ul.y);
    glVertex2i(eff_ul.x, eff_ul.y);
    glColor(Convert(HSVClr(m_hue, m_saturation, 0.0)));
    glVertex2i(eff_ul.x, eff_lr.y);
    glVertex2i(eff_lr.x, eff_lr.y);
    glEnd();
    int color_position = static_cast<int>(eff_ul.y + h * (1.0 - m_value));
    glColor(CLR_SHADOW);
    glBegin(GL_LINES);
    glVertex2i(eff_ul.x, color_position);
    glVertex2i(eff_lr.x, color_position);
    glEnd();
    glColor(m_arrow_color);
    glBegin(GL_TRIANGLES);
    glVertex2i(eff_lr.x + 4, color_position - 3);
    glVertex2i(eff_lr.x + 1, color_position);
    glVertex2i(eff_lr.x + 4, color_position + 3);
    glEnd();
    glEnable(GL_TEXTURE_2D);
}

void ValuePicker::LButtonDown(const Pt& pt, Uint32 keys)
{
    SetValueFromPt(pt);
}

void ValuePicker::LDrag(const Pt& pt, const Pt& move, Uint32 keys)
{
    SetValueFromPt(pt);
}

void ValuePicker::SetHueSaturation(double hue, double saturation)
{
    m_hue = hue;
    m_saturation = saturation;
}

void ValuePicker::SetValue(double value)
{
    m_value = value;
}

void ValuePicker::SetValueFromPt(Pt pt)
{
    Pt ul = UpperLeft(), lr = LowerRight();
    if (pt.x < ul.x)
        pt.x = ul.x;
    if (pt.y < ul.y)
        pt.y = ul.y;
    if (lr.x < pt.x)
        pt.x = lr.x;
    if (lr.y < pt.y)
        pt.y = lr.y;
    int h = Height();
    m_value = 1.0 - (pt.y - ul.y) / static_cast<double>(h);
    ChangedSignal(m_value);
}


////////////////////////////////////////////////
// ColorDlg
////////////////////////////////////////////////

// ColorDlg::ColorButton
ColorDlg::ColorButton::ColorButton() :
    Button()
{}

ColorDlg::ColorButton::ColorButton(const Clr& color) :
    Button(0, 0, 1, 1, "", boost::shared_ptr<Font>(), color),
    m_represented_color(CLR_BLACK)
{}

Clr ColorDlg::ColorButton::RepresentedColor() const
{
    return m_represented_color;
}

void ColorDlg::ColorButton::SetRepresentedColor(const Clr& color)
{
    m_represented_color = color;
}

void ColorDlg::ColorButton::RenderUnpressed()
{
    Button::RenderUnpressed();
    Pt ul = UpperLeft() + Pt(3, 3), lr = LowerRight() - Pt(3, 3);
    FlatRectangle(ul.x, ul.y, lr.x, lr.y, m_represented_color, CLR_ZERO, 0);
}

void ColorDlg::ColorButton::RenderPressed()
{
    Button::RenderPressed();
    Pt ul = UpperLeft() + Pt(4, 4), lr = LowerRight() - Pt(2, 2);
    FlatRectangle(ul.x, ul.y, lr.x, lr.y, m_represented_color, CLR_ZERO, 0);
}

void ColorDlg::ColorButton::RenderRollover()
{
    RenderUnpressed();
}


// ColorDlg::ColorDisplay
ColorDlg::ColorDisplay::ColorDisplay() :
    Control()
{}

ColorDlg::ColorDisplay::ColorDisplay(Clr color) :
    Control(0, 0, 1, 1, 0)
{
    SetColor(color);
}

void ColorDlg::ColorDisplay::Render()
{
    Pt ul = UpperLeft(), lr = LowerRight();
    const int SQUARE_SIZE = 7;
    glDisable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
    int i = 0, j = 0;
    for (int y = lr.y; y > ul.y; y -= SQUARE_SIZE, ++j) {
        int y0 = y - SQUARE_SIZE;
        if (y0 < ul.y)
            y0 = ul.y;
        i = 0;
        for (int x = lr.x; x > ul.x; x -= SQUARE_SIZE, ++i) {
            int x0 = x - SQUARE_SIZE;
            if (x0 < ul.x)
                x0 = ul.x;
            glColor(((i + j) % 2) ? CLR_WHITE : CLR_BLACK);
            glVertex2i(x, y0);
            glVertex2i(x0, y0);
            glVertex2i(x0, y);
            glVertex2i(x, y);
        }
    }
    glEnd();
    Clr full_alpha_color = Color();
    full_alpha_color.a = 255;
    glBegin(GL_TRIANGLES);
    glColor(full_alpha_color);
    glVertex2i(lr.x, ul.y);
    glVertex2i(ul.x, ul.y);
    glVertex2i(ul.x, lr.y);
    glColor(Color());
    glVertex2i(ul.x, lr.y);
    glVertex2i(lr.x, lr.y);
    glVertex2i(lr.x, ul.y);
    glEnd();
    glEnable(GL_TEXTURE_2D);
}


// ColorDlg::ColorButtonClickFunctor
ColorDlg::ColorButtonClickFunctor::ColorButtonClickFunctor(int id, ColorDlg* picker_) :
    button_id(id),
    picker(picker_)
{}

void ColorDlg::ColorButtonClickFunctor::operator()()
{
    picker->ColorButtonClicked(button_id);
}


// ColorDlg

// static(s)
std::vector<Clr> ColorDlg::s_custom_colors;

ColorDlg::ColorDlg() :
    Wnd(),
    m_original_color_specified(false),
    m_color_was_picked(false),
    m_hue_saturation_picker(0),
    m_value_picker(0),
    m_pickers_layout(0),
    m_new_color_square(0),
    m_old_color_square(0),
    m_new_color_square_text(0),
    m_old_color_square_text(0),
    m_color_squares_layout(0),
    m_color_buttons_layout(0),
    m_current_color_button(-1),
    m_ignore_sliders(false),
    m_ok(0),
    m_cancel(0),
    m_sliders_ok_cancel_layout(0)
{}

ColorDlg::ColorDlg(int x, int y, const boost::shared_ptr<Font>& font,
                   Clr dialog_color, Clr border_color, Clr text_color/* = CLR_BLACK*/) :
    Wnd(x, y, 315, 300, CLICKABLE | DRAGABLE | MODAL),
    m_original_color(CLR_ZERO),
    m_original_color_specified(false),
    m_color_was_picked(false),
    m_new_str("New"),
    m_old_str("Old"),
    m_red_str("R:"),
    m_green_str("G:"),
    m_blue_str("B:"),
    m_hue_str("H:"),
    m_saturation_str("S:"),
    m_value_str("V:"),
    m_alpha_str("A:"),
    m_ok_str("Ok"),
    m_cancel_str("Cancel"),
    m_hue_saturation_picker(0),
    m_value_picker(0),
    m_pickers_layout(0),
    m_new_color_square(0),
    m_old_color_square(0),
    m_new_color_square_text(0),
    m_old_color_square_text(0),
    m_color_squares_layout(0),
    m_color_buttons_layout(0),
    m_current_color_button(-1),
    m_ignore_sliders(false),
    m_ok(0),
    m_cancel(0),
    m_sliders_ok_cancel_layout(0),
    m_color(dialog_color),
    m_border_color(border_color),
    m_text_color(text_color)
{
    Init(font);
}

ColorDlg::ColorDlg(int x, int y, Clr original_color, const boost::shared_ptr<Font>& font,
                   Clr dialog_color, Clr border_color, Clr text_color/* = CLR_BLACK*/) :
    Wnd(x, y, 315, 300, CLICKABLE | DRAGABLE | MODAL),
    m_original_color(original_color),
    m_original_color_specified(true),
    m_color_was_picked(false),
    m_new_str("New"),
    m_old_str("Old"),
    m_red_str("R:"),
    m_green_str("G:"),
    m_blue_str("B:"),
    m_hue_str("H:"),
    m_saturation_str("S:"),
    m_value_str("V:"),
    m_alpha_str("A:"),
    m_ok_str("Ok"),
    m_cancel_str("Cancel"),
    m_hue_saturation_picker(0),
    m_value_picker(0),
    m_pickers_layout(0),
    m_new_color_square(0),
    m_old_color_square(0),
    m_new_color_square_text(0),
    m_old_color_square_text(0),
    m_color_squares_layout(0),
    m_color_buttons_layout(0),
    m_current_color_button(-1),
    m_ignore_sliders(false),
    m_ok(0),
    m_cancel(0),
    m_sliders_ok_cancel_layout(0),
    m_color(dialog_color),
    m_border_color(border_color),
    m_text_color(text_color)
{
    Init(font);
}

bool ColorDlg::ColorWasSelected() const
{
    return m_color_was_picked;
}

Clr ColorDlg::Result() const
{
    return Convert(m_current_color);
}

const std::string& ColorDlg::NewString(const std::string& str) const
{
    return m_new_str;
}

const std::string& ColorDlg::OldString(const std::string& str) const
{
    return m_old_str;
}

const std::string& ColorDlg::RedString(const std::string& str) const
{
    return m_red_str;
}

const std::string& ColorDlg::GreenString(const std::string& str) const
{
    return m_green_str;
}

const std::string& ColorDlg::BlueString(const std::string& str) const
{
    return m_blue_str;
}

const std::string& ColorDlg::HueString(const std::string& str) const
{
    return m_hue_str;
}

const std::string& ColorDlg::SaturationString(const std::string& str) const
{
    return m_saturation_str;
}

const std::string& ColorDlg::ValueString(const std::string& str) const
{
    return m_value_str;
}

const std::string& ColorDlg::AlphaString(const std::string& str) const
{
    return m_alpha_str;
}

const std::string& ColorDlg::OkString(const std::string& str) const
{
    return m_ok_str;
}

const std::string& ColorDlg::CancelString(const std::string& str) const
{
    return m_cancel_str;
}

void ColorDlg::SetNewString(const std::string& str)
{
    m_new_color_square_text->SetText(str);
    m_new_str = str;
}

void ColorDlg::SetOldString(const std::string& str)
{
    m_old_color_square_text->SetText(str);
    m_old_str = str;
}

void ColorDlg::SetRedString(const std::string& str)
{
    m_slider_labels[R]->SetText(str);
    m_red_str = str;
}

void ColorDlg::SetGreenString(const std::string& str)
{
    m_slider_labels[G]->SetText(str);
    m_green_str = str;
}

void ColorDlg::SetBlueString(const std::string& str)
{
    m_slider_labels[B]->SetText(str);
    m_blue_str = str;
}

void ColorDlg::SetHueString(const std::string& str)
{
    m_slider_labels[H]->SetText(str);
    m_hue_str = str;
}

void ColorDlg::SetSaturationString(const std::string& str)
{
    m_slider_labels[S]->SetText(str);
    m_saturation_str = str;
}

void ColorDlg::SetValueString(const std::string& str)
{
    m_slider_labels[V]->SetText(str);
    m_value_str = str;
}

void ColorDlg::SetAlphaString(const std::string& str)
{
    m_slider_labels[A]->SetText(str);
    m_alpha_str = str;
}

void ColorDlg::SetOkString(const std::string& str)
{
    m_ok->SetText(str);
    m_ok_str = str;
}

void ColorDlg::SetCancelString(const std::string& str)
{
    m_cancel->SetText(str);
    m_cancel_str = str;
}

void ColorDlg::Render()
{
    Pt ul = UpperLeft(), lr = LowerRight();
    FlatRectangle(ul.x, ul.y, lr.x, lr.y, m_color, m_border_color, 1);
    if (m_current_color_button != -1) {
        Pt button_ul = m_color_buttons[m_current_color_button]->UpperLeft() - Pt(2, 2);
        Pt button_lr = m_color_buttons[m_current_color_button]->LowerRight() + Pt(2, 2);
        FlatRectangle(button_ul.x, button_ul.y, button_lr.x, button_lr.y, CLR_ZERO, m_text_color, 2);
    }
}

void ColorDlg::KeyPress(Key key, Uint32 key_mods)
{
    if (key == GGK_RETURN || key == GGK_KP_ENTER)
        OkClicked();
    else if (key == GGK_ESCAPE)
        CancelClicked();
}

void ColorDlg::Init(const boost::shared_ptr<Font>& font)
{
    m_current_color = m_original_color_specified ? Convert(m_original_color) : Convert(CLR_BLACK);
    Clr color = Convert(m_current_color);

    boost::shared_ptr<StyleFactory> style = GetStyleFactory();

    const int COLOR_BUTTON_ROWS = 3;
    const int COLOR_BUTTON_COLS = 5;
    if (s_custom_colors.empty()) {
        for (int i = 0; i < COLOR_BUTTON_ROWS * COLOR_BUTTON_COLS; ++i) {
            s_custom_colors.push_back(CLR_BLACK);
        }
    }

    m_hue_saturation_picker = new HueSaturationPicker(10, 10, 300, 300);
    m_hue_saturation_picker->SetHueSaturation(m_current_color.h, m_current_color.s);
    m_value_picker = new ValuePicker(320, 10, 25, 300, m_text_color);
    m_value_picker->SetHueSaturation(m_current_color.h, m_current_color.s);
    m_value_picker->SetValue(m_current_color.v);
    const int HUE_SATURATION_PICKER_SIZE = 200;
    m_pickers_layout = new Layout(0, 0, HUE_SATURATION_PICKER_SIZE + 30, HUE_SATURATION_PICKER_SIZE,
                                  1, 2, 0, 5);
    m_pickers_layout->SetColumnStretch(0, 1);
    m_pickers_layout->SetMinimumColumnWidth(1, 20);
    m_pickers_layout->Add(m_hue_saturation_picker, 0, 0);
    m_pickers_layout->Add(m_value_picker, 0, 1);

    m_color_squares_layout = new Layout(0, m_pickers_layout->LowerRight().y + 5, m_pickers_layout->Width(), 40,
                                        1, 1, 0, 4);
    m_new_color_square = new ColorDisplay(color);
    if (m_original_color_specified) {
        m_new_color_square_text = style->NewTextControl(0, 0, 1, 1, m_new_str, font, m_text_color, TF_RIGHT);
        m_color_squares_layout->Add(m_new_color_square_text, 0, 0);
        m_color_squares_layout->Add(m_new_color_square, 0, 1);
        m_old_color_square_text = style->NewTextControl(0, 0, 1, 1, m_old_str, font, m_text_color, TF_RIGHT);
        m_color_squares_layout->Add(m_old_color_square_text, 1, 0);
        m_old_color_square = new ColorDisplay(m_original_color);
        m_color_squares_layout->Add(m_old_color_square, 1, 1);
        m_color_squares_layout->SetMinimumColumnWidth(0, 30);
        m_color_squares_layout->SetColumnStretch(1, 1);
    } else {
        m_color_squares_layout->Add(m_new_color_square, 0, 0);
    }

    m_color_buttons_layout = new Layout(0, m_color_squares_layout->LowerRight().y + 5, m_pickers_layout->Width(), 80,
                                        COLOR_BUTTON_ROWS, COLOR_BUTTON_COLS, 0, 4);
    for (int i = 0; i < COLOR_BUTTON_ROWS; ++i) {
        for (int j = 0; j < COLOR_BUTTON_COLS; ++j) {
            m_color_buttons.push_back(new ColorButton(m_color));
            m_color_buttons.back()->SetRepresentedColor(s_custom_colors[i * COLOR_BUTTON_COLS + j]);
            m_color_buttons_layout->Add(m_color_buttons.back(), i, j);
        }
    }

    using boost::lexical_cast;
    m_sliders_ok_cancel_layout = new Layout(m_pickers_layout->LowerRight().x + 5, 0, 150, (25 + 5) * 8 - 5,
                                            9, 3, 0, 5);
    m_sliders_ok_cancel_layout->SetMinimumColumnWidth(0, 15);
    m_sliders_ok_cancel_layout->SetMinimumColumnWidth(1, 30);
    m_sliders_ok_cancel_layout->SetColumnStretch(2, 1);
    m_slider_labels.push_back(style->NewTextControl(0, 0, 1, 1, m_red_str, font, m_text_color, TF_RIGHT));
    m_sliders_ok_cancel_layout->Add(m_slider_labels.back(), 0, 0);
    m_slider_values.push_back(style->NewTextControl(0, 0, 1, 1, lexical_cast<std::string>(static_cast<int>(color.r)),
                                                    font, m_text_color, TF_LEFT));
    m_sliders_ok_cancel_layout->Add(m_slider_values.back(), 0, 1);
    m_sliders.push_back(style->NewSlider(0, 0, 1, 1, 0, 255, HORIZONTAL,
                                         RAISED, m_color, 10));
    m_sliders.back()->SlideTo(color.r);
    m_sliders_ok_cancel_layout->Add(m_sliders.back(), 0, 2);

    m_slider_labels.push_back(style->NewTextControl(0, 0, 1, 1, m_green_str, font, m_text_color, TF_RIGHT));
    m_sliders_ok_cancel_layout->Add(m_slider_labels.back(), 1, 0);
    m_slider_values.push_back(style->NewTextControl(0, 0, 1, 1, lexical_cast<std::string>(static_cast<int>(color.g)),
                                                    font, m_text_color, TF_LEFT));
    m_sliders_ok_cancel_layout->Add(m_slider_values.back(), 1, 1);
    m_sliders.push_back(style->NewSlider(0, 0, 1, 1, 0, 255, HORIZONTAL,
                                         RAISED, m_color, 10));
    m_sliders.back()->SlideTo(color.g);
    m_sliders_ok_cancel_layout->Add(m_sliders.back(), 1, 2);

    m_slider_labels.push_back(style->NewTextControl(0, 0, 1, 1, m_blue_str, font, m_text_color, TF_RIGHT));
    m_sliders_ok_cancel_layout->Add(m_slider_labels.back(), 2, 0);
    m_slider_values.push_back(style->NewTextControl(0, 0, 1, 1, lexical_cast<std::string>(static_cast<int>(color.b)),
                                                    font, m_text_color, TF_LEFT));
    m_sliders_ok_cancel_layout->Add(m_slider_values.back(), 2, 1);
    m_sliders.push_back(style->NewSlider(0, 0, 1, 1, 0, 255, HORIZONTAL,
                                         RAISED, m_color, 10));
    m_sliders.back()->SlideTo(color.b);
    m_sliders_ok_cancel_layout->Add(m_sliders.back(), 2, 2);

    m_slider_labels.push_back(style->NewTextControl(0, 0, 1, 1, m_alpha_str, font, m_text_color, TF_RIGHT));
    m_sliders_ok_cancel_layout->Add(m_slider_labels.back(), 3, 0);
    m_slider_values.push_back(style->NewTextControl(0, 0, 1, 1, lexical_cast<std::string>(static_cast<int>(color.a)),
                                                    font, m_text_color, TF_LEFT));
    m_sliders_ok_cancel_layout->Add(m_slider_values.back(), 3, 1);
    m_sliders.push_back(style->NewSlider(0, 0, 1, 1, 0, 255, HORIZONTAL,
                                         RAISED, m_color, 10));
    m_sliders.back()->SlideTo(color.a);
    m_sliders_ok_cancel_layout->Add(m_sliders.back(), 3, 2);

    m_slider_labels.push_back(style->NewTextControl(0, 0, 1, 1, m_hue_str, font, m_text_color, TF_RIGHT));
    m_sliders_ok_cancel_layout->Add(m_slider_labels.back(), 4, 0);
    m_slider_values.push_back(style->NewTextControl(0, 0, 1, 1, lexical_cast<std::string>(static_cast<int>(m_current_color.h * 359)),
                                                    font, m_text_color, TF_LEFT));
    m_sliders_ok_cancel_layout->Add(m_slider_values.back(), 4, 1);
    m_sliders.push_back(style->NewSlider(0, 0, 1, 1, 0, 359, HORIZONTAL,
                                         RAISED, m_color, 10));
    m_sliders.back()->SlideTo(static_cast<int>(m_current_color.h * 359));
    m_sliders_ok_cancel_layout->Add(m_sliders.back(), 4, 2);

    m_slider_labels.push_back(style->NewTextControl(0, 0, 1, 1, m_saturation_str, font, m_text_color, TF_RIGHT));
    m_sliders_ok_cancel_layout->Add(m_slider_labels.back(), 5, 0);
    m_slider_values.push_back(style->NewTextControl(0, 0, 1, 1, lexical_cast<std::string>(static_cast<int>(m_current_color.s * 255)),
                                                    font, m_text_color, TF_LEFT));
    m_sliders_ok_cancel_layout->Add(m_slider_values.back(), 5, 1);
    m_sliders.push_back(style->NewSlider(0, 0, 1, 1, 0, 255, HORIZONTAL,
                                         RAISED, m_color, 10));
    m_sliders.back()->SlideTo(static_cast<int>(m_current_color.s * 255));
    m_sliders_ok_cancel_layout->Add(m_sliders.back(), 5, 2);

    m_slider_labels.push_back(style->NewTextControl(0, 0, 1, 1, m_value_str, font, m_text_color, TF_RIGHT));
    m_sliders_ok_cancel_layout->Add(m_slider_labels.back(), 6, 0);
    m_slider_values.push_back(style->NewTextControl(0, 0, 1, 1, lexical_cast<std::string>(static_cast<int>(m_current_color.v * 255)),
                                                    font, m_text_color, TF_LEFT));
    m_sliders_ok_cancel_layout->Add(m_slider_values.back(), 6, 1);
    m_sliders.push_back(style->NewSlider(0, 0, 1, 1, 0, 255, HORIZONTAL,
                                         RAISED, m_color, 10));
    m_sliders.back()->SlideTo(static_cast<int>(m_current_color.v * 255));
    m_sliders_ok_cancel_layout->Add(m_sliders.back(), 6, 2);

    m_ok = style->NewButton(0, 0, 1, 1, m_ok_str, font, m_color, m_text_color);
    m_sliders_ok_cancel_layout->Add(m_ok, 7, 0, 1, 3);
    m_cancel = style->NewButton(0, 0, 1, 1, m_cancel_str, font, m_color, m_text_color);
    m_sliders_ok_cancel_layout->Add(m_cancel, 8, 0, 1, 3);

    Layout* master_layout = new Layout(0, 0, ClientWidth(), ClientHeight(), 3, 2, 5, 5);
    master_layout->SetColumnStretch(0, 1.25);
    master_layout->SetColumnStretch(1, 1);
    master_layout->SetRowStretch(0, 1.25);
    master_layout->SetMinimumRowHeight(1, 40);
    master_layout->SetRowStretch(2, 1);
    master_layout->Add(m_pickers_layout, 0, 0);
    master_layout->Add(m_color_squares_layout, 1, 0);
    master_layout->Add(m_color_buttons_layout, 2, 0);
    master_layout->Add(m_sliders_ok_cancel_layout, 0, 1, 3, 1);
    SetLayout(master_layout);

    ConnectSignals();
}

void ColorDlg::ConnectSignals()
{
    for (unsigned int i = 0; i < m_color_buttons.size(); ++i) {
        Connect(m_color_buttons[i]->ClickedSignal, ColorButtonClickFunctor(i, this));
    }
    Connect(m_sliders[R]->SlidSignal, &ColorDlg::RedSliderChanged, this);
    Connect(m_sliders[G]->SlidSignal, &ColorDlg::GreenSliderChanged, this);
    Connect(m_sliders[B]->SlidSignal, &ColorDlg::BlueSliderChanged, this);
    Connect(m_sliders[A]->SlidSignal, &ColorDlg::AlphaSliderChanged, this);
    Connect(m_sliders[H]->SlidSignal, &ColorDlg::HueSliderChanged, this);
    Connect(m_sliders[S]->SlidSignal, &ColorDlg::SaturationSliderChanged, this);
    Connect(m_sliders[V]->SlidSignal, &ColorDlg::ValueSliderChanged, this);
    Connect(m_ok->ClickedSignal, &ColorDlg::OkClicked, this);
    Connect(m_cancel->ClickedSignal, &ColorDlg::CancelClicked, this);
    Connect(m_hue_saturation_picker->ChangedSignal, &ValuePicker::SetHueSaturation, m_value_picker);
    Connect(m_hue_saturation_picker->ChangedSignal, &ColorDlg::HueSaturationPickerChanged, this);
    Connect(m_value_picker->ChangedSignal, &ColorDlg::ValuePickerChanged, this);
}

void ColorDlg::ColorChanged(HSVClr color)
{
    m_current_color = color;
    m_hue_saturation_picker->SetHueSaturation(m_current_color.h, m_current_color.s);
    m_value_picker->SetHueSaturation(m_current_color.h, m_current_color.s);
    m_value_picker->SetValue(m_current_color.v);
    Clr rgb_color = Convert(m_current_color);
    m_new_color_square->SetColor(rgb_color);
    if (m_current_color_button != -1) {
        m_color_buttons[m_current_color_button]->SetRepresentedColor(rgb_color);
        s_custom_colors[m_current_color_button] = rgb_color;
    }
    m_ignore_sliders = true;
    UpdateRGBSliders();
    UpdateHSVSliders();
    m_ignore_sliders = false;
}

void ColorDlg::HueSaturationPickerChanged(double hue, double saturation)
{
    m_current_color.h = hue;
    m_current_color.s = saturation;
    ColorChanged(m_current_color);
}

void ColorDlg::ValuePickerChanged(double value)
{
    m_current_color.v = value;
    ColorChanged(m_current_color);
}

void ColorDlg::UpdateRGBSliders()
{
    Clr color = Convert(m_current_color);
    *m_slider_values[R] << static_cast<int>(color.r);
    *m_slider_values[G] << static_cast<int>(color.g);
    *m_slider_values[B] << static_cast<int>(color.b);
    *m_slider_values[A] << static_cast<int>(color.a);
    m_sliders[R]->SlideTo(color.r);
    m_sliders[G]->SlideTo(color.g);
    m_sliders[B]->SlideTo(color.b);
    m_sliders[A]->SlideTo(color.a);
}

void ColorDlg::UpdateHSVSliders()
{
    *m_slider_values[H] << static_cast<int>(m_current_color.h * 359);
    *m_slider_values[S] << static_cast<int>(m_current_color.s * 255);
    *m_slider_values[V] << static_cast<int>(m_current_color.v * 255);
    m_sliders[H]->SlideTo(static_cast<int>(m_current_color.h * 359));
    m_sliders[S]->SlideTo(static_cast<int>(m_current_color.s * 255));
    m_sliders[V]->SlideTo(static_cast<int>(m_current_color.v * 255));
}

void ColorDlg::ColorChangeFromRGBSlider()
{
    Clr color = Convert(m_current_color);
    m_hue_saturation_picker->SetHueSaturation(m_current_color.h, m_current_color.s);
    m_value_picker->SetHueSaturation(m_current_color.h, m_current_color.s);
    m_value_picker->SetValue(m_current_color.v);
    m_new_color_square->SetColor(color);
    if (m_current_color_button != -1) {
        m_color_buttons[m_current_color_button]->SetRepresentedColor(color);
        s_custom_colors[m_current_color_button] = color;
    }
    m_ignore_sliders = true;
    UpdateHSVSliders();
    m_ignore_sliders = false;
}

void ColorDlg::ColorButtonClicked(int i)
{
    m_current_color_button = i;
    m_current_color = Convert(m_color_buttons[m_current_color_button]->RepresentedColor());
    ColorChanged(m_current_color);
}

void ColorDlg::RedSliderChanged(int value, int low, int high)
{
    if (m_ignore_sliders)
        return;
    m_ignore_sliders = true;
    Clr color = Convert(m_current_color);
    color.r = value;
    m_current_color = Convert(color);
    ColorChangeFromRGBSlider();
    *m_slider_values[R] << value;
    m_ignore_sliders = false;
}

void ColorDlg::GreenSliderChanged(int value, int low, int high)
{
    if (m_ignore_sliders)
        return;
    m_ignore_sliders = true;
    Clr color = Convert(m_current_color);
    color.g = value;
    m_current_color = Convert(color);
    ColorChangeFromRGBSlider();
    *m_slider_values[G] << value;
    m_ignore_sliders = false;
}

void ColorDlg::BlueSliderChanged(int value, int low, int high)
{
    if (m_ignore_sliders)
        return;
    m_ignore_sliders = true;
    Clr color = Convert(m_current_color);
    color.b = value;
    m_current_color = Convert(color);
    ColorChangeFromRGBSlider();
    *m_slider_values[B] << value;
    m_ignore_sliders = false;
}

void ColorDlg::AlphaSliderChanged(int value, int low, int high)
{
    if (m_ignore_sliders)
        return;
    m_ignore_sliders = true;
    Clr color = Convert(m_current_color);
    color.a = value;
    m_current_color = Convert(color);
    ColorChangeFromRGBSlider();
    *m_slider_values[A] << value;
    m_ignore_sliders = false;
}

void ColorDlg::HueSliderChanged(int value, int low, int high)
{
    if (m_ignore_sliders)
        return;
    m_current_color.h = value / static_cast<double>(high - low);
    ColorChanged(m_current_color);
}

void ColorDlg::SaturationSliderChanged(int value, int low, int high)
{
    if (m_ignore_sliders)
        return;
    m_current_color.s = value / static_cast<double>(high - low);
    ColorChanged(m_current_color);
}

void ColorDlg::ValueSliderChanged(int value, int low, int high)
{
    if (m_ignore_sliders)
        return;
    m_current_color.v = value / static_cast<double>(high - low);
    ColorChanged(m_current_color);
}

void ColorDlg::OkClicked()
{
    m_color_was_picked = true;
    m_done = true;
}

void ColorDlg::CancelClicked()
{
    m_current_color = Convert(m_original_color);
    m_done = true;
}

