
#include "iv_plot.h"

#include <stream.h>
#include <InterViews/brush.h>
#include <InterViews/canvas.h>
#include <InterViews/window.h>
#include <InterViews/color.h>
#include <InterViews/event.h>
#include <InterViews/hit.h>
#include <InterViews/geometry.h>
#include <InterViews/xymarker.h>
#include <OS/math.h>
#include <math.h>
#include "iv_graph.h"
#include "iv_axis.h"
#include "cgidbg.h"
#include "plotdatg.h"
#include "dsp_app.h"

static const float min_size = 10.0;
const int Plot::out_limit = 4 ;
const int Plot::diff_limit = 3 ;

// void TestAlloc(const char * Msg=0);

double Plot::sample_width() const
{
	return graph()->sample_width();
}

void Plot::add_points(int chan, int elt, RelativePosition * pos)
{
	if (sample_width() == 0.) new_sample_width();
	// LogOut << "Plot::add_points(" << chan << ", " << elt << ")\n" ;
	if (chan >= channels) DbgError("Plot::add_points","bad channel");
	the_channels[chan].new_points(elt,pos);
	graph()->do_damage();
	// graph()->redraw();
	// graph()->check_axis_redraw();
/*
 *	if (graph()->is_eye_plot()) the_channels[chan].draw_eye(elt,pos);
 *	else the_channels[chan].draw(elt,pos);
 *	graph()->check_axis_redraw();
 *	// LogOut << "Plot::add_points exit\n" ;
 */
}

void Plot::do_damage()
{
	if (!last_canvas) return ;
	// Extension temp ;
    // temp.set(last_canvas,*last_allocation);
    // last_canvas->damage(temp);
	last_canvas->damage(last_allocation->left(),last_allocation->bottom(),
		last_allocation->right(),last_allocation->top());
/*
 *	LogOut << "Plot::do_damage, l = " << last_allocation->left() << 
 *		", b = " << last_allocation->bottom() << ", r = " << 
 *		last_allocation->right() << ", t = " << last_allocation->top() << "\n";
 */
}

void Plot::reset()
{
	for (int i = 0 ; i < channels; i++) the_channels[i].reset();
}

int Plot::new_sample_width()
{
	double new_width = graph()->get_data_plot()->GetNumberSamplesInPlot();
	// LogOut << "Plot::new_sample_width, new_width = " <<new_width << "\n" ;
	if (new_width == sample_width()) return  0;
	graph()->set_sample_width(new_width);
	for (int i = 0 ; i < channels; i++)
		the_channels[i].new_plot_size(new_width);
	return 1 ;
}

static const Brush * line_to_bottom_brush = 0 ;
static const Brush * axis_brush = 0 ;
static const Brush * border_brush = 0 ;
static const Color * axis_color = 0 ;
static const Color * border_color = 0 ;


static const Brush * channel_brush(int)
{
	static const Brush * one_brush = 0 ;
	if (!one_brush) {
		line_to_bottom_brush = new Brush(0x3333,0.);
		Resource::ref(line_to_bottom_brush);
		one_brush = new Brush(0);
		Resource::ref(one_brush);
		border_brush = one_brush ;
		axis_brush = new Brush(0x3333,0.);
		Resource::ref(axis_brush);
	}
	return one_brush ;
}


static const Color * channel_color( Display * display, int i)
{
	// LogOut << "chanel_color(" << (void *) display << ", " << i << ")\n" ;
	static const NumberColors = 4 ;
	static const Color ** colors = 0 ;
	static const char * color_names[NumberColors] = {
		"black", "RosyBrown", "LightBlue", "grey"};
	if (!colors) {
		colors = new Color * [NumberColors];
		for (int i = 0 ; i < NumberColors; i++) {
			colors[i] = Color::lookup(display,color_names[i]);
			if (!colors[i]) DbgError("Color",color_names[i]);
			// LogOut << "colors[" <<i << "] = " << (void *) colors[i] << "\n" ;
			Resource::ref(colors[i]);
		}
		axis_color = Color::lookup(display,"SkyBlue");
		Resource::ref(axis_color);
		border_color = colors[0] ;
	}
	return colors[i % NumberColors] ;
}

