#include <../../nrnconf.h>
#include "classreg.h"
#if HAVE_IV

#include <InterViews/handler.h>
#include <InterViews/action.h>
#include <InterViews/style.h>
#include <InterViews/color.h>
#include <InterViews/polyglyph.h>
#include <IV-look/kit.h>
#include <InterViews/layout.h>
#include <InterViews/background.h>
#include <OS/math.h>

#include <ivstream.h>
#include <stdio.h>
#include "scenepic.h"
#include "shapeplt.h"
#include "graph.h"
#include "ivoc.h"
#include "nrnoc2iv.h"
#include "rubband.h"
#include "symchoos.h"
#include "symdir.h"
#include "parse.h"
#include "utility.h"
#include "objcmd.h"
#include "idraw.h"

#define SelectVariable_		"PlotWhat PlotShape"
#define VariableScale_		"VariableScale PlotShape"
#define TimePlot_		"TimePlot PlotShape"
#define SpacePlot_		"SpacePlot PlotShape"
#define ShapePlot_		"ShapePlot PlotShape"
#define MoveText_		"MoveText PlotShape"

extern "C" {
extern Symlist* hoc_built_in_symlist;
}	
#endif // HAVE_IV

// PlotShape class registration for oc
static double sh_flush(void* v) {
#if HAVE_IV
IFGUI
	((ShapePlot*)v)->flush();
ENDGUI
#endif
	return 1.;
}

static double fast_flush(void* v) {
#if HAVE_IV
IFGUI
	((ShapePlot*)v)->fast_flush();
ENDGUI
#endif
	return 1.;
}

static double sh_begin(void*) {// a noop. Exists only because graphs and
	return 1.;		// shapes are often in same list
}

static double sh_scale(void* v) {
#if HAVE_IV
IFGUI
	((ShapePlot*)v)->scale(float(*getarg(1)), float(*getarg(2)));
ENDGUI
#endif
	return 1.;
}

static double sh_view(void* v) {
#if HAVE_IV
IFGUI
	ShapePlot* sh = (ShapePlot*)v;
	if (ifarg(8)) {
		Coord x[8]; int i;
		for (i=0; i < 8; ++i) {
			x[i] = *getarg(i+1);
		}
		sh->view(x);
	}
ENDGUI
#endif
	return 1.;
}

static double sh_variable(void* v) {
#if HAVE_IV
IFGUI
	Symbol* s;
	s = hoc_table_lookup(gargstr(1), hoc_built_in_symlist);
	if (s) {
		((ShapePlot*)v)->variable(s);
	}
ENDGUI
#endif
	return 1.;
}

static double sh_view_count(void* v) {
	int n = 0;
#if HAVE_IV
IFGUI
	n = ((ShapeScene*)v)->view_count();
ENDGUI
#endif
	return double(n);
}

static double sh_save_name(void* v) {
#if HAVE_IV
IFGUI
	((ShapeScene*)v)->name(gargstr(1));
ENDGUI
#endif
	return 1.;
}

static double sh_unmap(void*v) {
#if HAVE_IV
IFGUI
	ShapeScene* s = (ShapeScene*)v;
	s->dismiss();
ENDGUI
#endif
	return 0.;
}

static double sh_printfile(void* v) {
#if HAVE_IV
IFGUI
	ShapeScene* s = (ShapeScene*)v;
	s->printfile(gargstr(1));
ENDGUI
#endif
	return 1.;
}

static double sh_show(void* v) {
#if HAVE_IV
IFGUI
	ShapeScene* s = (ShapeScene*)v;
	s->shape_type(int(chkarg(1, 0., 2.)));
ENDGUI
#endif
	return 1.;
}

extern double ivoc_gr_menu_action(void* v);

static double s_colormap(void* v) {
#if HAVE_IV
IFGUI
	ShapePlot* s = (ShapePlot*)v;
	if (ifarg(4)) {
		s->color_value()->colormap(int(chkarg(1, 0, 255)),
			int(chkarg(2, 0, 255)),
			int(chkarg(3, 0, 255)),
			int(chkarg(4, 0, 255))
		);
	}else{
		bool b = false;
		if (ifarg(2)) {
			b = (bool)chkarg(2, 0, 1);
		}
		s->color_value()->colormap(int(chkarg(1, 0, 1000)), b);
	}
ENDGUI
#endif
	return 1.;
}

