/*********************************************************************************
 J2DPlotData.cc
 
	J2DPlotData class.
 
	Copyright @ 1997 by Glenn W. Bach. All rights reserved.

 ********************************************************************************/

#include <J2DPlotData.h>
#include <jAssert.h>

/*********************************************************************************
 Create

 ********************************************************************************/

JBoolean
J2DPlotData::Create
	(
	J2DPlotData **plotData, 
	const JArray<JFloat>& x, 
	const JArray<JFloat>& y,
	JBoolean listen
	)
{
	if (OKToCreate(x,y))
		{
		*plotData = new J2DPlotData(x,y,listen);
		return kTrue;
		}
		
	return kFalse;
}

/*********************************************************************************
 OKToCreate

 ********************************************************************************/

JBoolean
J2DPlotData::OKToCreate
	(
	const JArray<JFloat>& x, 
	const JArray<JFloat>& y
	)
{
	return JI2B(x.GetElementCount() == y.GetElementCount());
}

/*********************************************************************************
 Constructor (private)

 ********************************************************************************/

J2DPlotData::J2DPlotData
	(
	const JArray<JFloat>& x,
	const JArray<JFloat>& y,
	const JBoolean listen
	)
	:
	JPlotDataBase(kScatterPlot)
{
	SetElementCount(x.GetElementCount());
	assert(y.GetElementCount() == x.GetElementCount());
	
	if (listen)
		{
		itsXData = const_cast< JArray<JFloat>* >(&x);
		itsYData = const_cast< JArray<JFloat>* >(&y);
		ListenTo(itsXData);
		ListenTo(itsYData);
		}
		
	else
		{
		itsXData = new JArray<JFloat>(x);
		itsYData = new JArray<JFloat>(y);
		}
		
	itsHasXErrors = kFalse;
	itsHasYErrors = kFalse;
	itsIsValid = kTrue;
	itsIsListening = listen;
	
	itsCurrentXMax = 0;
	itsCurrentXMin = 0;
	itsCurrentYMax = 0;
	itsCurrentYMin = 0;
}

/*********************************************************************************
 Destructor
 

 ********************************************************************************/
 
J2DPlotData::~J2DPlotData()
{
	if (!itsIsListening)
		{
		delete itsXData;
		delete itsYData;
		if (itsHasXErrors)
			{
			delete itsXPErrorData;
			if (!itsXErrorsSymmetric)
				{
				delete itsXMErrorData;
				}
			}
		if (itsHasYErrors)
			{
			delete itsYPErrorData;
			if (!itsYErrorsSymmetric)
				{
				delete itsYMErrorData;
				}
			}
		}
}

/*********************************************************************************
 GetElement

 ********************************************************************************/

void
J2DPlotData::GetElement
	(
	const JIndex	index,
	J2DDataPoint*	data
	)
	const
{
	data->x = itsXData->GetElement(index);
	data->y = itsYData->GetElement(index);
	
	if (itsHasXErrors)
		{
		data->xerr = itsXPErrorData->GetElement(index);
		if (!itsXErrorsSymmetric)
			{
			data->xmerr = itsXMErrorData->GetElement(index);
			}
		else
			{
			data->xmerr = data->xerr;
			}
		}
	else
		{
		data->xerr  = 0;
		data->xmerr = 0;
		}
		
	if (itsHasYErrors)
		{
		data->yerr = itsYPErrorData->GetElement(index);
		if (!itsYErrorsSymmetric)
			{
			data->ymerr = itsYMErrorData->GetElement(index);
			}
		else
			{
			data->ymerr = data->yerr;
			}
		}
	else
		{
		data->yerr  = 0;
		data->ymerr = 0;
		}
}

/*********************************************************************************
 GetXMax

 ********************************************************************************/
 
JFloat
J2DPlotData::GetXMax()
	const
{
	if (GetElementCount() == 0)
		{
		if (itsIsValid)
			{
			return 0;
			}
		else
			{
			return itsCurrentXMax;
			}
		}
		
	JFloat max;
	J2DDataPoint pt;
	GetElement(1, &pt);
	max = pt.x + pt.xerr;
	
	for (JSize i = 2; i <= GetElementCount(); i++)
		{
		GetElement(i, &pt);
		if (pt.x + pt.xerr > max)
			{
			max = pt.x + pt.xerr;
			}
		}
		
	const_cast<J2DPlotData*>(this)->itsCurrentXMax = max;
	return max;
}

/*********************************************************************************
 GetXMin

 ********************************************************************************/