Plot::Plot (Graph * gr):
		channels(gr->get_data_plot()->GetNumberOfChannels()),
		send_complete(0),
		the_graph(gr),
		last_canvas(0),
		last_allocation(0),
		x_sum(0),
		y_sum(0),
		hit_count(0),
		last_height(0),
		last_width(0)
{
	the_channels = new PlotChan[channels];
	int k = 0 ;
	for (int i = 0 ; i < channels; i++) {
		int elts = graph()->get_data_plot_channel(i)->GetNumberElements();
		the_channels[i].init(i,this,elts);
	}
}

Plot::~Plot () {
    // _color[i]->unref();
    // _brush[i]->unref();
	for (int i = 0 ; i < channels ;i++) the_channels[i].clear();
	delete the_channels ;
	delete last_allocation ;
}

void Plot::request (Requisition& r) const {
    Glyph::request(r);
    Requirement& rx = r.x_requirement();
    Requirement& ry = r.y_requirement();
    rx.natural(min_size);
    rx.stretch(fil);
    rx.shrink(min_size);
    rx.alignment(0.0);
    ry.natural(min_size);
    ry.stretch(fil);
    ry.shrink(min_size);
    ry.alignment(1.0);
/*
 *    Requirement x(10.0, fil, fil, 0.0);
 *    r.require(Dimension_X, x);
 *  Requirement y(10.0, fil, fil, 0.0);
 *  r.require(Dimension_Y, y);
 */
}

static const Coord max_tick_length = 6. ;
void Plot::allocate (Canvas* c, const Allocation& a, Extension& ext)
{
	if (!last_allocation) last_allocation = new Allocation ;
	*last_allocation = a ;
	Coord margin = 2.0;
	Coord offset = max_tick_length + margin + 6. ;
	Coord width_dec  = 2.*offset ;

	Allotment x = a.x_allotment();
	x.origin(x.origin()+offset) ;
	x.span(x.span()-width_dec);
	last_allocation->allot_x(x);

	Allotment y = a.y_allotment();
	y.origin(y.origin()-offset) ;
	y.span(y.span()-width_dec);
	last_allocation->allot_y(y);

	last_canvas = c ;
    ext.merge(c, a);
}

void PlotChan::reset()
{
	for (int i = 0 ; i < elements; i++) plot_chan_elts[i].reset();
}

void PlotChan::new_plot()
{
	for (int i = 0 ; i < elements; i++) plot_chan_elts[i].new_plot();
}

void PlotChanElt::new_plot()
{
	// LogOut << "PlotChanElt::reset\n" ;

	count = 0 ;
}

void PlotChanElt::reset()
{
	new_plot();
}

void PlotChan::new_plot_size(double size)
{
	for (int i = 0 ; i < elements; i++) plot_chan_elts[i].new_plot_size(size);
}

void PlotChanElt::new_plot_size(double size)
{
	// LogOut << "PlotChanElt::new_plot_size(" << size << ")\n" ;
	int32 Size = (int32)(size+.99999999999999999999) ;
	new_plot();
	limit = Size ;
	if (limit >= max_size) {
		position = new RelativePosition[Size];
		max_size = Size ;
/*
 *		LogOut << "PlotChan::new_plot_size(" << size << "), max_size = " <<
 *			max_size << "\n" ;
 */
	}
}

void PlotChan::init(int chan, Plot * pl, int elt )
{
	plot = pl;
	channel = chan ;
	elements = elt ;
	plot_chan_elts = new PlotChanElt[elt];
	for (int i = 0 ; i < elements; i++) plot_chan_elts[i].init(i,this);
}

void PlotChanElt::init(int elt, PlotChan *plc)
{
	element = elt;
	plot_chan = plc ;
	max_size=limit = count = 0 ;
	position = 0 ;
	color = 0 ;
	brush= 0;
}