static double sh_hinton(void* v) {
#if HAVE_IV
IFGUI
	ShapeScene* ss = (ShapeScene*)v;
	double* pd = hoc_pgetarg(1);
	double xsize = chkarg(4, 1e-9, 1e9);
	double ysize = xsize;
	if (ifarg(5)) {
		ysize = chkarg(5, 1e-9, 1e9);
	}
	Hinton* h = new Hinton(pd, xsize, ysize, ss);
	ss->append(new FastGraphItem(h));
	ss->move(ss->count() - 1, *getarg(2), *getarg(3));
ENDGUI
#endif
	return 1.;
}

static double exec_menu(void* v) {
#if HAVE_IV
IFGUI
	((Scene*)v)->picker()->exec_item(gargstr(1));
ENDGUI
#endif
	return 0.;
}

extern double ivoc_gr_menu_tool(void*);
extern double ivoc_gr_mark(void*);
extern double ivoc_gr_size(void*);
extern double ivoc_gr_label(void*);
extern double ivoc_gr_line(void*);
extern double ivoc_gr_begin_line(void*);
extern double ivoc_gr_erase(void*);
extern double ivoc_gr_gif(void*);
extern double ivoc_erase_all(void*);
extern double nrniv_sh_observe(void*);
extern double nrniv_sh_rotate(void*);
extern double nrniv_sh_nearest(void*);
extern double nrniv_sh_push(void*);
extern double nrniv_sh_color(void*);
extern double nrniv_sh_color_all(void*);
extern double nrniv_sh_color_list(void*);
extern double nrniv_len_scale(void*);
extern Object** nrniv_sh_nearest_seg(void*);
extern Object** nrniv_sh_selected_seg(void*);

static Member_func sh_members[] = {
	"hinton", sh_hinton,
	"nearest", nrniv_sh_nearest,
	"push_selected", nrniv_sh_push,
	"scale", sh_scale,
	"view", sh_view,
	"size", ivoc_gr_size,
	"view_count", sh_view_count,
	"flush", sh_flush,
	"fastflush", fast_flush,
	"begin", sh_begin,
	"variable", sh_variable,
	"save_name", sh_save_name,
	"unmap", sh_unmap,
	"color", nrniv_sh_color,
	"color_all", nrniv_sh_color_all,
	"color_list", nrniv_sh_color_list,
	"printfile", sh_printfile,
	"show", sh_show,
	"menu_action", ivoc_gr_menu_action,
	"menu_tool", ivoc_gr_menu_tool,
	"colormap", s_colormap, 
	"exec_menu", exec_menu,
	"observe", nrniv_sh_observe,
	"rotate", nrniv_sh_rotate,
	"beginline", ivoc_gr_begin_line,
	"line", ivoc_gr_line,
	"label", ivoc_gr_label,
	"mark", ivoc_gr_mark,
	"erase", ivoc_gr_erase,
	"erase_all", ivoc_erase_all,
	"len_scale", nrniv_len_scale,
	"gif", ivoc_gr_gif,
	0,0
};

static Member_ret_obj_func retobj_members[] = {
	"nearest_seg", nrniv_sh_nearest_seg,
	"selected_seg", nrniv_sh_selected_seg
};

static void* sh_cons(Object* ho) {
#if HAVE_IV
	ShapePlot* sh = NULL;
IFGUI
	int i=1;
	int iarg=1;
	SectionList* sl = NULL;
	// first arg may be an object.
	if (ifarg(iarg)) {
		if (hoc_is_object_arg(iarg)) {
			sl = new SectionList(*hoc_objgetarg(iarg));
			sl->ref();
			++iarg;
		}
	}
	if (ifarg(iarg)) {
		i = int(chkarg(iarg,0,1));
	}
	sh = new ShapePlot(NULL, sl);
	Resource::unref(sl);
	sh->ref();
	sh->hoc_obj_ptr(ho);
	if (i) {
		sh->view(200);
	}
ENDGUI
	return (void*)sh;
#else
	return 0;
#endif
}

static void sh_destruct(void* v) {
#if HAVE_IV
IFGUI
	((ShapeScene*)v)->dismiss();
ENDGUI
	Resource::unref((ShapeScene*)v);
#endif
}
void PlotShape_reg() {
//	printf("PlotShape_reg\n");
	class2oc("PlotShape", sh_cons, sh_destruct, sh_members, NULL, retobj_members, NULL);
}