JFloat 
J2DPlotData::GetXMin()
	const
{
	JFloat min = 0;
	
	JSize count = GetElementCount();
	
	if (count > 0)
		{
		J2DDataPoint pt;
		GetElement(1, &pt);
		min = pt.x - pt.xmerr;
		
		for (JSize i = 2; i <= GetElementCount(); i++)
			{
			GetElement(i, &pt);
			if (pt.x - pt.xmerr < min)
				{
				min = pt.x - pt.xmerr;
				}
			}
		}
		
	if (itsIsValid)
		{
		const_cast<J2DPlotData*>(this)->itsCurrentXMin = min;
		}
	return itsCurrentXMin;
}

/*********************************************************************************
 GetXRange

 ********************************************************************************/

void
J2DPlotData::GetXRange
	(
	JFloat* min, 
	JFloat* max
	)
	const
{
	*min = GetXMin();
	*max = GetXMax();
}

/*********************************************************************************
 GetYMax

 ********************************************************************************/

JFloat 
J2DPlotData::GetYMax()
	const
{
	if (GetElementCount() == 0)
		{
		if (itsIsValid)
			{
			return 0;
			}
		else
			{
			return itsCurrentYMax;
			}
		}
		
	JFloat max;
	J2DDataPoint pt;
	GetElement(1, &pt);
	max = pt.y + pt.yerr;
	
	for (JSize i = 2; i <= GetElementCount(); i++)
		{
		GetElement(i, &pt);
		if (pt.y + pt.yerr > max)
			{
			max = pt.y + pt.yerr;
			}
		}
	const_cast<J2DPlotData*>(this)->itsCurrentYMax = max;
	return max;
}

/*********************************************************************************
 GetYMin

 ********************************************************************************/

JFloat 
J2DPlotData::GetYMin()
	const
{
	JFloat min = 0;
	
	JSize count = GetElementCount();
	
	if (count > 0)
		{
		J2DDataPoint pt;
		GetElement(1, &pt);
		min = pt.y - pt.ymerr;
		
		for (JSize i = 2; i <= GetElementCount(); i++)
			{
			GetElement(i, &pt);
			if (pt.y - pt.ymerr < min)
				{
				min = pt.y - pt.ymerr;
				}
			}
		}
	if (itsIsValid)
		{
		const_cast<J2DPlotData*>(this)->itsCurrentYMin = min;
		}
	return itsCurrentYMin;
}

/*********************************************************************************
 GetYRange

 ********************************************************************************/

JBoolean
J2DPlotData::GetYRange
	(
	const JFloat	xMin, 
	const JFloat	xMax,
	JFloat*			yMin, 
	JFloat*			yMax
	)
	const
{
	*yMin = GetYMin();
	*yMax = GetYMax();
	return kTrue;
}

/*********************************************************************************
 IgnoreDataChanges

 ********************************************************************************/

void 
J2DPlotData::IgnoreDataChanges()
{
	if (itsIsListening == kFalse)
		{
		return;
		}
		
	StopListening(itsXData);
	itsXData = new JArray<JFloat>(*itsXData);

	if (itsHasXErrors)
		{
		StopListening(itsXPErrorData);
		itsXPErrorData = new JArray<JFloat>(*itsXPErrorData);
		if (!itsXErrorsSymmetric)
			{
			StopListening(itsXMErrorData);
			itsXMErrorData = new JArray<JFloat>(*itsXMErrorData);
			}
		}
		
	StopListening(itsYData);
	itsYData = new JArray<JFloat>(*itsYData);

	if (itsHasYErrors)
		{
		StopListening(itsYPErrorData);
		itsYPErrorData = new JArray<JFloat>(*itsYPErrorData);
		if (!itsYErrorsSymmetric)
			{
			StopListening(itsYMErrorData);
			itsYMErrorData = new JArray<JFloat>(*itsYMErrorData);
			}
		}
	
	itsIsListening = kFalse;
}

/*********************************************************************************
 SetXErrors
 

 ********************************************************************************/

JBoolean 
J2DPlotData::SetXErrors
	(
	JArray<JFloat>& xErr
	)
{
	if (xErr.GetElementCount() != GetElementCount())
		{
		return kFalse;
		}
		
	if (itsIsListening)
		{
		if (itsHasXErrors)
			{
			StopListening(itsXPErrorData);
			if (!itsXErrorsSymmetric)
				{
				StopListening(itsXMErrorData);
				}
			}
		itsXPErrorData = &xErr;
		ListenTo(itsXPErrorData);
		}
		
	else
		{
		itsXPErrorData = new JArray<JFloat>(xErr);
		}
		
	itsHasXErrors = kTrue;
	itsXErrorsSymmetric = kTrue;	
	Broadcast(CurveChanged());
	return kTrue;
}

/*********************************************************************************
 SetXErrors
 

 ********************************************************************************/

