
#include "iv_axis.h"
#include "iv_graph.h"
#include "iv_plot.h"
#include "genmenu.h"
#include "cgidbg.h"
#include <stdlib.h>
#include <limits.h>


#include <OS/enter-scope.h>
#include <InterViews/enter-scope.h>
#include <InterViews/background.h>
#include <InterViews/brush.h>
#include <InterViews/canvas.h>
#include <InterViews/color.h>
#include <InterViews/hit.h>
#include <InterViews/event.h>
#include <InterViews/geometry.h>
#include <InterViews/patch.h>
#include <InterViews/session.h>
#include <InterViews/style.h>
#include <InterViews/target.h>
#include <InterViews/xymarker.h>
#include <InterViews/layout.h>
#include <InterViews/window.h>
#include <InterViews/label.h>
#include <InterViews/glyph.h>
#include <InterViews/monoglyph.h>
#include <InterViews/cursor.h>
#include <InterViews/font.h>
#include <IV-look/kit.h>
#include <OS/string.h>
#include <string.h>
#include <strstream.h>
#include <stream.h>
#include <X11/keysym.h>
// #include <X11/keysymdef.h>
#include <X11/cursorfont.h>
#include "plotdatg.h"
#include "dsp_app.h"
#include "axslab.h"
#include <OS/enter-scope.h>
#include <InterViews/enter-scope.h>
#include "mkstr.h"
#include "plotfile.h"

const Color* color (const char* name) {
	String v;
	Session* session = Session::instance();
	Style* style = session->style();
	WidgetKit& kit = *WidgetKit::instance();
	style->find_attribute(name, v);

	ColorIntensity r, g, b;
	if (
		v.string() != nil &&
		Color::find(session->default_display(), v, r, g, b)
	) {
		return new Color(r, g, b);
	} else {
		return kit.foreground();
	}
}

static const int buf_size = 5000;

Graph::Graph (
	float w, float h,const Color* bg, DataPlot * plt, Style *style):
		DspActiveHandler(nil, style),
		data_plot(plt),
		x_min_relative_displayed(0),
		x_diff_relative_displayed(1),
		y_min_relative_displayed(0),
		y_diff_relative_displayed(1),
		x_scale_flag(0),
		y_scale_flag(0),
		resize_key_stroke(0),
		first_pos_x(0),
		first_pos_y(0),
		curr_sample_width(0),
		prev_sample_width(0),
		orig_sample_width(0),
		the_menu_keyboard(0),
		is_inside(0),
		save_key(0),
		_caption_patch(0),
		in_update(0),
		in_adjust_adjuster(0),
		save_cursor(0),
		x_min_this_plot(0),
		x_max_this_plot(0),
		y_min_this_plot(0),
		y_max_this_plot(0),
		_rescale_x(no_data),
		_rescale_y(no_data),
		last_canvas(0),
		last_allocation(0),
		prompt_value_return(0),
		got_value(0)
{
	box.attach(Dimension_X,this);
	_init = false;
	_current = -1;
	_w = w;
	_h = h;
	_x_origin = 0.0 ;
	_y_origin = 0.0 ;
	_x_range = 0.0 - 0.0 ;
	_y_range = 0.0 - 0.0;
	// _x = new Coord[buf_size];
	// _y = new Coord[buf_size];
	_font = WidgetKit::instance()->font();
	Resource::ref(_font);
	_plot = new Plot(this);
	_ppatch = new Patch(_plot);
	_marker = new XYMarker(_ppatch, DspApplication::cyan(), nil);
	_xaxis = new XAxis(this);
	_xpatch = new Patch(_xaxis);
	_yaxis = new YAxis(this);
	_ypatch = new Patch(_yaxis);
	x_labels = new AxisLabelingLinearX(this) ;
	y_labels = new AxisLabelingLinearY(this) ;
	x_ticks = 0 ;
	y_ticks = 0 ;

	layout = LayoutKit::instance();
	kit = WidgetKit::instance();

	_caption_patch = new Patch(nil);
	make_caption();
	const number_plot_menus = 4;	// more detail, less detail, locate, view
	the_menu_keyboard = build_menu(kit,*layout,style,plt);
	background_patch = new Patch(
		layout->vbox(
			layout->hbox(
		   		_ypatch,
		   		new Target(
			   		_marker, TargetPrimitiveHit
		   		)
			),
		  	_xpatch, _caption_patch
			//_xaxis
		)
	);
	body(
		new Background(
			layout->vbox(
				kit->outset_frame(the_menu_keyboard),
				new Background(background_patch, bg),
				kit->inset_frame(
					kit->hscroll_bar(adjustable())
				)
			),bg
		)
	);
}

Graph::~Graph () {
	delete x_labels ;
	delete y_labels ;
	Resource::unref(_font);
}

static const char * transform_name(DataPlot::Transform t)
{
	switch(t) {
case DataPlot::none:
		return 0 ;
case DataPlot::power:
		return "Power" ;
case DataPlot::db:
		return "Decibel" ;
case DataPlot::db_power:
		return "Decibel power" ;
case DataPlot::spectral:
		return "Spectral" ;
case DataPlot::copy :
		return "Copy" ;
default:
		DbgError("transform_name","bad type");
	}
	return 0 ;
}

double Graph::InternalToUserTime(double InternalTime)
{
	double Return = InternalTime ;
	int BaseChan = data_plot->GetBaseChannel();
	// double Result = data_plot->DisplaySampleRateFactor(BaseChan);
	double Result = data_plot->GetSampleRateAdjustment(BaseChan);
	PlotChannel * chan = data_plot->GetChannel(BaseChan);
	// if (Result > 1.e-50) InternalTime = InternalTime
	//  / (data_plot->GetBlockSize() * Result) ;
	if (Result > 1.e-50) InternalTime = InternalTime / Result ;
	return InternalTime ;
}