#if HAVE_IV

/* static */ class ShapePlotImpl : public Observer {
public:
	ShapePlotImpl(ShapePlot*, Symbol*);
	~ShapePlotImpl();
	virtual void time();
	virtual void space();
	virtual void shape();
	virtual void show_shape_val(bool);
	virtual void select_variable();
	virtual void scale();
	virtual void colorbar();
	virtual void update(Observable*);
public:
	ShapePlot* sp_;
	Symbol* sym_;
	GLabel* variable_;
	double graphid_;
	int colorid_;
	SectionHandler* time_sh_;
	bool showing_;
	Glyph* colorbar_;
	bool fast_;
};

/* static */ class MakeTimePlot : public SectionHandler {
public:
	MakeTimePlot(ShapePlotImpl*);
	virtual ~MakeTimePlot();
	virtual bool event(Event&);
private:
	ShapePlotImpl* spi_;
};

/* static */ class MakeSpacePlot : public RubberAction {
public:
	MakeSpacePlot(ShapePlotImpl*);
	virtual ~MakeSpacePlot();
	virtual void execute(Rubberband*);
private:
	ShapePlotImpl* spi_;
};

declareActionCallback(ShapePlotImpl);
implementActionCallback(ShapePlotImpl);

ShapePlot::ShapePlot(Symbol* sym, SectionList* sl) : ShapeScene(sl) {
	spi_ = new ShapePlotImpl(this, sym);
	variable(spi_->sym_);
	picker()->add_menu("Plot What?", new ActionCallback(ShapePlotImpl)(
		spi_, &ShapePlotImpl::select_variable));
	picker()->add_menu("Variable scale", new ActionCallback(ShapePlotImpl)(
		spi_, &ShapePlotImpl::scale));
	picker()->add_radio_menu("Time Plot", new ActionCallback(ShapePlotImpl)(
		spi_, &ShapePlotImpl::time));
	picker()->add_radio_menu("Space Plot", new ActionCallback(ShapePlotImpl)(
		spi_, &ShapePlotImpl::space));
	picker()->add_radio_menu("Shape Plot", new ActionCallback(ShapePlotImpl)(
		spi_, &ShapePlotImpl::shape));
	color_value()->attach(spi_);
	spi_->colorbar();
}
ShapePlot::~ShapePlot() {
	color_value()->detach(spi_);
	delete spi_;
}
void ShapePlot::observe(SectionList* sl) {
//	printf("ShapePlot::observe\n");
	ShapeScene::observe(sl);
	if (spi_->showing_) {
		PolyGlyph* pg = shape_section_list();
		GlyphIndex i, cnt = pg->count();
		for (i=0; i < cnt; ++i) {
			ShapeSection* ss = (ShapeSection*)pg->component(i);
			ss->set_range_variable(spi_->sym_);
		}
		damage_all();
	}
}

void ShapePlot::update_ptrs() {
	PolyGlyph* pg = shape_section_list();
	GlyphIndex i, cnt = pg->count();
	for (i=0; i < cnt; ++i) {
		ShapeSection* ss = (ShapeSection*)pg->component(i);
		ss->update_ptrs();
	}
}

void ShapePlot::erase_all() {
	Resource::unref(spi_->colorbar_);
	spi_->colorbar_ = NULL;
	ShapeScene::erase_all();
}
void ShapePlotImpl::update(Observable*) {
	colorbar();
	sp_->damage_all();
}

void ShapePlot::variable(Symbol* sym) {
	GlyphIndex i;
	spi_->sym_ = sym;
	i = glyph_index(spi_->variable_);
	GLabel* g = new GLabel(spi_->sym_->name, colors->color(1), 1, 1, .5, .5);
	if (i >= 0) {
		damage(i);
		replace(i, new GraphItem(g, 0));
		damage(i);
	}else{
		append_fixed(new GraphItem(g, 0));
	}
	Resource::unref(spi_->variable_);
	Resource::ref(g);
	spi_->variable_ = g;
	if (spi_->showing_) {
		spi_->showing_ = false;
		spi_->show_shape_val(true);
	}
	scale(-80, 40);
}
const char* ShapePlot::varname()const {
	return spi_->sym_->name;
}

void ShapePlot::scale(float min, float max) {
	color_value()->set_scale(min, max);
}
void ShapePlot::save_phase1(ostream& o) {
	o << "{" << endl;
	save_class(o, "PlotShape");
	char buf[256];
	sprintf(buf, "save_window_.variable(\"%s\")", spi_->sym_->name);
	o << buf << endl;
}

