// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: gradient.cpp 264 2007-03-01 10:10:54Z wojdyr $

/// In this file:
///  Gradient Dialog (GradientDlg) and helpers

#include <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/spinctrl.h>

#include "gradient.h"
#include "cmn.h" //SpinCtrl, change_color_dlg, add_apply_close_buttons, iround
#include "../common.h" //iround()

using namespace std;

enum {
    ID_CGD_RADIO               = 27900
};

BEGIN_EVENT_TABLE(ColorSpinSelector, wxPanel)
    EVT_BUTTON (-1, ColorSpinSelector::OnSelector)
END_EVENT_TABLE()

ColorSpinSelector::ColorSpinSelector(wxWindow *parent, wxString const& title,
                                     wxColour const& col)
    : wxPanel(parent, -1)
{
    wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
    wxStaticBoxSizer *sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, title);
    sizer->Add(new wxStaticText(this, -1, wxT("R")), 
                    0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5);
    r = new SpinCtrl(this, -1, col.Red(), 0, 255);
    sizer->Add(r, 0, wxALL, 5);
    sizer->Add(new wxStaticText(this, -1, wxT("G")), 
                    0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5);
    g = new SpinCtrl(this, -1, col.Green(), 0, 255);
    sizer->Add(g, 0, wxALL, 5);
    sizer->Add(new wxStaticText(this, -1, wxT("B")), 
                    0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5);
    b = new SpinCtrl(this, -1, col.Blue(), 0, 255);
    sizer->Add(b, 0, wxALL, 5);
    sizer->Add(new wxButton(this, -1, wxT("Selector...")), 0, wxALL, 5);
    top_sizer->Add(sizer, 1, wxEXPAND);
    SetSizerAndFit(top_sizer);
}

void ColorSpinSelector::OnSelector(wxCommandEvent &)
{
    wxColour c(r->GetValue(), g->GetValue(), b->GetValue());
    if (change_color_dlg(c)) {
        r->SetValue(c.Red());
        g->SetValue(c.Green());
        b->SetValue(c.Blue());
    }
    // parent is notified about changes by handling all wxSpinCtrl events
    wxSpinEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED);
    wxPostEvent(this, event);
}


BEGIN_EVENT_TABLE(GradientDlg, wxDialog)
    EVT_SPINCTRL (-1, GradientDlg::OnSpinEvent)
    EVT_RADIOBOX (ID_CGD_RADIO, GradientDlg::OnRadioChanged)
END_EVENT_TABLE()

GradientDlg::GradientDlg(wxWindow *parent, wxWindowID id, 
                         wxColour const& first_col, wxColour const& last_col)
    : wxDialog(parent, id, wxT("Select color gradient"), 
               wxDefaultPosition, wxDefaultSize, 
               wxDEFAULT_DIALOG_STYLE/*|wxRESIZE_BORDER*/)
{
    wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL);

    from = new ColorSpinSelector(this, wxT("from"), first_col);
    top_sizer->Add(from, 0, wxALL, 5);

    to = new ColorSpinSelector(this, wxT("to"), last_col);
    top_sizer->Add(to, 0, wxALL, 5);

    wxArrayString choices;
    choices.Add(wxT("HSV gradient, clockwise hue"));
    choices.Add(wxT("HSV gradient, counter-clockwise"));
    choices.Add(wxT("RGB gradient"));
    choices.Add(wxT("one color"));
    kind_rb = new wxRadioBox(this, ID_CGD_RADIO, wxT("how to extrapolate..."),
                             wxDefaultPosition, wxDefaultSize, choices,
                             1, wxRA_SPECIFY_COLS);
    top_sizer->Add(kind_rb, 0, wxALL|wxEXPAND, 5);
    display = new ColorGradientDisplay<GradientDlg>(this, 
                                 this, &GradientDlg::update_gradient_display);
    display->SetMinSize(wxSize(-1, 15));
    top_sizer->Add(display, 0, wxALL|wxEXPAND, 5);

    add_apply_close_buttons(this, top_sizer);
    SetSizerAndFit(top_sizer);
    update_gradient_display();
}

void GradientDlg::update_gradient_display()
{
    int display_width = display->GetClientSize().GetWidth();
    display->data.resize(display_width);
    for (int i = 0; i < display_width; ++i)
        display->data[i] = get_value(i / (display_width-1.0));
    display->Refresh();
}

static void rgb2hsv(unsigned char r, unsigned char g, unsigned char b,
                    unsigned char &h, unsigned char &s, unsigned char &v)
{
    v = max(max(r, g), b);
    h = s = 0;
    if (v == 0) 
        return;
    unsigned char delta = v - min(min(r, g), b);
    s = 255 * delta / v;
    if (s == 0) 
        return;
    if (v == r) 
        h = 43 * (g - b) / delta;
    else if (v == g) 
        h = 85 + 43 * (b - r) / delta;
    else  //  v == b
        h = 171 + 43 * (r - g) / delta;
}

static wxColour hsv2wxColour(unsigned char h, unsigned char s, unsigned char v)
{
    if (s == 0) 
        return wxColour(v, v, v);

    float hx = h / 42.501;      // 0 <= hx <= 5.
    int i = (int) floor(hx);
    float f = hx - i; 
    float sx = s / 255.;
    unsigned char p = iround(v * (1 - sx));
    unsigned char q = iround(v * (1 - sx * f));
    unsigned char t = iround(v * (1 - sx * (1 - f)));

    switch(i) {
        case 0:
            return wxColour(v, t, p);
        case 1:
            return wxColour(q, v, p);
        case 2:
            return wxColour(p, v, t);
        case 3:
            return wxColour(p, q, v);
        case 4:
            return wxColour(t, p, v);
        case 5:                
        default:
            return wxColour(v, p, q);
    }
}


wxColour GradientDlg::get_value(float x)
{
    wxColour c;
    if (x < 0) 
        x = 0;
    if (x > 1)
        x = 1;
    int kind = kind_rb->GetSelection();
    if (kind == 0 || kind == 1) { //hsv
        unsigned char h1, s1, v1, h2, s2, v2;
        rgb2hsv(from->r->GetValue(), from->g->GetValue(), from->b->GetValue(), 
                h1, s1, v1);
        rgb2hsv(to->r->GetValue(), to->g->GetValue(), to->b->GetValue(), 
                h2, s2, v2);
        int corr = 0;
        if (kind == 0 && h1 > h2)
            corr = 256;
        else if (kind == 1 && h1 < h2)
            corr = -256;
        c = hsv2wxColour(iround(h1 * (1-x) + (h2 + corr) * x), 
                         iround(s1 * (1-x) + s2 * x), 
                         iround(v1 * (1-x) + v2 * x)); 
    }
    else if (kind == 2) { //rgb
        c = wxColour(
                iround(from->r->GetValue() * (1-x) + to->r->GetValue() * x), 
                iround(from->g->GetValue() * (1-x) + to->g->GetValue() * x), 
                iround(from->b->GetValue() * (1-x) + to->b->GetValue() * x)); 
    }
    else { //one color
        c = wxColour(from->r->GetValue(), from->g->GetValue(), 
                        from->b->GetValue());
    }
    return c;
}



syntax highlighted by Code2HTML, v. 0.9.1