void PlotChan::clear()
{
    for (int i = 0 ; i < elements; i++) plot_chan_elts[i].clear();
	delete plot_chan_elts ;
}

void PlotChanElt::clear()
{
	delete position ;
}

int32 PlotChan::new_points(int elt,RelativePosition *pos)
{
	// LogOut << "PlotChan::new_points(" << (void *) pos << ")\n" ;
	// LogOut << "channel " << channel << ",  element = " << elt << "\n" ;
	return plot_chan_elts[elt].new_points(pos);
}

int32 PlotChanElt::new_points(RelativePosition *pos)
{
	if (!limit) return 0 ;
	// LogOut << "PlotChanElt::new_points, count = " << count << "\n" ;
	int total = 0 ;
    for (RelativePosition *test_pos=pos;test_pos->x >-1;test_pos++) total++;
	// LogOut << "Adding  " << total << " samples\n" ;
	if (!pos) return 0 ;
	if (!color) {
		color=channel_color(DspApplication::display(), element);
		brush=channel_brush(plot_chan->channel);
		// LogOut<< "color for " << element << " = " << (void *) color << "\n" ;
	}
	int32 save = count ;
    for (RelativePosition * NextPos = position+count ;
        pos->x > -1; NextPos++, count++, pos++) {
		if (count >= limit) {
			// LogOut << "count = " << count << ", limit = " << limit << "\n";
			DbgError("PlotChan::new_points","too many");
		}
		*NextPos = *pos ;
/*
 *		LogOut << "position[" << count <<
 *			"] = (" << NextPos->x << ", " << NextPos->y << ")\n" ;
 */
	}
	// LogOut << "after count = " << count << ", save = " << save << "\n" ;
	if (save > 1) return save - 1 ;
	return 0 ;
}


void PlotChan::draw_eye(int elt, RelativePosition * pos)
{
	plot_chan_elts[elt].draw_eye(pos);
}

void PlotChanElt::draw_eye(RelativePosition * pos)
{
	draw_eye(new_points(pos));
}


void PlotChan::draw(int elt,RelativePosition * pos)
{
		plot_chan_elts[elt].draw(pos);
}

void PlotChanElt::draw(RelativePosition * pos)
{
	// LogOut << "PlotChanElt::draw(RelativePosition)\n" ;
	draw(new_points(pos));
}


void PlotChan::draw_eye(int elt,int start)
{
		plot_chan_elts[elt].draw_eye(start);
}

Plot * PlotChanElt::plot() const
{
		return plot_chan->plot ;
}

void PlotChanElt::draw_eye(int start)
{
/*
 *	LogOut << "PlotChanElt::draw_eye(" << start << "), conut = " << count <<
 *		"\n" ;
 */
	Canvas * c = plot()->canvas();
	if (!c) return ;
	const Allocation& a = *(plot()->allocation());
	Coord l = a.left();   Coord r = a.right();
	Coord b = a.bottom(); Coord t = a.top();
/*
 *	LogOut << "l = " << l << ", r = " << r << ", t = " << t << ", b = " << b
 *		<< "\n" ;
 *	LogOut << "count = " << count << "\n" ;
 */
	RelativePosition  * base_pos = position ;
	int is_x_scale = x_scaling();
	int is_y_scale = y_scaling();
	for (int i = start ; i < count ; i++) {
		RelativePosition& NextPos = base_pos[i];
		float x_pos = NextPos.x ;
		float y_pos = NextPos.y ;
		if (is_x_scale) x_pos = x_scale(x_pos);
		if (is_y_scale) y_pos = y_scale(y_pos);
		// LogOut << "x_pos = " << x_pos << ", y_pos = " << y_pos << "\n" ;
		Coord x = l * (1 - x_pos) + r * x_pos;
		Coord y = b * (1 - y_pos) + t * y_pos;
		// LogOut << "x = " << x << ", y = " << y << "\n" ;
		c->move_to(x-1, y-1);
        c->line_to(x-1, y+1);
        c->line_to(x+1, y+1);
        c->line_to(x+1, y-1);
        c->line_to(x-1, y-1);
        c->stroke(color, brush);
	}
}