void ShapePlot::shape_plot() {}
void ShapePlot::make_time_plot(Section*, float) {
}
void ShapePlot::make_space_plot(Section*, float, Section*, float) {
}
void ShapePlot::flush() {
	spi_->fast_ = false;
	if (tool() == SHAPE) {
		damage_all();
	}
}
void ShapePlot::fast_flush() {
	if (tool() == SHAPE) {
		int i, cnt = view_count();
		spi_->fast_ = true;
		// fast for only one view
		for (i=0; i < cnt; ++i) {
			XYView* v = sceneview(i);
			Coord x = v->left(), y = v->bottom();
#if defined(WIN32)
// if x,y is not on the screen then use right, top. Otherwise InvalidateRect
// will not take effect.
			Window* w = v->canvas() ? v->canvas()->window() : NULL;
			if (w) {
				if (w->left() < 0) {
					x = v->right();
				}
				if (w->bottom() < 0) {
					y = v->top();
				}
			}
#endif
			v->damage(x, y, x, y);
		}
	}
}
#if defined(WIN32)
extern void* mswin_setclip(Canvas*, int, int, int, int);
extern void mswin_delete_object(void*);
#endif

void ShapePlot::draw(Canvas* c, const Allocation& a) const {
	if (spi_->fast_) {
#if defined(WIN32) || MAC
//win32 clipping is much more strict than X11 clipping even though the
//implementations seem to agree that clipping is the intersection of
//all clip requests on the clip stack in canvas. Clipping is originally
//set to the damage area and thus nothing actually gets drawn to
//the buffer. Furthermore, when copying from buffer to screen, only
//the original damage area is copied. For some reason in X11 neither
//seems to prevent the correct appearance although I wonder if it
//will work on all x11 implementations. However, for WIN32, both cause
//problems and thus the implementation of Canvas16::clip was changed to
// not do an intersection and the "damage_all" below along with a change
// to the window.c paint function in which the damage area is reread before
// the bit block transfer solves the problem.
// At any rate this is fast only for the first view since the fast_draw's
// only work once.

		XYView* v = XYView::current_draw_view();
		c->push_clipping(true);
#if MAC
		c->clip_rect(v->left(), v->bottom(), v->right(), v->top());
#endif
#if defined(WIN32)
//Consider the commit message:
// -------
// 28)  Branch: MAIN Date: 2004-05-21 06:30 Commit by: hines 
// canvas16.cpp    
//MSWin PolyGlyphs inside a ScrollBox occasionally do not repair all the damage
// This is apparently a long standing problem dating from 1999 which just
//now became very noticable with the MultipleRunFitter Parameter panel
//when there are many parameters (so that it is a scrollbox). This has
//been traced to a #if 0 fragment in the clipping mechanism which turned
//off the intersection calculation when a new clipping region was requested
// We have turned this section back on. There was no reason mentioned in
//the 1999 log message when the #if 0 was introduced. The major reason for
//the larger change in that file was drawing dashed brush lines. If a
//problem with clipping exists then it will have to be fixed in another way.
// -------
// Well, here is the reason for the #if 0. Without it, the fast_flush movie
// is not drawn for shapeplots. (Strangely, the neurondemo movie works for
// the stylized neuron but not the pyramidal cell.
// So as not to ruin the MAIN 28 commit of canvas16.cpp
// we force the clipping to the entire canvas for the fast_flush.
// Feeble attempts to do everything here, foundered on my inability to
// include <windows.h> in this file. So the implementation is placed in
// ivocwin.cpp. Note that the clip region must be deleted after use.
//
		void* new_clip = mswin_setclip(c, 0, 0, c->pwidth(), c->pheight());
#endif
#endif
		GlyphIndex i, cnt = count();
		for (i=0; i < cnt; ++i) {
			GraphItem* gi = (GraphItem*)component(i);
			if (gi->is_fast()) {
				Coord x, y;
				location(i, x, y);
				((FastShape*)(gi->body()))->fast_draw(c, x, y, false);
			}
		}
#if defined(WIN32) || MAC
		c->pop_clipping();
#if defined(WIN32)
		mswin_delete_object(new_clip);
#endif
		v->damage_all();;
#endif
		spi_->fast_ = false;
	}else{
		ShapeScene::draw(c, a);
	}
}

