

// ##########################################################################
// #                                                                        #
// #                                  Chart                                 #
// #                                                                        #
// #                            - Definitionen -                            #
// #                                                                        #
// ##########################################################################



#include"gui/chart.h"
#include"ui/dsplsurf.h"
#include"base/seqimp.cxx"
#include"base/basicops.cxx"
#include"math.h"


typedef CL_Basics  <CL_Pair> BasicsValPair;
CL_Pair BasicsValPair::_Null = CL_Pair();
typedef CL_Iterator<CL_Pair> IteratorValPair;



// **************************************************************************
// *                             OBJEKT-HANDLING                            *
// **************************************************************************



// ************
// Konstruktion
// ************


Chart :: Chart(UI_VObjCollection*  parent,
					const UI_Rectangle& shape,
					UI_ViewID           id,
					const char*         ttl,
					ChartStyle          style
				  )
		 : UI_VObjCollection(parent, shape, id)
{
  CnvX.initialize(7,2,',',TRUE);
  CnvY.initialize(7,2,',',TRUE);
  Distance = 5;
  MaxX   = 0;
  MaxY   = 0;
  MinX   = 0;
  MinY   = 0;
  LabelW = 40;
  LabelH = 15;
  Num    = 0;
  Area   = UI_Rectangle(50,50,shape.Width()-100,shape.Height()-100);
  Points = NULL;
  DscX   = new UI_Label(this,UI_Rectangle(25,20,50,20),ID_CHART_DSCX);
  DscY   = new UI_Label(this,UI_Rectangle(Area.Right()+5,Area.Bottom()-10,
														50,20),ID_CHART_DSCY);
  CTitle  = new UI_Label(this,UI_Rectangle(shape.Width() / 2 + shape.Left() - 50,
														0,100,20),ID_CHART_TITLE);
  CTitle->Title() = ttl;
  BrushColor = UIColor_Red;
  PenColor   = UIColor_Black;
  DrawOption = UID_Fill | UID_Outline;
  set_Style(style);
  Delta = 0;
};




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

// Destruktion
// ***********


Chart :: ~Chart()
{
  delete [] Points;
  Points = NULL;
  Num    = 0;
};



// ***************
// Initialisierung
// ***************


bool Chart :: MakeVisualElement()
{
  bool res = UI_VObjCollection::MakeVisualElement();
  return res;
};


void Chart :: Initialize       ()
{
  UI_VObjCollection::Initialize();
  return;
};


bool Chart :: Paint(const UI_Rectangle&)
{
  draw();
  return FALSE;
};


void Chart :: reshapeLabels()
{
  UI_Rectangle shpY = DscY ->Shape();
  UI_Rectangle shpX = DscX ->Shape();
  UI_Rectangle shpT = CTitle->Shape();

  DscY->Shape()  = UI_Rectangle( Area.Left() - shpY.Width()/2,
											Area.Top()  - shpY.Height() - LabelH/2 - 1,
											shpY.Width(), shpY.Height()
										 );

  DscX->Shape()   = UI_Rectangle( Area.Right() + 2,
											 Area.Bottom() - shpX.Height(),
											 shpX.Width(), shpX.Height()
										  );
  CTitle->Shape() = UI_Rectangle( Area.Width()/2 + Area.Left() - shpT.Width()/2,
											 0, shpT.Width(), shpT.Height()
										  );
  return;
};



void Chart :: draw()
{
  CreateDisplaySurface();
  reshapeLabels();
  UI_VObjCollection::Paint(UI_Rectangle(0,0,_shape.Width()+2,_shape.Height()+2));
  if (fillArray() && _displaySurface)
	{
	  _displaySurface->Brush().Color(BrushColor);
	  _displaySurface->Pen  ().Color(PenColor);
	  if (Num > 2)
		  _displaySurface->DrawPolygon(Points,Num,DrawOption);
	  labelXAxis();
	  labelYAxis();
	};
  drawXAxis();
  drawYAxis();
  DestroyDisplaySurface();
  _displaySurface = NULL;
  return;
};


bool Chart :: fillArray()
{
  delete [] Points;
  Points = NULL;
  Num    = 0;
  if (setExtrema() && Poly)
	  (this->*Poly)();
  if (Points)
		 return TRUE;
  else return FALSE;
};