void PlotChan::draw(int elt,int start)
{
	plot_chan_elts[elt].draw(start);
}

RelativePosition& PlotChan::get_sample(int elt, int sample)
{
	return plot_chan_elts[elt].get_sample(sample);
}

RelativePosition& PlotChanElt::get_sample(int sample)
{
	return position[sample];
}

void PlotChanElt::draw(int start)
{
	// LogOut << "PlotChanElt::draw(" << start << "), count = " << count << "\n" ;
	// TestAlloc("draw enter");
	if (start >= count-1) return ;
	Canvas * c = plot()->canvas();
	if (!c) return ;
	const Allocation& a = *(plot()->allocation());
	// LogOut << "Canvas = " << (void *) c << "\n" ;
	// LogOut << "Allocation = " << (void *) &a << "\n" ;
	Coord l = a.left();   Coord r = a.right();
	Coord b = a.bottom(); Coord t = a.top();
/*
 *	LogOut << "l = " << l << ", r = " << r << ", t = " << t << ", b = " << b
 *		<< "\n" ;
 */
	Coord diff = position[start+1].x - position[start].x ;
	int line_to_bottom = ((r-l)*diff) > 8 ;
/*
 *	LogOut << "diff = " << diff << ", line_to_bottom = " << line_to_bottom
 *		<< "\n" ;
 */
	int is_x_scale = x_scaling();
	int is_y_scale = y_scaling();
	for (int i = start ; i < count ; i++) {
		RelativePosition& NextPos = position[i];
		float x_pos = NextPos.x ;
		float y_pos = NextPos.y ;
		if (is_x_scale) x_pos = x_scale(x_pos);
		if (is_y_scale) y_pos = y_scale(y_pos);
		Coord x = l * (1 - x_pos) + r * x_pos;
		Coord y = b * (1 - y_pos) + t * y_pos;
/*
 *		LogOut << i << ":Coord = (" << x << ", " << y  << "), (" << x_pos <<
 *			", " << y_pos << ")\n" ;
 */
		if (i==start) c->move_to(x,y) ;
        else c->line_to(x,y);
		if (line_to_bottom) {
			Coord draw_to = b ;
			int do_draw = 1 ;
			for (int elt = 0; elt < element;elt++) {
				RelativePosition& real_pos = plot_chan->get_sample(elt,i);
				if (real_pos.x != NextPos.x) continue ;
				float y_real_pos = real_pos.y ;
				if (is_y_scale) y_real_pos = y_scale(y_real_pos);
				Coord y_real = b * (1 - y_real_pos) + t * y_real_pos;
				if (y_real >= y) {
					do_draw = 0 ;
					break ;
				}
				if (y_real > draw_to) draw_to = y_real ;
			}
			if (do_draw) {
    			c->stroke(color, brush);
				c->line_to(x,draw_to);
    			c->stroke(color, line_to_bottom_brush);
				c->move_to(x,y);
			}
		}
	}
    c->stroke(color, brush);
	// TestAlloc("draw exit");
}

void Plot::draw()
{
	if (sample_width() == 0.) new_sample_width();
	// LogOut << "Plot::draw()\n" ;
	// LogOut << "is_mapped = " << graph()->is_mapped() << "\n" ;
	if (!graph()->is_mapped()) return ;
	int eye = graph()->is_eye_plot() ;
	// LogOut << "eye = " << eye << "\n" ;
	for (int chan = 0 ; chan < channels ; chan++)
		for (int elt = 0 ; elt < the_channels[chan].elements; elt++)
			if (eye) the_channels[chan].draw_eye(elt,0);
			else the_channels[chan].draw(elt,0);
}