ShapePlotImpl::ShapePlotImpl(ShapePlot* sp, Symbol* sym) {
	sp_ = sp;
	colorid_ = 0;
	graphid_ = 0;
	showing_ = false;
	fast_ = false;
	colorbar_ = NULL;
	if (sym) {
		sym_ = sym;
	}else{
		sym_ = hoc_table_lookup("v", hoc_built_in_symlist);
	}
	variable_ = NULL;
	time_sh_ = new MakeTimePlot(this);
	time_sh_->ref();
}

ShapePlotImpl::~ShapePlotImpl() {
	Resource::unref(variable_);
	time_sh_->unref();
	Resource::unref(colorbar_);
}

void ShapePlotImpl::colorbar() {
	bool showing;
	if (colorbar_) {
		int i = sp_->glyph_index(colorbar_);
		Resource::unref(colorbar_);
		showing = sp_->showing(i);
		sp_->remove(i);
	}else{
		showing = false;
	}
	colorbar_ = sp_->color_value()->make_glyph();
	colorbar_->ref();
	sp_->append_fixed(new GraphItem(colorbar_, 0));
	sp_->show(sp_->count() - 1, showing);
	if (showing) {
		XYView* v = XYView::current_pick_view();
		sp_->move(sp_->count() - 1, v->left(), v->top());
	}
}

void ShapePlotImpl::select_variable() {
	if (Oc::helpmode()) {
		Oc::help(SelectVariable_);
	}
	SymChooser* sc;
	Oc oc;
        Style* style = new Style(Session::instance()->style());
        style->attribute("caption", "Variable in the shape domain");
        sc = new SymChooser(new SymDirectory(RANGEVAR) , WidgetKit::instance(), style, NULL, 1);
        sc->ref();
        while (sc->post_for(XYView::current_pick_view()->canvas()->window())) {
		Symbol* s;
		s = hoc_table_lookup(sc->selected()->string(), hoc_built_in_symlist);
		if (s) {
			sp_->variable(s);
			break;
		}
	}
	sc->unref();		        
}

void ShapePlotImpl::scale() {
	if (Oc::helpmode()) {
		Oc::help(VariableScale_);
	}
	Coord x, y;
	x = sp_->color_value()->low();	
	y = sp_->color_value()->high();	
        if (var_pair_chooser("Variable range scale", x, y,
        	XYView::current_pick_view()->canvas()->window())) {
		sp_->scale(x, y);
        }
}

void ShapePlotImpl::time() {
	if (Oc::helpmode()) {
		Oc::help(TimePlot_);
		return;
	}
	sp_->tool(ShapePlot::TIME);
	graphid_ = 0;
	colorid_ = 2;
	sp_->color(colors->color(1));
	sp_->section_handler(time_sh_);
	show_shape_val(false);
	sp_->picker()->bind_select((OcHandler*)NULL);
}

void ShapePlotImpl::space() {
	if (Oc::helpmode()) {
		Oc::help(SpacePlot_);
		return;
	}
	sp_->tool(ShapePlot::SPACE);
	graphid_ = 0;
	colorid_ = 1;
	sp_->color(colors->color(1));
	sp_->section_handler((SectionHandler*)NULL);
	show_shape_val(false);
	sp_->picker()->bind_select(new RubberLine(new MakeSpacePlot(this)));
}

void ShapePlotImpl::shape() {
	if (Oc::helpmode()) {
		Oc::help(ShapePlot_);
		return;
	}
	sp_->tool(ShapePlot::SHAPE);
//printf("shape\n");
	sp_->section_handler((SectionHandler*)NULL);
	sp_->picker()->bind_select((OcHandler*)NULL);
	show_shape_val(true);
}

void ShapePlotImpl::show_shape_val(bool show) {
	if (show != showing_) {
		PolyGlyph* pg = sp_->shape_section_list();
		GlyphIndex i, cnt = pg->count();
		if (show) {
			for (i=0; i < cnt; ++i) {
				ShapeSection* ss = (ShapeSection*)pg->component(i);
				ss->set_range_variable(sym_);
			}
		}else{
			for (i=0; i < cnt; ++i) {
				ShapeSection* ss = (ShapeSection*)pg->component(i);
				ss->clear_variable();
			}
		}
		if (colorbar_) {
			int i = sp_->glyph_index(colorbar_);
			sp_->show(i, show);
			if (show) {
				XYView* v = XYView::current_pick_view();
				sp_->move(i, v->left(), v->top());
			}
		}
		sp_->damage_all();
		showing_ = show;
	}
}
	