void Graph::make_caption() const
{
	const char * t_name = transform_name(data_plot->the_transform);
	const char * xposition = 0 ;
	const TimeSize = 32 ;
	char TimeLabel[TimeSize] ;
	if (data_plot->is_two_dimensional()) {
		// LogOut << "Compute two dimensional time begin\n" ;

		int32 BaseSample = data_plot->GetFirstBlockOnCurrentPlot();

		int BaseChannel = data_plot->GetBaseChannel();
		double RateFactor = data_plot->GetSampleRateAdjustment(BaseChannel);
		if (RateFactor < 1.e-50) RateFactor = 1. ;

		double AverageTimeThisPlot = data_plot->IndexToTime(BaseSample) ;

		// LogOut << "Average = " << AverageTimeThisPlot << "\n" ;
		// LogOut << "BaseSample = " << BaseSample << "\n" ;
 
		for (int i = 0; i < TimeSize ; i++) TimeLabel[i] = '\0' ;
		ostrstream Wrt(TimeLabel,TimeSize-1);
		if (!(Wrt << AverageTimeThisPlot).good()) 
			TheLog << "Can't write time label = " <<
			  AverageTimeThisPlot << "\n" ;
		else xposition = TimeLabel ; 
	
		// LogOut << "Compute two dimensional time end\n" ;
	}
	const capt_char = 512 ;
	char caption[capt_char] ;
	caption[0] ='\0' ;
	if (t_name) {
		strcpy(caption,t_name);
		strcat(caption," view of plot ");
	}
	const char *capt = data_plot->GetCaption();
	if (strlen(capt) < capt_char-100) strcat(caption,data_plot->GetCaption());
	if (xposition) {
		strcat(caption," at time ");
		strcat(caption,xposition);
	}
	strcat(caption,".");
	// LogOut << "cation `" << caption << "'\n" ;
	_caption_patch->body( layout->hbox( layout->hglue(10.0),
		kit->fancy_label(caption),
			layout->vbox(layout->hglue(10.0)))
	);
}

void Graph::set_sample_width(double width)
{
	// LogOut << "set_sample_width(" << width << ")\n" ;
	// LogOut << "orig_sample_width = " << orig_sample_width << "\n" ;
	if (orig_sample_width == 0.0) {
		orig_sample_width = width ;
		prev_sample_width = width ;
	} else prev_sample_width = curr_sample_width ;
	// LogOut << "orig_sample_width = " << orig_sample_width << "\n" ;
	curr_sample_width = width ;
}

void Graph::new_sample_width()
{
	_plot->new_sample_width();
}

int Graph::is_eye_plot() const
{
	return get_data_plot()->is_eye_plot();
}

int Graph::check_do_rescale_x()
{
	// LogOut << "check_do_rescale_x, _rescale_x = " << _rescale_x << "\n" ;
	if (_rescale_x == no_data) return 1 ;
	if (_rescale_x < no_rescaling) {
		_rescale_x = no_rescaling ;
		return 1 ;
	}
	return 0 ;
}

int Graph::check_do_rescale_y()
{
	if (_rescale_y == no_data) return 1 ;
	if (_rescale_y < no_rescaling) {
		_rescale_y = no_rescaling ;
		return 1 ;
	}
	return 0 ;
}

void Graph::set_allow_rescale_x()
{
	if (_rescale_x > no_fetch_remote) _rescale_x = no_fetch_remote ;
}

void Graph::set_allow_rescale_y()
{
	if (_rescale_y > no_fetch_remote) _rescale_y = no_fetch_remote ;
}