void Plot::draw_axis(Canvas *c, const Allocation& ) const
{
		if (!axis_color) return ;
		// graph()->check_axis_redraw();
		// draw border
		Coord left = last_allocation->left()-1;
		Coord right = last_allocation->right()+1;
		Coord bottom = last_allocation->bottom()-1;
		Coord top = last_allocation->top()+1 ;
		c->rect(left,bottom,right,top,border_color,border_brush);
		Coord base,inc ;
		int num, i, j ;

		const Coord tick_length = max_tick_length * .4  ;
		num = graph()->y_axis().tick_positioning(base,inc);
		Coord tick_skip = inc * .2 ;
		// draw X ticks
		// LogOut << "tick_skip = " << tick_skip << "\n" ;
		Coord lim = bottom + 5. ;
		for (j = 1 ; j < 5 ; j++) {
			Coord new_base = base - tick_skip * j ;
			if (new_base < lim) break ;
			c->move_to(right,new_base);
			c->line_to(right+tick_length,new_base);
			c->stroke(border_color,border_brush); 
			c->move_to(left,new_base);
			c->line_to(left-tick_length,new_base);
			c->stroke(border_color,border_brush); 
		}
		lim= top - 5. ;
		for (i = 0; i < num;i++,base+=inc) {
			c->move_to(left,base);
			c->line_to(right,base);
			c->stroke(axis_color,border_brush); 
			for (j = 0 ; j < 5 ; j++) {
				Coord use_tick_length = tick_length ;
				if (!j) use_tick_length *= 2 ;
				Coord new_base = base + tick_skip * j ;
				if (new_base > lim) break ;
				c->move_to(right,new_base);
				c->line_to(right+use_tick_length,new_base);
				c->stroke(border_color,border_brush); 
				c->move_to(left,new_base);
				c->line_to(left-use_tick_length,new_base);
				c->stroke(border_color,border_brush); 
			}
		}
		// draw Y ticks
		num = graph()->x_axis().tick_positioning(base,inc);
		tick_skip = inc * .2 ;
		// LogOut << "tick_skip = " << tick_skip << "\n" ;
		lim = left + 5. ;
		for (j = 1 ; j < 5 ; j++) {
			Coord new_base = base - tick_skip * j ;
			if (new_base < lim) break ;
			c->move_to(new_base,top);
			c->line_to(new_base,top+tick_length);
			c->stroke(border_color,border_brush); 
			c->move_to(new_base,bottom);
			c->line_to(new_base,bottom-tick_length);
			c->stroke(border_color,border_brush); 
		}
		lim = right - 5. ;
		for (i = 0; i < num;i++,base+=inc) {
			c->move_to(base,top);
			c->line_to(base,bottom);
			c->stroke(axis_color,border_brush); 
			for (j = 0 ; j < 5 ; j++) {
				Coord use_tick_length = tick_length ;
				if (!j) use_tick_length *= 2 ;
				Coord new_base = base + tick_skip * j ;
				// LogOut << "j = " << j << ", new_base = " << new_base << "\n" ;
				if (new_base > lim) break ;
				c->move_to(new_base,top);
				c->line_to(new_base,top+use_tick_length);
				c->stroke(border_color,border_brush); 
				c->move_to(new_base,bottom);
				c->line_to(new_base,bottom-use_tick_length);
				c->stroke(border_color,border_brush); 
			}
				
		}
}

void Plot::draw(Canvas* c, const Allocation& a) const
{
	// LogOut << "Plot::draw(Canvas*,Allocation&)\n" ;
	if (c != last_canvas) DbgError("Plot::draw","bad last_canvas");

	int changed = 0 ;
	Coord temp ;
	if ((temp = a.top() - a.bottom()) != last_height) {
		changed = 1;
		((Plot *) this)->last_height = temp;
	}
	if ((temp = a.right() - a.left()) != last_width) {
		changed = 1;
		((Plot *) this)->last_width = temp;
	}
	if (changed) graph()->mark(-1);



	// LogOut << "Plot::draw, channels = " << channels << "\n" ;
	// LogOut << "Canvas = " << (void *) c << "\n" ;
	// LogOut << "Allocation = " << (void *) &a << "\n" ;
	// LogOut << "calling check_init\n" ;
	// LogOut << "after check_init\n" ;
	if (send_complete) {
		graph()->complete();
		// LogOut << "did send_complete\n" ;
	}
	graph()->get_data_plot()->check_init() ;
	graph()->x_axis().draw(c,a);
	draw_axis(c,a);
	((Plot*) this)->draw();
	// LogOut << "Plot::draw exit\n" ;
}