MakeTimePlot::MakeTimePlot(ShapePlotImpl* spi){
	spi_ = spi;
}
MakeTimePlot::~MakeTimePlot(){
}

bool MakeTimePlot::event(Event&) {
		Oc oc;
		ShapeSection* ss = shape_section();
		Section* sec = ss->section();
		if (spi_->sp_->tool() != ShapePlot::TIME) {
			return false;
		}
		if (spi_->graphid_ == 0) {
			oc.run("newPlotV()\n");
			oc.run("hoc_ac_ = object_id(graphItem)\n");
			spi_->graphid_ = hoc_ac_;
		}
		oc.run("hoc_ac_ = object_id(graphItem)\n");
//printf("graphid_=%g hoc_ac_=%g\n", graphid_, hoc_ac_);
		float x =  spi_->sp_->arc_selected();
		x = nrn_arc_position(sec, node_exact(sec, x));
		x = (nrn_section_orientation(sec) == 0.) ? x : 1. - x;
		if (spi_->graphid_ == hoc_ac_) {
			char buf[200];
			sprintf(buf, "{graphItem.color(%d)}\n", spi_->colorid_);
			oc.run(buf);
			sprintf(buf, "{graphItem.addvar(\"%s.%s(%g)\")}\n",
			  hoc_section_pathname(sec),
			  spi_->sp_->varname(),
			  x
			);
			oc.run(buf);
			ss->setColor(colors->color(spi_->colorid_),
				ShapeScene::current_pick_scene());
			++spi_->colorid_;
		}else{
			spi_->graphid_ = 0;
		}
	return true;
}

MakeSpacePlot::MakeSpacePlot(ShapePlotImpl* spi){
	spi_ = spi;
}
MakeSpacePlot::~MakeSpacePlot(){
}

void MakeSpacePlot::execute(Rubberband* rb) {
	ShapePlot* sp = spi_->sp_;
	RubberLine* rl = (RubberLine*)rb;
	Coord x1, y1, x2, y2;
	float a1, a2;
	rl->get_line(x1, y1, x2, y2);
//printf("MakeSpacePlot %g %g %g %g\n", x1, y1, x2, y2);
	char buf[256];
	Oc oc;
	oc.run("objectvar rvp_\n");
	Section* sec1, *sec2;
	sp->nearest(x1, y1);
	sec1 = sp->selected()->section();
	a1 = sp->arc_selected();
	a1 = (a1 < .5) ? 0. : 1.;
	sp->nearest(x2, y2);
	sec2 = sp->selected()->section();
	a2 = sp->arc_selected();
	a2 = (a2 < .5) ? 0. : 1.;
	if (sec1 == sec2 && a1 == a2) {
		printf("Null path for space plot: ignored\n");
		return;
	}
	oc.run("hoc_ac_ = object_id(graphItem)\n");
//printf("graphid_=%g hoc_ac_=%g\n", graphid_, hoc_ac_);
	if (spi_->graphid_ == 0. || spi_->graphid_ != hoc_ac_) {
		oc.run("graphItem = new Graph()\n");
		oc.run("hoc_ac_ = object_id(graphItem)\n");
		spi_->graphid_ = hoc_ac_;
		oc.run("{graphItem.save_name(\"flush_list.\")}\n");
		oc.run("{flush_list.append(graphItem)}\n");
		spi_->colorid_ = 1;
	}
	++spi_->colorid_;
	ColorValue* cv = sp->color_value();
	sprintf(buf, "rvp_ = new RangeVarPlot(\"%s\")\n", sp->varname());oc.run(buf);
	sprintf(buf, "%s rvp_.begin(%g)\n", hoc_section_pathname(sec1), a1);oc.run(buf);
	sprintf(buf, "%s rvp_.end(%g)\n", hoc_section_pathname(sec2), a2);oc.run(buf);
	oc.run("{rvp_.origin(rvp_.d2root)}\n");
	sprintf(buf, "{graphItem.size(rvp_.left(), rvp_.right(), %g, %g)}\n", cv->low(), cv->high()); oc.run(buf);
	sprintf(buf, "{graphItem.addobject(rvp_, %d, 1) graphItem.yaxis()}\n", spi_->colorid_); oc.run(buf);
	sp->color(sec1, sec2, colors->color(spi_->colorid_));
}