void Graph::do_damage()
{
	if (!last_canvas) return ;
	if (!allow_rescale_x() && !allow_rescale_y()) return ;
	// _plot->do_damage();
	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 Graph::add_points(int chan, int elt, RelativePosition * pos)
{
	// LogOut << "add_points for " << get_data_plot()->GetCaption() << "\n" ;
	// LogOut << "_rescale_x = " << _rescale_x << "\n" ;
	_plot->add_points(chan,elt, pos);
	if (_rescale_x == no_data) set_x_tick_spacing();
	if (_rescale_x == no_data) _rescale_x = data_sent ;
	if (_rescale_y == no_data) _rescale_y = data_sent ;
	_xaxis->check_change();
	_yaxis->check_change();
}

void Graph::focus_out()
{
	// LogOut << "Graph::focus_out\n" ;
}

InputHandler * Graph::focus_in()
{
	// LogOut << "Graph::focus_in\n" ;
	return InputHandler::focus_in();
}

boolean Graph::is_mapped() const
{
	if (!window()) return false ;
	return window()->is_mapped();
}

void Graph::request (Requisition& r) const {
	MonoGlyph::request(r);
	Requirement& rx = r.x_requirement();
	Requirement& ry = r.y_requirement();
	rx.stretch(fil);
	rx.shrink(0.0);
	rx.alignment(0.0);
	rx.natural(_w);
	ry.stretch(fil);
	ry.shrink(0.0);
	ry.alignment(1.0);
	ry.natural(_h);
}

void Graph::mark(RelativePosition& pos)
{
	// LogOut << "Graph::mark\n" ;
	_current = 0 ;
	Coord x = x_coord(pos.x);
	Coord y = y_coord(pos.y);
	// LogOut << "marking (" << x << ", " << y << ")\n" ;
	_marker->mark(x - Plot::diff_limit, y - Plot::diff_limit,
		x + Plot::diff_limit, y + Plot::diff_limit);
}

void Graph::mark (int i)
{
	if (i < 0) _marker->unmark();
}

void Graph::mark (Coord x, Coord y, long key)
{
	switch(key) {
case XK_Up:
case XK_Down:
		_marker->mark(x-3,y-3,x+3,y+3);
		break ;
case XK_FUNCTION_FLAG | XK_Right:
case XK_FUNCTION_FLAG | XK_Left:
		_marker->mark(x-1,y-30,x+1,y+30);
		break ;
case XK_FUNCTION_FLAG | XK_Down:
case XK_FUNCTION_FLAG | XK_Up:
		_marker->mark(x-30,y-1,x+30,y+1);
		break ;
	}
}

void Graph::allocate (Canvas* c, const Allocation& a, Extension& ext) {
	if (!last_allocation) last_allocation = new Allocation ;
	*last_allocation = a ;
	last_canvas = c ;
	InputHandler::allocate(c, a, ext);
	mark(_current);
	rescale_x();
	rescale_y();
}

void Graph::enter()
{
/*
 *	LogOut << "Graph::enter, key = 0x" << hex  << save_key << ", cursor = 0x"
 *		<< save_cursor << dec << "\n" ;
 */
	window()->push_cursor();
	window()->cursor(new Cursor(XC_crosshair));
	is_inside = 1 ;
	if (save_cursor) resize(save_key,save_cursor);
	save_key = 0 ;
	save_cursor = 0 ;
}

void Graph::leave()
{
/*
 *	LogOut << "Graph::leave, key = 0x" << hex  << save_key << ", cursor = 0x"
 *		<< save_cursor << dec << "\n" ;
 */
	is_inside = 0 ;
	// save_key = 0 ;
	// save_cursor = 0 ;
	end_rescale() ;
	window()->pop_cursor();
}



void Graph::check_clear_prev_x()
{
	if (x_scale_flag) return ;
	x_min_relative_displayed = 0 ;
	x_diff_relative_displayed = 1 ;
}

void Graph::check_clear_prev_y()
{
	if (y_scale_flag) return ;
	y_min_relative_displayed = 0 ;
	y_diff_relative_displayed = 1 ;
}

void Graph::release(const Event& e)
{
	// LogOut << "Graph::release\n" ;
	the_menu_keyboard->release(e);
	if (e.middle_is_down()) {
		end_rescale();
		return ;
	}
	if (!resize_key_stroke) return ;
	Coord last_x = e.pointer_x();
	Coord last_y = e.pointer_y();
	// LogOut << "last_x = " << last_x << "\n" ;
	// LogOut << "last_y = " << last_y << "\n" ;
	if (last_x > first_pos_x) {
		Coord temp = last_x ;
		last_x = first_pos_x ;
		first_pos_x = temp ;
	}
	if (last_y > first_pos_y) {
		Coord temp = last_y ;
		last_y = first_pos_y ;
		first_pos_y = temp ;
	}
	int success = 0 ;
	int success2 = 0 ;
switch (resize_key_stroke) {
case XK_FUNCTION_FLAG | XK_Left:
	success = rescale_x_less_detail(first_pos_x,last_x);
	if (success) check_clear_prev_y();
	break ;
case XK_FUNCTION_FLAG | XK_Right:
	success = rescale_x_more_detail(first_pos_x,last_x);
	if (success) check_clear_prev_y();
	break ;
case XK_Down:
	success = rescale_x_less_detail(first_pos_x,last_x);
	success2 = rescale_y_less_detail(first_pos_y,last_y);
	success = success || success2 ;
	break ;
case XK_Up:
	success = rescale_x_more_detail(first_pos_x,last_x);
	success2 = rescale_y_more_detail(first_pos_y,last_y);
	success = success || success2 ;
	break ;
case XK_FUNCTION_FLAG | XK_Up:
	success = rescale_y_more_detail(first_pos_y,last_y);
	if (success) check_clear_prev_x();
	break ;
case XK_FUNCTION_FLAG | XK_Down:
	success = rescale_y_less_detail(first_pos_y,last_y);
	if (success) check_clear_prev_x();
	break ;
default:
	DbgError("Graph::release","bad resize_key_stroke");
	}
	// LogOut << "success = " << success << "\n" ;
	end_rescale();
	if (success) _ppatch->redraw();
	// else display error message ;
}


void Graph::resize(long key, long cursor)
{
		if (!is_inside) {
			save_key = key ;
			save_cursor = cursor ;
/*
 *			LogOut << "Graph::resize - setting, key = 0x" << hex  <<
 *				save_key << ", cursor = 0x" << save_cursor << dec << "\n" ;
 */
			end_rescale();
			return ;
		}	
		end_rescale();
		resize_key_stroke = key;
		window()->push_cursor();
		window()->cursor(new Cursor((int)cursor));
}

void Graph::draw_new_width(double width)
{
	// LogOut << "draw_new_width(" << width << ")\n" ;
	if (width == curr_sample_width) return ;
	if (width > curr_sample_width) get_data_plot()->
			ChangeXScale(0, curr_sample_width/width,0);
	else get_data_plot()->
			ChangeXScale(0,(int32) (width/curr_sample_width),1) ;
	curr_sample_width = width ;
	rescale_x();
}

void Graph::shift_home()
{
	// LogOut << "Graph::shift_home\n" ;
	end_rescale();
	// LogOut <<  "curr_sample_width= " << curr_sample_width << "\n" ;
	int changed= 0 ;
	if (x_diff_relative_displayed != 1 ||
		x_min_relative_displayed != 0) {
		x_scale_flag = 1 ;
		changed = 1 ;
	}

	if (y_diff_relative_displayed != 1 ||
		y_min_relative_displayed != 0) {
		y_scale_flag = 1 ;
		changed = 1 ;
	}
	// LogOut <<  "prev_sample_width= " << prev_sample_width << "\n" ;
	if (curr_sample_width != prev_sample_width) {
		// draw_new_width(prev_sample_width);
		data_plot->RestorePreviousXScaling();
		data_plot->PlotThisPage();
		check_axis_redraw();
		mark(-1);
	} else if (changed) {
		set_allow_rescale_x();
		set_allow_rescale_y();
		_ppatch->redraw();
		check_axis_redraw();
		mark(-1);
	}
}

void Graph::home()
{
	end_rescale();
	// LogOut <<  "curr_sample_width= " << curr_sample_width << "\n" ;
	int changed = x_scale_flag || y_scale_flag  ;
	x_scale_flag = 0 ;
	y_scale_flag = 0 ;
	// LogOut <<  "orig_sample_width= " << orig_sample_width << "\n" ;
	if (curr_sample_width != orig_sample_width) {
		// draw_new_width(orig_sample_width) ;
		data_plot->RestoreOriginalXScaling();
		data_plot->PlotThisPage();
		mark(-1);
	} else if (changed) {
		set_allow_rescale_x();
		set_allow_rescale_y();
		_ppatch->redraw();
		check_axis_redraw();
		mark(-1);
	}
}

void Graph::keystroke(const Event& e)
{
	int control = e.control_is_down();
	int shift = e.shift_is_down();
	long sym = e.keysym();
	sym &=0xffffL ;
	// LogOut << "keysym = 0x" << hex << (int) sym << dec << "\n" ;
	switch(sym) {
default:
		break ;
case XK_c:
case XK_C:
		if (!control) {
			break ;
		}
case XK_Escape:
		end_rescale();
		save_key=0 ;
		save_cursor=0;
		return ;
	}
	if (!the_menu_keyboard->accept_keystroke(e))
		DspActiveHandler::keystroke(e);
}
/**********************************
case XK_Prior: // key pad page up
		get_data_plot()->PageUp();
		break ;
case XK_Next: // key pad page down
		get_data_plot()->PageDown();
		break ;
case XK_Left:
case XK_FUNCTION_FLAG | XK_Left:
		if (control) resize(XK_Down, XC_top_left_arrow);
		else resize(XK_FUNCTION_FLAG | XK_Left,XC_sb_left_arrow);
		break ;
case XK_Up:
case XK_FUNCTION_FLAG | XK_Up:
		if (control) resize(XK_Up, XC_draft_large);
		else resize(XK_FUNCTION_FLAG | XK_Up,XC_sb_up_arrow);
		break ;
case XK_Right:
case XK_FUNCTION_FLAG | XK_Right:
		if (control) resize(XK_Up, XC_draft_large);
		else resize(XK_FUNCTION_FLAG | XK_Right,XC_sb_right_arrow);
		break ;
case XK_Down:
case XK_FUNCTION_FLAG | XK_Down:
		if (control) resize(XK_Down, XC_top_left_arrow);
		else resize(XK_FUNCTION_FLAG | XK_Down,XC_sb_down_arrow);
		break ;
case XK_End:
		if (shift) get_data_plot()->PageStart();
		else get_data_plot()->PageEnd();
		break ;
case XK_Home:
		if (shift) shift_home() ;
		else home();
		break ;
	}
}
**************************************/

Coord Graph::x_to_coord(double x)
{
	float rel = x_rel(x);
	return x_coord(rel);
}

Coord Graph::y_to_coord(double x)
{
	float rel = y_rel(x);
	return y_coord(rel);
}

double Graph::coord_to_x(Coord x_c)
{
	float rel = x_rel(x_c);
/*
 *	LogOut << "Graph::coord_to_x( " << x_c << "), rel = " << rel <<
 *		", return " <<  x_abs_value(rel) <<"\n" ;
 */
	return x_abs_value(rel);
}

double Graph::coord_to_y(Coord y_c)
{
	float rel = y_rel(y_c);
/*
 *	LogOut << "Graph::coord_to_y( " << y_c << "), rel = " << rel <<
 *		", return " <<  y_abs_value(rel) <<"\n" ;
 */
	return y_abs_value(rel);
}

void Graph::list_pos(RelativePosition& pos)
{
	double x = x_abs_value(pos.x);
	double y = y_abs_value(pos.y);
	const buf_size = 80 ;
	char buf[buf_size];
	memset(buf,'\0',buf_size);
	ostrstream str(buf,buf_size-1);
	str << "( " << x << ", " << y << "): " ;
	const char * c = data_plot->GetCaption();
	strncat(buf,c, buf_size - strlen(buf) - 2);
	*Output + OutputCppEntry << buf << "\n"  ;
}

void Graph::press (const Event& e)
{
	// LogOut << "Graph::press\n" ;
	unsigned int the_button = e.pointer_button();
    int left = the_button == Event::left ;
    int right = the_button == Event::right  ;
    int middle = the_button == Event::middle  ;

	if (left && resize_key_stroke && e.type() == Event::down) {
		first_pos_x = e.pointer_x();
		first_pos_y = e.pointer_y();
		// LogOut << "first_pos_x = " << first_pos_x << "\n" ;
		// LogOut << "first_pos_y = " << first_pos_y << "\n" ;
		mark(first_pos_x,first_pos_y,resize_key_stroke);
		return ;
	}
	end_rescale();

	if (right) {
		Hit h(&e);
		_ppatch->repick(0, h);
		return ;
	}
	if (left) {
		RelativePosition pos(x_rel(e.pointer_x()),y_rel(e.pointer_y())); 
		list_pos(pos);
		return ;
	}
	the_menu_keyboard->press(e);
}

Coord Graph::get_plot_width() const
{
	return _ppatch->allocation().allotment(Dimension_X).span();
}

int32 Graph::get_plot_pixel_width() const
{
	// LogOut << "pix1\n" ;
	Coord width = _ppatch->allocation().allotment(Dimension_X).span();
	// LogOut << "pix2" << width << "\n" ;
	float pix_w = DspApplication::canvas()->to_pixels(width);
/*
 *	LogOut << "Graph::get_plot_pixel_width(), Coord = " << width <<
 *		", pixels = " << pix_w << "\n" ;
 */
	return (int32) pix_w ;
}

void Graph::complete()
{
	_plot->complete();
	data_plot->complete();
}

void Graph::send_complete_after_draw()
{
	_plot->send_complete_after_draw();
}

void Graph::set_x_tick_spacing()
{
	// LogOut << "Graph::set_x_tick_spacing()\n" ;
	_xaxis->check_redraw() ;
	 if (data_plot->is_two_dimensional()) {
		make_caption();
		_caption_patch->reallocate();
		_caption_patch->redraw();
	}
}

void Graph::set_y_tick_spacing()
{
	_yaxis->check_redraw();
}

double Graph::x_min() const
{
	if (is_eye_plot()) {
		((Graph *) this)->update_x_limits();
		double min_x = x_min_this_plot ;
		if (x_scale_flag) {
			double max_x = x_max_this_plot ;
			double diff = max_x - min_x ;
			double Return = min_x + diff * x_min_relative_displayed ;
			// LogOut << "x_min returning " << Return << "\n" ;
			return Return ;
		}
		return min_x ;
	}
	// if (get_data_plot()->is_two_dimensional())
	//	 return get_data_plot()->GetScaleX().GetMinimum();
	return get_data_plot()->get_min_x_current_plot();
}

double Graph::x_max() const
{
	if (is_eye_plot()) {
		((Graph *) this)->update_x_limits();
		double max_x = x_max_this_plot ;
		if (x_scale_flag) {
			double min_x = x_min_this_plot ;
			double diff = max_x - min_x ;
			double Return  = x_min() + diff / x_diff_relative_displayed ;
			// LogOut << "x_max returning " << Return << "\n" ;
			return Return ;
		}
		return max_x ;
	}
	return get_data_plot()->get_max_x_current_plot();
}

void Graph::update_x_limits()
{
	// LogOut << "Graph::update_x_limits(), _rescale_x = "<<_rescale_x<<"\n";
	if (allow_fetch_remote_x()) {
		check_do_rescale_x();
		double new_x_min = get_data_plot()->GetMinXVal();
		double new_x_max = get_data_plot()->GetMaxXVal();
		// LogOut << "new_x = " << new_x_min << " : " << new_x_max << "\n" ;
		if (is_eye_plot() && x_scale_flag) {
			double new_diff = new_x_max - new_x_min ;
			double old_diff = x_max_this_plot - x_min_this_plot ;
			if (old_diff < 1.e-50 || new_diff < 1.e-50) {
				x_min_relative_displayed = 0 ;
				x_diff_relative_displayed = 1 ;
				x_scale_flag = 0 ;
			} else  {
				double displayed_min_val = old_diff * x_min_relative_displayed
					+ x_min_this_plot ;
				x_diff_relative_displayed *= new_diff / old_diff ;
				x_min_relative_displayed = 
					( displayed_min_val - new_x_min)/new_diff ;
			}
		}
		x_min_this_plot = new_x_min ;
		x_max_this_plot = new_x_max ;
	}
}

void Graph::update_y_limits()
{
	if (allow_fetch_remote_y()) {
		check_do_rescale_y();
		double new_y_min = get_data_plot()->GetMinYVal();
		double new_y_max = get_data_plot()->GetMaxYVal();
		if (y_scale_flag) {
			double new_diff = new_y_max - new_y_min ;
			double old_diff = y_max_this_plot - y_min_this_plot ;
			if (old_diff < 1.e-50 || new_diff < 1.e-50) {
				y_min_relative_displayed = 0 ;
				y_diff_relative_displayed = 1 ;
				y_scale_flag = 0 ;
			} else  {
				double displayed_min_val = old_diff * y_min_relative_displayed
					+ y_min_this_plot ;
				// LogOut<<"displayed min val = " << displayed_min_val << "\n" ;
				y_diff_relative_displayed *= new_diff / old_diff ;
				y_min_relative_displayed =
					( displayed_min_val - new_y_min)/new_diff ;
			}
		}
		y_min_this_plot = new_y_min ;
		y_max_this_plot = new_y_max ;
	}
}

double Graph::y_min() const
{
	((Graph *) this)->update_y_limits();
	double min_y =  y_min_this_plot ;
	// LogOut << "Graph::y_min, min_y = " << min_y << "\n" ;
	if (y_scale_flag) {
		double max_y = y_max_this_plot ;
		double diff = max_y - min_y ;
		// LogOut << "max_y = " << max_y << ", diff = " << diff << "\n" ;
		min_y += diff * y_min_relative_displayed ; 
	}
	// LogOut << "Returning " << min_y << "\n" ;
	return min_y ;
}

double Graph::y_max() const
{
	((Graph *) this)->update_y_limits();
	double max_y = y_max_this_plot ;
	if (y_scale_flag) {
		double min_y = y_min_this_plot ;
		double diff = max_y - min_y ;
		return y_min() + diff / y_diff_relative_displayed ; 
	}
	return max_y ;
}

static const double very_small = 1.e-200 ;
static const double very_big = 1.e200 ;

int Graph::x_setup_rescale(Coord abs_right, Coord abs_left)
{
/*
 *	LogOut << "Graph::x_setup_rescale(" << abs_right << ", " <<
 *		abs_left << ")\n" ;
 */	
	rel_max = x_rel(abs_right);
	rel_min = x_rel(abs_left);
	rel_diff = rel_max - rel_min ;
/*
 *	LogOut << "rel_max = " << rel_max << ", rel_min = " << rel_min
 *		<< ", rel_diff = " << rel_diff << "\n" ;
 */
	if (rel_diff < .01) return 0 ;
	cur_max = x_max();
	cur_min = x_min();
	cur_diff = cur_max - cur_min ;
/*
 *	LogOut << "cur_max = " << cur_max << ", cur_min = " << cur_min <<
 *		", cur_diff = " << cur_diff << "\n" ;
 */
	min_val = get_data_plot()->GetMinXVal() ;
	max_val = get_data_plot()->GetMaxXVal() ;
	diff = max_val - min_val ;
/*
 *	LogOut << "min_val = " << min_val << ", max_val = " << max_val <<
 *			", diff = " << diff << "\n" ;
 */
	if (diff < very_small) return 0 ;
	return 1 ;
}

int Graph::y_setup_rescale(Coord abs_top, Coord abs_bottom)
{
/*
 *	LogOut << "Graph::y_setup_rescale(" << abs_top << ", " <<
 *		abs_bottom << ")\n" ;
 */
	
	rel_max = y_rel(abs_top);
	rel_min = y_rel(abs_bottom);
	rel_diff = rel_max - rel_min ;
/*
 *	LogOut << "rel_max = " << rel_max << ", rel_min = " << rel_min
 *		<< ", rel_diff = " << rel_diff << "\n" ;
 */
	if (rel_diff < .01) return 0 ;
	cur_max = y_max();
	cur_min = y_min();
	cur_diff = cur_max - cur_min ;
/*
 *	LogOut << "cur_max = " << cur_max << ", cur_min = " << cur_min <<
 *		", cur_diff = " << cur_diff << "\n" ;
 */
	min_val = get_y_min_this_plot();
	max_val = get_y_max_this_plot();
	diff = max_val - min_val ;
/*
 *	LogOut << "min_val = " << min_val << ", max_val = " << max_val <<
 *			", diff = " << diff << "\n" ;
 */
	if (diff < very_small) return 0 ;
	return 1 ;
}

int Graph::rescale_y_more_detail()
{
	double new_diff = cur_diff * rel_diff ;
	double new_y_min = cur_min + rel_min * cur_diff ;
/*
 *	LogOut << "new_y_min = " << new_y_min << ", new_diff = " <<
 *		new_diff << "\n" ;
 */ 
	if (new_diff <= very_small) return 0 ;
	y_scale_flag = 1 ;
	y_min_relative_displayed = (new_y_min - min_val) / diff ;
	y_diff_relative_displayed = diff / new_diff ;
/*
 *	LogOut << "y_min_relative_displayed = " << y_min_relative_displayed
 *		<< "\n" ;
 *	LogOut << "y_diff_relative_displayed = " << y_diff_relative_displayed
 *		<< "\n" ;
 */
	rescale_y();
	return 1 ;
}

int Graph::rescale_y_more_detail(Coord abs_top, Coord abs_bottom)
{
/*
 *	LogOut << "Graph::rescale_y_more_detail(" << abs_top << ", " <<
 *		abs_bottom << ")\n" ;
 */
	if (!y_setup_rescale(abs_top, abs_bottom)) return 0 ;
	return rescale_y_more_detail();
}

int Graph::rescale_y_less_detail(Coord abs_top, Coord abs_bottom)
{
/*
 *	LogOut << "Graph::rescale_y_less_detail(" << abs_top << ", " <<
 *		abs_bottom << ")\n" ;
 */
	if (!y_setup_rescale(abs_top, abs_bottom)) return 0 ;
	return rescale_y_less_detail();
}


int Graph::rescale_x_more_detail()
{
	double new_diff = cur_diff * rel_diff ;
	double new_x_min = cur_min + rel_min * cur_diff ;
/*
 *	LogOut << "new_x_min = " << new_x_min << ", new_diff = " <<
 *		new_diff << "\n" ;
 */
	if (new_diff <= very_small) return 0 ;
	if (is_eye_plot()) {
		x_scale_flag = 1 ;
		x_min_relative_displayed = (new_x_min - min_val) / diff ;
		x_diff_relative_displayed = diff / new_diff ;
/*
 *		LogOut << "x_min_relative_displayed = " << x_min_relative_displayed
 *			<< "\n" ;
 *		LogOut << "x_diff_relative_displayed = " << x_diff_relative_displayed
 *			<< "\n" ;
 */
		rescale_x();
		return 1 ;
	}
	return 0 ;
}

int Graph::rescale_x_more_detail(Coord abs_right, Coord abs_left)
{
/*
 *	LogOut << "Graph::rescale_x_more_detail(" << abs_right << ", " <<
 *		abs_left << ")\n" ;
 */
	// if (!x_setup_rescale(abs_right, abs_left)) return 0 ;
	x_setup_rescale(abs_right, abs_left);
	if (!is_eye_plot()) {
		int Return = get_data_plot()->ChangeXScale(rel_min, rel_max,1);
		if (Return) rescale_x();
		return Return ;
	}
	return rescale_x_more_detail();
}

int Graph::rescale_y_less_detail()
{
	double new_diff = cur_diff / rel_diff ;
	double new_y_min = cur_min - rel_min * new_diff ;
	if (rel_diff < very_small) return 0 ;
/*
 *	LogOut << "new_y_min = " << new_y_min << ", new_diff = " <<
 *		new_diff << "\n" ;
 */
	if (new_diff <= very_small) return 0 ;
	y_scale_flag = 1 ;
	y_min_relative_displayed = (new_y_min - min_val) / diff ;
	y_diff_relative_displayed = diff / new_diff ;
/*
 *	LogOut << "y_min_relative_displayed = " << y_min_relative_displayed
 *		<< "\n" ;
 *	LogOut << "y_diff_relative_displayed = " << y_diff_relative_displayed
 *		<< "\n" ;
 */
	rescale_y();
	return 1 ;
}

int Graph::rescale_x_less_detail(Coord abs_right, Coord abs_left)
{
/*
 *	LogOut << "Graph::rescale_x_less_detail(" << abs_right << ", " <<
 *		abs_left << ")\n" ;
 */
	// if (!x_setup_rescale(abs_right, abs_left)) return 0 ;
	x_setup_rescale(abs_right, abs_left) ;
	// LogOut << "rel_min = " << rel_min << ", rel_max = " << rel_max << "\n" ;
	if (!is_eye_plot()) {
		int Return = get_data_plot()->ChangeXScale(rel_min, rel_max,0);
		if (Return) rescale_x();
		return Return ;
	}
	return rescale_x_less_detail();
}

int Graph::rescale_x_less_detail()
{
	double new_diff = cur_diff / rel_diff ;
	double new_x_min = cur_min - rel_min * new_diff ;
	if (rel_diff < very_small) return 0 ;
/*
 *	LogOut << "new_x_min = " << new_x_min << ", new_diff = " <<
 *		new_diff << "\n" ;
 */
	if (new_diff <= very_small) return 0 ;
	if (is_eye_plot()) {
		x_scale_flag = 1 ;
		x_min_relative_displayed = (new_x_min - min_val) / diff ;
		x_diff_relative_displayed = diff / new_diff ;
/*
 *		LogOut << "x_min_relative_displayed = " << x_min_relative_displayed
 *			<< "\n" ;
 *		LogOut << "x_diff_relative_displayed = " << x_diff_relative_displayed
 *			<< "\n" ;
 */
		rescale_x();
		return 1 ;
	}
	
	return 0 ;
}

void Graph::end_rescale()
{
/*
 *	LogOut << "Graph::end_rescale() key = 0x" << hex << resize_key_stroke <<
 *		dec << "\n" ;
 */
	if (!resize_key_stroke) return ;
	window()->pop_cursor();
	_marker->unmark();
	resize_key_stroke = 0 ;
}


double Graph::x_abs_value(float x_rel)
{
	double x_min_ = x_min();
	double x_max_ = x_max();
	return x_min_ + x_rel*(x_max_ - x_min_);
}

float Graph::x_coord(float x_rel)
{
	// LogOut << "Graph::x_coord(" << x_rel << ")\n" ;
	if (!is_mapped()) return 0 ;
	const Allocation&a = *(_plot->allocation());
	Coord left = a.left(); Coord right = a.right();
	double diff = right -left ;
	// LogOut << "l = " << left << ", r = " << right << ", d = " << diff << "\n";
	// LogOut << "return " << x_rel * diff + left << "\n" ;
	return x_rel * diff + left ;
}

float Graph::y_coord(float y_rel)
{
	// LogOut << "Graph::y_coord(" << y_rel << ")\n" ;
	if (!is_mapped()) return 0 ;
	const Allocation&a = *(_plot->allocation());
	Coord bottom = a.bottom(); Coord top = a.top();
	double diff = top -bottom ;
/*
 *	LogOut << "b = " << bottom << ", t = " << top << ", d = " << diff << "\n";
 *	LogOut << "return " << y_rel * diff + bottom << "\n" ;
 */
	return y_rel * diff + bottom ;
}


float Graph::x_rel(Coord x_abs)
{
	if (!is_mapped()) return 0 ;
	const Allocation&a = *(_plot->allocation());
	Coord left = a.left(); Coord right = a.right();
	double diff = right -left ;
	if (diff <= 0.0) return 0. ;
	return (x_abs - left) / diff ;
}

float Graph::x_rel(double x_abs)
{
	if (!is_eye_plot()) {
		double diff = x_max() - x_min() ;
		if (diff < 1.e-20) return 0 ;
		return x_abs / diff + x_min();
	}
	double max = get_data_plot()->GetMaxXVal();
	double min = get_data_plot()->GetMinXVal();
	double diff = max - min ;
	if (diff <= 0.0) return .5 ;
	return (x_abs - min) / diff ;
}

double Graph::y_abs_value(float y_rel)
{
	double y_min_ = y_min();
	return y_min_ + y_rel*(y_max() - y_min_);
}

float Graph::y_rel(Coord y_abs)
{
	if (!is_mapped()) return 0 ;
	const Allocation * a = _plot->allocation();
	if (!a) return 0 ;
	Coord bottom = a->bottom(); Coord top = a->top();
	double diff = top -bottom ;
	if (diff <= 0.0) return 0.;
	return (y_abs - bottom) / diff ;
}

float Graph::y_rel(double y_abs)
{
	double max = get_y_max_this_plot();
	double min = get_y_min_this_plot();
	double diff = max - min ;
	if (diff <= 0.0) return .5 ;
	return (y_abs - min) / diff ;
}

float Graph::y_scale(float y) const
{
	// LogOut << "Graph::y_scale(" << y << ")\n" ;
	if (!y_scale_flag) return y;
	double ys = (y - y_min_relative_displayed) * y_diff_relative_displayed ;
	if (ys < 0.0) ys = 0.0 ;
	else if (ys > 1.0) ys = 1.0;
	// LogOut << "y_scale returning " << ys << "\n" ;
	return ys;
}

float Graph::x_scale(float x) const
{
	if (!x_scale_flag) return x;
	double xs = (x - x_min_relative_displayed) * x_diff_relative_displayed ;
	if (xs < 0.0) xs = 0.0 ;
	if (xs > 1.0) xs = 1.0;
	return xs;
}


PlotChannelPointer * Graph::get_data_plot_channel(int i) const 
{
	return data_plot->GetChannel(i);
}

void Graph::redraw() const
{
	_ppatch->redraw();
	// background_patch->change(0);
	// background_patch->redraw();
}

void Graph::reset()
{
	_rescale_x = _rescale_y = no_data ;
	_plot->reset();
}

void Graph::rescale_x()
{
	// LogOut <<"Graph::rescale_x() for "<<get_data_plot()->GetCaption()<<"\n";
	// LogOut << "_rescale_x = " << _rescale_x << "\n" ;
	if (_rescale_x == no_data) return ;
	set_allow_rescale_x();
	redraw_x();
}

void Graph::rescale_y()
{
/*
 *	LogOut <<"Graph::rescale_y() for "<<get_data_plot()->GetCaption()<<"\n";
 *	LogOut << "_rescale_y = " << _rescale_x << "\n" ;
 */
	if (_rescale_y == no_data) return ;
	set_allow_rescale_y();
	redraw_y();
}

void Graph::redraw_x() const
{
	// LogOut << "Graph_redraw_x()\n" ;
	// _xpatch->redraw();
	background_patch->redraw();
}

void Graph::redraw_y() const
{
	// LogOut << "Graph_redraw_y()\n" ;
	// _ypatch->redraw();
	background_patch->redraw();
}

void Graph::check_axis_redraw() const
{
	// _xaxis->check_redraw();
	// _yaxis->check_redraw();
	((Graph*)this)->set_x_tick_spacing();
	((Graph*)this)->set_y_tick_spacing();
}

const Allocation *  Graph::plot_allocation() const
{
	return _plot->allocation();
}

Adjustable* Graph::adjustable()
{
	return &box ;
}

void Graph::update(Observable*)
{
	if (in_update || in_adjust_adjuster) return ;
	int new_sample = (int) (box.cur_lower(Dimension_X) + .5) ;
	// LogOut << "Graph::update, new_sample = " << new_sample << "\n" ;
	int32 end_limit = data_plot->last_sample_index() -
		data_plot->samples_in_plot();
	if (new_sample < 0 ) new_sample = 0 ;
	if (new_sample > end_limit) new_sample = end_limit ;
	// LogOut<<"end = "<<end_limit << ", new_sample =" << new_sample << "\n" ;
	in_update = 1 ;
	data_plot->plot_sample(new_sample);
	in_update = 0 ;
}

void Graph::adjust_adjuster()
{
	in_adjust_adjuster = 1 ;
	// int32 end_limit = data_plot->last_sample_index() - data_plot->samples_in_plot();
	int32 end_limit = data_plot->last_sample_index() ;
/*
 *	LogOut << "adjust_adjuster(), end_limit = " << end_limit <<
 *	   ", current = " << data_plot->current_sample() << ", e-c = " <<
 *		end_limit - data_plot->current_sample() << "\n" ;
 */
	box.window_size(data_plot->samples_in_plot());
	box.upper_bound(end_limit);
	int scroll_increment = 1 ;
	// int page_increment= data_plot->GetNumberSamplesInPlot() ;
	int page_increment= data_plot->samples_in_plot();
	if (!data_plot->is_two_dimensional() || data_plot->GetBlockSize() == 1) {
		scroll_increment = (int32) (page_increment * .1) ;
		if (scroll_increment < 1) scroll_increment = 1 ;
	} 
	box.scroll_incr(scroll_increment);
	box.page_incr(page_increment);
	box.current_value(data_plot->current_sample())  ;
	in_adjust_adjuster = 0 ;
}

void Graph::update_size()
{
	adjust_adjuster();
}

void Graph::background_draw(Canvas *c, const Allocation & a) const
{
	if (_rescale_x == no_data) return ;
	background_patch->draw(c, a);
}

static void plot_save_callback(const char * name, InputType, void* obj)
{
    ((Graph *)obj)->save_plot(name);
}


void Graph::save_plot(const char * name)
{
	if (!name) return ;
	if (!*name) return ;
	if (*name == '\03') return ;
	ThePlotFileManager.WritePlot(name,*data_plot);
}

void Graph::save_plot()
{
	const char * win_name = data_plot->GetCaption() ;
	char * name = Concatenate(win_name,".plt");

	char * prompt = Concatenate("Specify file to save plot `", win_name,"' to:");
	DspApplication::prompt(prompt,plot_save_callback, name,this,
		data_plot->window());
	delete name ;
	delete prompt;
}

void Graph::raise_window()
{
	window()->raise();
}

static void prompt_value_callback(const char * name, InputType, void* obj)
{
    ((Graph *)obj)->prompt_value(name);
}

void Graph::prompt_value(const char * val)
{
	got_value = 0 ;
	if (!val) return ;
	if (!*val) return ;
	if (*val == '\03') return ;
	{
		prompt_value_return = .3333e-33 ;
		istrstream value(val);
    	value >> prompt_value_return ;
		if (!value.fail()) got_value = 1 ;
/*
 *		LogOut << "prompt_value_return = " << prompt_value_return << ", "
 *			<< got_value << "\n" ;
 */
	}
}

int Graph::prompt_for_value(const char * msg)
{
	 DspApplication::prompt(msg, prompt_value_callback,
		"",this,data_plot->window());
	return got_value ;
}

void Graph::to_time()
{
	if (!prompt_for_value("Specify time for first sample")) return;
	data_plot->GoToTime(prompt_value_return);
}


void Graph::to_sample()
{
	if (!prompt_for_value("Specify sample index for first sample:")) return;
	if (prompt_value_return > INT_MAX || prompt_value_return < INT_MIN) return;
	data_plot->GoToSampleIndex((int32) prompt_value_return);
}

void Graph::ahead_time()
{
    if (!prompt_for_value("Specify time to move ahead (- to move back):"))
		return;
    data_plot->GoToTime(data_plot->GetCurrentAddress() + prompt_value_return) ;
}

void Graph::ahead_samples()
{
    if (!prompt_for_value(
		"Specify sample index to move ahead (- to move back):")) return;
	if (prompt_value_return > INT_MAX || prompt_value_return < INT_MIN) return;
    data_plot->GoToSampleIndex(data_plot->GetCurrentSample() + (int32) index);

}

void Graph::eye_plot_samples()
{
	double samples = data_plot->GetNumberSamplesInPlot();
	// LogOut << "Graph::eye_plot_samples(), samples = " << samples << "\n" ;
	if (samples < 2) return ;
	const buf_size=64;
	char buf[buf_size];
	memset(buf,'\0',buf_size);
	ostrstream str(buf,buf_size-1);
	str << sample_width();
	char * to_del = Concatenate(buf,
		" samples now displayed. Specify up to 10000 samples to display or RETURN:");
	int ok = prompt_for_value(to_del) ;
	delete to_del ;
	if (!ok) return ;
	if (samples == prompt_value_return) return ;
	if (prompt_value_return < 2) return ;
	if (prompt_value_return > 10000) return ;
	if (prompt_value_return > samples)
		data_plot->ChangeXScale(0,(prompt_value_return-1)/(samples-1),1);
	else data_plot->ChangeXScale(0,(samples-1)/(prompt_value_return-1),0);
}