void Chart :: polyLinear()
{
  Delta  = 0;
  Num    = Values.Size()+2;              // Number of points needed
  Points = new UI_Point[Num];
  Points[0] = Area.BottomLeft();         // first point is origin
  for (unsigned i = 1; i < Num-1; i++)                   // fill following points
		Points[i] = UI_Point(coordX(Values[i-1].valueX()), // with x-value and
									coordY(Values[i-1].valueY()));// y-value
  Points[Num-1] = UI_Point(Area.BottomRight()); // last point is bottom right
  return;
};


void Chart :: polyGradual()
{
  Delta  = 1;
  Num    = Values.Size()*2+2;            // Number of points needed
  Points = new UI_Point[Num];
  Points[0]  = Area.BottomLeft();         // first point is origin
  unsigned j = 1;
  unsigned n = Values.Size();
  long     x = Area.Left();
  for (unsigned i = 0; i < n; i++)
	 {                                                  // fill following points
		Points[j]   = UI_Point(x,                          // with x-value and
									  coordY(Values[i].valueY()));// y-value
		x           = coordX(Values[i].valueX());
		Points[j+1] = UI_Point(x,                          // with x-value and
									  coordY(Values[i].valueY()));// y-value
		j += 2;
	 };
  Points[Num-1] = UI_Point(Area.BottomRight()); // last point is bottom right
  return;
};


bool Chart :: setExtrema()
{
  bool res = FALSE;
  if (Values.Size() > 0)
	{
	  Values.Sort();
	  MaxX = Values[Values.Size()-1].valueX();  // Extrema of X
	  MinX = Values[0].valueX();
	  MaxY = Values[0].valueY();
	  MinY = MaxY;
	  for (unsigned i = 1; i < Values.Size(); i++)
	  {
		 if (Values[i].valueY() < MinY)
			  MinY = Values[i].valueY();
		 if (Values[i].valueY() > MaxY)
			  MaxY = Values[i].valueY();
	  };
	  res = TRUE;
	}
  else
	{
	  MaxX = 0;
	  MaxY = 0;
	  MinX = 0;
	  MinY = 0;
	};
  return res;
};


bool Chart :: drawXAxis()
{
  if (_displaySurface)
	{
		_displaySurface->DrawLine(UI_Point(Area.TopLeft()),Area.BottomLeft());
		return TRUE;
	}
  else return FALSE;
};


bool Chart :: drawYAxis()
{
  if (_displaySurface)
	{
		_displaySurface->DrawLine(UI_Point(Area.BottomLeft()),Area.BottomRight());
		return TRUE;
	}
  else return FALSE;
};


bool Chart :: labelXAxis()
{
  if (_displaySurface && Points && Num > 0)
	{
	  unsigned half  = ceil (LabelW*1.0/2); // half width of label
	  long     x     = Points[0].XCoord();  // x-coordinate
	  long rightmost = x + half;            // rigth of label
	  double valX    = MinX;                // x-value

	  drawXLabel(x,valX,half);              // first x-value with MinX

	  unsigned i = 1;  // Index of point sequence
	  unsigned j = 0;  // Index of value sequence
	  unsigned d = 0;  // counter to check, if j has to be incremented

	  while (i < Num)
	  {
		 x = Points[i].XCoord();
		 if (x - half >= rightmost + Distance)
		  {
			 if (j < Values.Size())          // read next from sequence if
					valX = Values[j].valueX(); // possible
			 else valX = valueX(x);          // else calculate x-value
			 drawXLabel(x,valX,half);
			 rightmost = x + half;
		  };
		 i++;
		 if (d >= Delta)
		  {
			 d = 0;
			 j ++;
		  }
		 else d++;
	  };
	 return TRUE;
	}
  else return FALSE;
};


bool Chart :: drawXLabel(long x, double vx, unsigned half)
{
  if (_displaySurface)
	{
	  static CL_String label;
	  CnvX(vx,label);
	  _displaySurface->WriteString(label,
											 UI_Rectangle(x - half, Area.Bottom() + 5,
															  LabelW, LabelH
															 ) );
	  _displaySurface->DrawLine   (UI_Point(x, Area.Bottom() - 5),
											 UI_Point(x, Area.Bottom() + 5) );
	  return TRUE;
	}
  else return FALSE;
};