static const Color* gray;
static int csize;
static const Color** crange;

static int spec[] = {    95, 0, 95, 
			     111, 0, 111,
			     127, 0, 143,
			     143, 0, 127,
			     159, 0, 111,
			     175,  0, 95,
			     191,  0, 79,
			     207,  0, 63,
			     207, 31, 47,
			     223, 47, 47,
			     239, 63, 31,
			     255, 79, 15,
			     255, 95, 7,
			     255, 111, 0,
			     255, 127, 0,
			     255, 143, 0,
			     255, 159, 0,
			     255, 175, 0,
			     255, 191, 0,
			     255, 207, 0,
			     255, 223, 0, 
			     255, 239, 0,
			     255, 247, 0,
			     255, 255, 0,
			     -1};


ColorValue::ColorValue(){
	if (!gray) {
		Style* s = Session::instance()->style();
		CopyString name;
		csize = 0;
		if (s->find_attribute("shape_scale_file", name)) {
			name = expand_env_var(name.string());
//printf("ColorValue %s\n", name.string());
			FILE* f;
			if ((f = fopen(name.string(), "r")) == 0) {
#ifdef WIN32
#else
			printf("Cannot open %s: Using built-in colormap for shapeplot\n", name.string());
#endif
			}else{
				int r, g, b;
				while(fscanf(f, "%d %d %d", &r, &g, &b) == 3) {
					++csize;
				}
				if (csize) {
					crange = new const Color*[csize];
					rewind(f);
					csize = 0;
					while(fscanf(f, "%d %d %d", &r, &g, &b) == 3) {
						crange[csize] = new Color(
						  ColorIntensity(r/256.),
						  ColorIntensity(g/256.),
						  ColorIntensity(b/256.)
						);
						Resource::ref(crange[csize]);
						++csize;
					}
				}
				fclose(f);
			}
		}
		if (csize == 0) {
			for ( csize=0; spec[csize*3] != -1; csize++) { }
			crange = new const Color*[csize];
			for ( csize=0; spec[csize*3] != -1; csize++) {
				crange[csize] = new Color(
				  ColorIntensity(spec[csize*3]/256.),
				  ColorIntensity(spec[csize*3+1]/256.),
				  ColorIntensity(spec[csize*3+2]/256.)
				);
				Resource::ref(crange[csize]);
			}
		}
		gray = Color::lookup(Session::instance()->default_display(), "gray");
		Resource::ref(gray);
	}
	csize_ = 0;
	crange_ = NULL;
	set_scale(0, 1);
}

ColorValue::~ColorValue(){
	int i;
	if (csize_) { // delete the local
		for (i=0; i < csize_; ++i) {
			crange_[i]->unref();
		}	
		delete [] crange_;
	}
}

void ColorValue::set_scale(float low, float high) {
	if (low < high) {
		low_ = low;
		high_ = high;
	}
	notify();
}
const Color* ColorValue::get_color(float val) const {
	float x = (val - low_)/(high_ - low_);
    if (csize_) {
	if (x > .99)return crange_[csize_-1];
	else if (x < 0) return crange_[0];
	else return crange_[int(csize_ * x)];
    }else{
	if (x > .99)return crange[csize-1];
	else if (x < 0) return crange[0];
	else return crange[int(csize * x)];
    }
}
	
const Color* ColorValue::no_value() const {
	return gray;
}

class ColorValueGlyphItem : public MonoGlyph {
public:
	ColorValueGlyphItem(const char*, const Color*);
	virtual ~ColorValueGlyphItem();
	virtual void draw(Canvas*, const Allocation&)const;
private:
	CopyString label_;
	const Color* color_;
};
	
ColorValueGlyphItem::ColorValueGlyphItem(const char* buf, const Color* c) {
	body(new Background(WidgetKit::instance()->label(buf), c));
	label_ = buf;
	color_ = c;
}

ColorValueGlyphItem::~ColorValueGlyphItem() {}

void ColorValueGlyphItem::draw(Canvas* c, const Allocation& a)const {
	body()->draw(c, a);
	if (OcIdraw::idraw_stream) {
		OcIdraw::pict();
		OcIdraw::rect(c, a.left(), a.bottom(), a.right(), a.top(), color_, NULL, true);
		Transformer t;
		t.translate(a.left(), a.bottom());
		OcIdraw::text(c, label_.string(), t);
		OcIdraw::end();	
	}
}