JBoolean 
J2DPlotData::SetXErrors
	(
	JArray<JFloat>& xPErr, 
	JArray<JFloat>& xMErr
	)
{
	if ((xPErr.GetElementCount() != GetElementCount()) ||
		(xMErr.GetElementCount() != GetElementCount()))
		{
		return kFalse;
		}
		
	if (itsIsListening)
		{
		if (itsHasXErrors)
			{
			StopListening(itsXPErrorData);
			if (!itsXErrorsSymmetric)
				{
				StopListening(itsXMErrorData);
				}
			}
		itsXPErrorData = &xPErr;
		itsXMErrorData = &xMErr;
		ListenTo(itsXPErrorData);
		ListenTo(itsXMErrorData);
		}
		
	else
		{
		itsXPErrorData = new JArray<JFloat>(xPErr);
		itsXMErrorData = new JArray<JFloat>(xMErr);
		}
		
	itsHasXErrors = kTrue;
	itsXErrorsSymmetric = kFalse;	
	Broadcast(CurveChanged());
	return kTrue;
}
	
/*********************************************************************************
 SetYErrors
 

 ********************************************************************************/

JBoolean 
J2DPlotData::SetYErrors
	(
	JArray<JFloat>& yErr
	)
{
	if (yErr.GetElementCount() != GetElementCount())
		{
		return kFalse;
		}
		
	if (itsIsListening)
		{
		if (itsHasYErrors)
			{
			StopListening(itsYPErrorData);
			if (!itsYErrorsSymmetric)
				{
				StopListening(itsYMErrorData);
				}
			}
		itsYPErrorData = &yErr;
		ListenTo(itsYPErrorData);
		}
		
	else
		{
		itsYPErrorData = new JArray<JFloat>(yErr);
		}
		
	itsHasYErrors = kTrue;
	itsYErrorsSymmetric = kTrue;	
	Broadcast(CurveChanged());
	return kTrue;

}

/*********************************************************************************
 SetYErrors
 

 ********************************************************************************/

JBoolean 
J2DPlotData::SetYErrors
	(
	JArray<JFloat>& yPErr, 
	JArray<JFloat>& yMErr
	)
{
	if ((yPErr.GetElementCount() != GetElementCount()) ||
		(yMErr.GetElementCount() != GetElementCount()))
		{
		return kFalse;
		}
		
	if (itsIsListening)
		{
		if (itsHasYErrors)
			{
			StopListening(itsYPErrorData);
			if (!itsYErrorsSymmetric)
				{
				StopListening(itsYMErrorData);
				}
			}
		itsYPErrorData = &yPErr;
		itsYMErrorData = &yMErr;
		ListenTo(itsYPErrorData);
		ListenTo(itsYMErrorData);
		}
	
	else
		{
		itsYPErrorData = new JArray<JFloat>(yPErr);
		itsYMErrorData = new JArray<JFloat>(yMErr);
		}
	
	itsHasYErrors = kTrue;
	itsYErrorsSymmetric = kFalse;	
	Broadcast(CurveChanged());
	return kTrue;

}

/*********************************************************************************
 Receive
 

 ********************************************************************************/

void 
J2DPlotData::Receive
	(
	JBroadcaster* sender, 
	const Message& message
	)
{
	if (message.Is(JOrderedSetT::kElementChanged) || 
		message.Is(JOrderedSetT::kElementMoved) )
		{
		Broadcast(CurveChanged());
		}
		
	if (message.Is(JOrderedSetT::kElementsInserted) ||
		message.Is(JOrderedSetT::kElementsRemoved) ||
		message.Is(JOrderedSetT::kAllElementsRemoved) )
		{
		ValidateCurve();
		Broadcast(CurveChanged());
		}
}

/*********************************************************************************
 ValidateCurve
 

 ********************************************************************************/

void 
J2DPlotData::ValidateCurve()
{
	JSize xCount = itsXData->GetElementCount();
	JSize yCount = itsYData->GetElementCount();
	if (xCount != yCount)
		{
		itsIsValid = kFalse;
		SetElementCount(0);
		return;
		}
		
	if (itsHasXErrors)
		{
		if (itsXPErrorData->GetElementCount() != xCount)
			{
			itsIsValid = kFalse;
			SetElementCount(0);
			return;
			}
		if (!itsXErrorsSymmetric)
			{
			if (itsXMErrorData->GetElementCount() != xCount)
				{
				itsIsValid = kFalse;
				SetElementCount(0);
				return;
				} 
			}
		}
	if (itsHasYErrors)
		{
		if (itsYPErrorData->GetElementCount() != xCount)
			{
			itsIsValid = kFalse;
			SetElementCount(0);
			return;
			}
		if (!itsYErrorsSymmetric)
			{
			if (itsYMErrorData->GetElementCount() != xCount)
				{
				itsIsValid = kFalse;
				SetElementCount(0);
				return;
				} 
			}
		}
	itsIsValid = kTrue;
	SetElementCount(xCount);
}

#define JTemplateType J2DPlotData
#include <JPtrArray.tmpls>
#undef JTemplateType
