//
// A simple color editor using glyphs -- Dave Sternlicht, with alot of help
// from Mark Linton.
//
#include <IV-look/adjustable.h>
#include <IV-look/adjuster.h>
#include <IV-look/button.h>
#include <IV-look/kit.h>
#include <IV-look/menu.h>
#include <IV-look/telltale.h>
#include <InterViews/background.h>
#include <InterViews/box.h>
#include <InterViews/center.h>
#include <InterViews/color.h>
#include <InterViews/display.h>
#include <InterViews/fixedspan.h>
#include <InterViews/glue.h>
#include <InterViews/label.h>
#include <InterViews/margin.h>
#include <InterViews/patch.h>
#include <InterViews/session.h>
#include <InterViews/style.h>
#include <InterViews/window.h>
#include <stdio.h>

//
// Valuator
//

class Valuator : public Adjustable {
public:
    Valuator(Coord lower, Coord upper);

    virtual Coord lower(DimensionName) const;
    virtual Coord upper(DimensionName) const;
    virtual Coord length(DimensionName) const;
    virtual Coord cur_lower(DimensionName) const;
    virtual Coord cur_upper(DimensionName) const;
    virtual Coord cur_length(DimensionName) const;

    void init(Coord value);
    virtual void scroll_to(DimensionName, Coord position);
    virtual void scroll_forward(DimensionName);
    virtual void scroll_backward(DimensionName);
private:
    Coord lower_;
    Coord span_;
    Coord curlower_;
    Coord len_;
};

Valuator::Valuator(Coord lower, Coord upper) {
    lower_ = lower;
    span_ = upper - lower;
    len_ = span_ / 10.0;
    curlower_ = (lower + upper - len_) / 2;
}

Coord Valuator::lower(DimensionName) const { return lower_; }
Coord Valuator::upper(DimensionName) const { return lower_ + span_; }
Coord Valuator::length(DimensionName) const { return span_; }
Coord Valuator::cur_lower(DimensionName) const { return curlower_; }
Coord Valuator::cur_upper(DimensionName) const { return curlower_ + len_; }
Coord Valuator::cur_length(DimensionName) const { return len_; }

void Valuator::init(Coord value) {
    curlower_ = value - len_ / 2;
    constrain(Dimension_X, curlower_);
    notify();
}

void Valuator::scroll_to(DimensionName d, Coord position) {
    Coord p = position - len_ / 2;
    constrain(d, p);
    if (p != curlower_) {
	curlower_ = p;
	notify();
    }
}

void Valuator::scroll_forward(DimensionName d) {
    scroll_to(d, curlower_ + len_);
}

void Valuator::scroll_backward(DimensionName d) {
    scroll_to(d, curlower_);
}

class ValuatorView : public MonoGlyph, public Adjuster {
public:
  ValuatorView(Valuator*, Style*);
    virtual ~ValuatorView();

    virtual void update_all();
private:
    Style* style_;
    Patch* patch_;
};

//
// ValuatorView
//

ValuatorView::ValuatorView(
    Valuator* v, Style* s
) : MonoGlyph(nil), Adjuster(v) {
    Resource::ref(s);
    style_ = s;
    patch_ = new Patch(nil);
    body(patch_);
}

ValuatorView::~ValuatorView() {
    Resource::unref(style_);
}

void ValuatorView::update_all() {
   Adjustable* a = subject(); 
   Coord v = a->cur_lower(Dimension_X) + a->cur_length(Dimension_X) / 2;

    char buf[20];
    sprintf(buf, "value = %f", v);
    patch_->body(
	new HMargin(
	    new Label(buf, style_->font(), style_->foreground()),
	    0.0, fil, 0.0, 0.0, fil, 0.0
	)
    );
    patch_->redraw();
    patch_->reallocate();
    patch_->redraw();
}

//
// ColorViewer
//

class ColorViewer : public Patch, public Adjuster {
public:
    ColorViewer(Valuator*, Valuator*, Valuator*, Style*);
    virtual ~ColorViewer();

    virtual void allocate(Canvas*, const Allocation&, Extension&);
    virtual void draw(Canvas*, const Allocation&) const;
    virtual void update_all();
    virtual void disconnect(Adjustable*);
private:
    Adjustable* red_;
    Adjustable* green_;
    Adjustable* blue_;
    Color* color_;
};

ColorViewer::ColorViewer(
    Valuator* r, Valuator* g, Valuator* b, Style*
) : Patch(nil) {
    red_ = r;
    red_->attach(this);
    green_ = g;
    green_->attach(this);
    blue_ = b;
    blue_->attach(this);
    color_ = nil;
}

ColorViewer::~ColorViewer() {
    if (red_ != nil) {
	red_->detach(this);
    }
    if (green_ != nil) {
	green_->detach(this);
    }
    if (blue_ != nil) {
	blue_->detach(this);
    }
}

void ColorViewer::allocate(Canvas* c, const Allocation& a, Extension& ext) {
    ext.xy_extents(a);
    Patch::allocate(c, a, ext);
}

void ColorViewer::draw(Canvas* c, const Allocation& a) const {
    c->fill_rect(a.left(), a.bottom(), a.right(), a.top(), color_);
}

void ColorViewer::update_all() {
    Resource::unref(color_);
    float foo =  red_->cur_lower(Dimension_X);
    color_ = new Color(
	float(red_->cur_lower(Dimension_X)),
	float(green_->cur_lower(Dimension_X)),
	float(blue_->cur_lower(Dimension_X)),
	1.0
    );
    Resource::ref(color_);
    redraw();
}

void ColorViewer::disconnect(Adjustable* a) {
    if (a == red_) {
	red_ = nil;
    } else if (a == green_) {
	green_ = nil;
    } else if (a == blue_) {
	blue_ = nil;
    }
}

//
// main
//

int main(int argc, char** argv) {
    Session* session = new Session(
	"ColorViewer", argc, argv);
    Style* style = session->style();
    Kit* kit = Kit::instance();
    Valuator* v1 = new Valuator(0.0, 1.0);
    Valuator* v2 = new Valuator(0.0, 1.0);
    Valuator* v3 = new Valuator(0.0, 1.0);
    Window* w = new ApplicationWindow(
	new Background(
	    new LRBox(
		new TBBox(
		    new VCenter(
			new LRBox(
			    new ValuatorView(v1, style),
			    new ValuatorView(v2, style),
			    new ValuatorView(v3, style)
			),
			1.0
		    ),
		    new FixedSpan(
			new ColorViewer(v1, v2, v3, style),
			Dimension_Y, Coord(144.0)
		    ),
		),
		kit->vscroll_bar(v1, style),
		kit->vscroll_bar(v2, style),
		kit->vscroll_bar(v3, style)
	    ),
	    style->background()
	)
    );
    v1->init(0.5);
    v2->init(0.5);
    v3->init(0.5);
    return session->run_window(w);
}