Glyph* ColorValue::make_glyph() {
	LayoutKit& lk = *LayoutKit::instance();
	WidgetKit& wk = *WidgetKit::instance();
	PolyGlyph* box = lk.vbox(csize+2);
	int c = csize_ ? csize_ : csize;
	for (int i = c-1;i >= 0; --i) {
		char buf[50];
		float x = low_ + i*(high_ - low_)/(c-1);
		sprintf(buf, "%5g", x);
		box->append(new ColorValueGlyphItem(buf, get_color(x)));
//		box->append(new Background(wk.label(buf), get_color(x)));
	}
	return box;
}

void ColorValue::colormap(int size, bool global) {
	int i;
	if (csize_) {
		for (i=0; i < csize_; ++i) {
			crange_[i]->unref();
		}
		delete [] crange_;
		crange_ = NULL;
		csize_ = 0;
	}
    if (global) {
	if (csize) {
		for (i=0; i < csize; ++i) {
			crange[i]->unref();
		}
		delete [] crange;
	}
	csize = (size > 1) ? size : 2;
	crange = new const Color*[csize];
	for (i=0; i < csize; ++i) {
		crange[i] = gray;
		crange[i]->ref();
	}
    }else{
	csize_ = (size > 1) ? size : 2;
	crange_ = new const Color*[csize_];
	for (i=0; i < csize_; ++i) {
		crange_[i] = gray;
		crange_[i]->ref();
	}
    }
}
void ColorValue::colormap(int i, int r, int g, int b) {
    if (crange_) {
	if (i >= 0 && i < csize_ && r < 256 && g < 256 && b < 256) {
		crange_[i]->unref();
		crange_[i] = new Color(
			ColorIntensity(r/256.),
			ColorIntensity(g/256.),
			ColorIntensity(b/256.)
		);
		Resource::ref(crange_[i]);
	}
    }else{
	if (i >= 0 && i < csize && r < 256 && g < 256 && b < 256) {
		crange[i]->unref();
		crange[i] = new Color(
			ColorIntensity(r/256.),
			ColorIntensity(g/256.),
			ColorIntensity(b/256.)
		);
		Resource::ref(crange[i]);
	}
    }
}

FastGraphItem::FastGraphItem(FastShape* g, bool s, bool p) : GraphItem(g, s, p) {}

FastShape::FastShape() {
}
FastShape::~FastShape() {
}

Hinton::Hinton(double* pd, Coord xsize, Coord ysize, ShapeScene* ss) {
	pd_ = pd;
	old_ = NULL; // not referenced
	xsize_ = xsize/2;
	ysize_ = ysize/2;
	ss_ = ss;
	Oc oc;
	oc.notify_when_freed(pd_, this);
}
Hinton::~Hinton() {
	Oc oc;
	oc.notify_pointer_disconnect(this);
}
void Hinton::update(Observable*) {
	pd_ = NULL;
	ss_->remove(ss_->glyph_index(this));
}
void Hinton::request(Requisition& req) const {
	assert(this);
	Requirement rx(2.*xsize_, 0, 0, .5);
	Requirement ry(2.*ysize_, 0, 0, .5);
	req.require_x(rx);
	req.require_y(ry);
}

void Hinton::allocate(Canvas* c, const Allocation& a, Extension& ext) {
	ext.set(c, a);
}

void Hinton::draw(Canvas* c, const Allocation& a) const {
	if (pd_) {
		Coord x = a.x();
		Coord y = a.y();
		const Color* color = ss_->color_value()->get_color(*pd_);
		c->fill_rect(x - xsize_, y - ysize_, x + xsize_, y + ysize_, color);
		((Hinton*)this)->old_ = color;
		IfIdraw(rect(c, x - xsize_, y - ysize_, x + xsize_, y + ysize_, color, NULL, true));
	}
}
void Hinton::fast_draw(Canvas* c, Coord x, Coord y, bool) const {
	if (pd_) {
		const Color* color = ss_->color_value()->get_color(*pd_);
		if (color != old_) {
			c->fill_rect(x - xsize_, y - ysize_, x + xsize_, y + ysize_, color);
			((Hinton*)this)->old_ = color;
		}
	}
}

#endif //HAVE_IV