bool Chart :: labelYAxis()
{
  if (_displaySurface && Points && Num > 0)
	{
	  unsigned half  = ceil (LabelH*1.0/2); // half height of label
	  long     y     = Points[0].YCoord();  // y-coordinate
	  long topmost   = y + half;            // top of label
	  double valY    = MinY;                // y-value

	  drawYLabel(y,valY,half);              // first y-value with MinY

	  unsigned i = 1;  // Index of point sequence
	  unsigned j = 0;  // Index of value sequence
	  unsigned d = 0;  // counter to check, if j has to be incremented

	  while (i < Num)
	  {
		 y = Points[i].YCoord();
		 if (y + half <= topmost - Distance)
		  {
			 if (j < Values.Size())          // read next from sequence if
					valY = Values[j].valueY(); // possible
			 else valY = valueY(y);          // else calculate x-value
			 drawYLabel(y,valY,half);
			 topmost   = y - half;
		  };
		 i++;
		 if (d >= Delta)
		  {
			 d = 0;
			 j ++;
		  }
		 else d++;
	  };
	  return TRUE;
	}
  else return FALSE;
};


bool Chart :: drawYLabel(long y, double vy, unsigned half)
{
  if (_displaySurface)
	{
	  static CL_String label;
	  CnvY(vy,label);
	  _displaySurface->WriteString(label,
											 UI_Rectangle(Area.Left() - LabelW - 5,
															  y - half, LabelW, LabelH) );
	  _displaySurface->DrawLine(UI_Point(Area.Left() -  5, y),
										 UI_Point(Area.Left() +  5, y) );
	  return TRUE;
	}
  else return FALSE;
};



long   Chart :: coordX(double x) const  // x-coordinate of function's x-value
{
  if (MaxX > 0)
		 return ( (x - MinX)/(MaxX - MinX) * Area.Width() + Area.Left());
  else return 0;
};


long   Chart :: coordY(double y) const  // y-coordinate of function's y-value
{
  if (MaxY > 0)
		 return ( Area.Height() * ( 1.0 - (y - MinY)/(MaxY-MinY)) + Area.Top());
  else return 0;
};


double Chart :: valueX(long   x) const  // function's x-value of coordinate x
{
  if (Area.Width() > 0)
		 return ( (x - Area.Left()) * (MaxX -MinX) * 1.0 / Area.Width() + MinX);
  else return 0;
};


double Chart :: valueY(long   y) const  // function's y-value of coordinate y

{

  if (Area.Height() > 0)

		 return ( (MaxY - MinY) * (1.0 - (y - Area.Top()) * 1.0 / Area.Height() )

				  + MinY

				  );

  else return 0;

};




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

// Attributszugriff
// ****************




PairSequence& Chart :: values()
{
  return Values;
};


UI_Rectangle& Chart :: area  ()

{

  return Area;
};



CL_String&    Chart :: Title ()

{

  return CTitle->Title();
};



unsigned&     Chart :: labelW()

{

  return LabelW;;
};



unsigned&     Chart :: labelH()

{

  return LabelH;
};



UI_Label*     Chart :: dscX  ()

{

  return DscX;
};



UI_Label*     Chart :: dscY  ()

{

  return DscY;
};



unsigned&     Chart :: distance()

{

  return Distance;
};



short unsigned& Chart :: drawOption()
{
  return DrawOption;
};


UI_Color&       Chart :: brushColor()
{
  return BrushColor;
};


UI_Color&       Chart :: penColor  ()
{
  return PenColor;
};


StringConverter& Chart :: cnvX()
{
  return CnvX;
};


StringConverter& Chart :: cnvY()
{
  return CnvY;
};


void Chart :: set_Style(ChartStyle s)
{
  Style = s;
  if (s == GRADUAL)
		 Poly = &Chart::polyGradual;
  else Poly = &Chart::polyLinear;
  return;
};


ChartStyle Chart :: get_Style() const
{
  return Style;
};