float PlotChanElt::x_scale(float x) const
{
	return plot_chan->x_scale(x);
}

float PlotChanElt::y_scale(float y) const
{
	return plot_chan->y_scale(y);
}

float PlotChan::x_scale(float x) const
{
	return plot->graph()->x_scale(x);
}

float PlotChan::y_scale(float x) const
{
	return plot->graph()->y_scale(x);
}

int PlotChanElt::y_scaling() const
{
	return plot_chan->y_scaling();
}

int PlotChanElt::x_scaling() const
{
	return plot_chan->x_scaling();
}

int PlotChan::y_scaling() const
{
	return plot->graph()->y_scaling();
}

int PlotChan::x_scaling() const
{
	return plot->graph()->x_scaling();
}

void PlotChan::pick(Canvas* c, const Allocation& a, int depth , Hit& h)
{
	for (int elt = 0 ; elt < elements; elt++)
		plot_chan_elts[elt].pick(c,a,depth,h);
}

void PlotChanElt::pick(Canvas*, const Allocation&, int depth , Hit& h)
{
    const Event* e = h.event();
    Coord x = e->pointer_x();
    Coord y = e->pointer_y();
	// LogOut << "PlotChanElt::pick, x = " << x << ", y = " << y << "\n" ;
	int is_x_scale = x_scaling();
    int is_y_scale = y_scaling();

	Graph * gr = graph();
	
   	for (int i = 0; i < count; ++i) {
		float x_pos = position[i].x ;
		float y_pos = position[i].y ;
		if (is_x_scale) x_pos = x_scale(x_pos);
        if (is_y_scale) y_pos = y_scale(y_pos);
		
       	Coord dx = fabs(gr->x_coord(x_pos) - x) ;
       	Coord dy = fabs(gr->y_coord(y_pos) - y) ;
		// LogOut << "c_x = " << gr->x_coord(x_pos) << ", dx = " << dx <<  "\n" ;
		// LogOut << "c_y = " << gr->y_coord(y_pos) << ", dy = " << dy << "\n" ;
       	if (dx < plot()->diff_limit && dy < plot()->diff_limit) {
			// LogOut << "marking " << i << "\n" ;
			if (plot()->hit_count < plot()->out_limit) {
				RelativePosition p(x_pos,y_pos);
				gr->list_pos(p);
			}
			plot()->x_sum += x_pos ; 
			plot()->y_sum += y_pos ; 
			plot()->hit_count++ ;
       	}
	}
}

void Plot::mark(RelativePosition& pos)
{
	graph()->mark(pos);
}

void Plot::pick(Canvas* c, const Allocation& a, int depth, Hit& h)
{
	x_sum = 0 ;
    y_sum = 0 ;
    hit_count = 0 ;
    int w = 2 * diff_limit + 1 ;

	for (int chan = 0 ; chan < channels ; chan++)
		the_channels[chan].pick(c,a,depth,h);

	if (!hit_count) {
		graph()->mark(-1);
		return ;
	}
	RelativePosition temp((float)(x_sum/hit_count),(float)(y_sum/hit_count));
	graph()->mark(temp);
	if (hit_count > out_limit) *Output + OutputCppEntry << "There were "
		<< hit_count - out_limit << " additional hits in a " << w << " x "
		<< w << " pixel window.\n" ;
	if (hit_count > 1) {
		*Output + OutputCppEntry << "Average of " << hit_count << " hits is: " ;
		graph()->list_pos(temp);
	}
}
