/**@file shape.cpp: Implementation of some graphical shapes. */
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
 #include <wx/wx.h>
#endif
#include <wx/cursor.h>
#include <wx/dcbuffer.h>
#include <wx/file.h> // loadfile

#include "shape.h"
#include "hardware/OsSupp.h"


/** Draws the position of the shape on the plate.  
 *  @param[in] dc Device context reference  */
void CircRTShape::Paint(wxDC & dc)
{
  if(xPos==NULL || yPos==NULL) return;   //no input value

  dc.SetBackgroundMode(wxTRANSPARENT);
  wxBrush brush = wxBrush(color, style);
  dc.SetBrush(brush);
  dc.SetPen( wxPen(color, 2, wxSOLID));   
   
  double x = xPos->GetDouble();
  double y = yPos->GetDouble();
  if(IsNaN(&x)||IsNaN(&y)) return;

  x = Tx->X.Log2Canvas(x);  
  y = Tx->Y.Log2Canvas(y);  

  double Rx = fabs(Tx->X.Log2Canvas(Radius) - Tx->X.Log2Canvas(0));  
  double Ry = fabs(Tx->Y.Log2Canvas(Radius) - Tx->Y.Log2Canvas(0));  

  dc.DrawEllipse(x-Rx, y-Ry, 2*Rx, 2*Ry);
  if(PaintCross)
    {
    dc.DrawLine(x-Rx/4, y, x+Rx/4, y);
    dc.DrawLine(x, y-Ry/4, x, y+Ry/4);
    }
}


void XAxis::Paint(wxDC & dc)
{
  if(!Visible) return;
  dc.SetPen(LinePen);
  dc.DrawLine(
     Tx->X.Log2Canvas(Start), Tx->Y.Log2Canvas(Depth),
     Tx->X.Log2Canvas(End), Tx->Y.Log2Canvas(Depth) );
}


void YAxis::Paint(wxDC & dc)
{
  if(!Visible) return;
  dc.SetPen(LinePen);
  dc.DrawLine(
     Tx->X.Log2Canvas(Depth), Tx->Y.Log2Canvas(Start),
     Tx->X.Log2Canvas(Depth), Tx->Y.Log2Canvas(End) );
}


/** draw horizontal grid lines */
void HGrid::Paint(wxDC & dc)
{
double Val,StepVal,canval;
unsigned steps;
bool ValidRange;

  if(!Visible) return;

  dc.SetPen(LinePen);

  ValidRange = (Start!=0 || End!=0);
  if(!ValidRange)
    {
    Start = Tx->Y.Start;
    End = Tx->Y.End;
    }
      
  StepVal=(End-Start)/(double)Ticks;
  Val=Start;
  for(steps=0;steps<=Ticks;steps++)
    {
    canval = Tx->Y.Log2Canvas(Val);
    dc.DrawLine(Tx->X.CanvasStart, canval,
                Tx->X.CanvasEnd, canval);

    Val+=StepVal;
    }

  if(!ValidRange)
    Start = End = 0;
}


/** draw vertical grid lines */
void VGrid::Paint(wxDC & dc)
{
double Val, StepVal, canval;
unsigned steps;
bool ValidRange;
  if(!Visible) return;

  dc.SetPen(LinePen);

  ValidRange = (Start!=0 || End!=0);
  if(!ValidRange)
    {
    Start = Tx->X.Start;
    End = Tx->X.End;
    }

  StepVal=(End-Start)/(double)Ticks;
  Val=Start;
  for(steps=0;steps<=Ticks;steps++)  
    {
    canval = Tx->X.Log2Canvas(Val);
    dc.DrawLine(canval, Tx->Y.CanvasStart,
                canval, Tx->Y.CanvasEnd);
    Val+=StepVal;
    }    

  if(!ValidRange)
    Start = End = 0;
}


void VCursor::Paint(wxDC &dc)
{
  if(!Visible) return;

  dc.SetPen(LinePen);
  double canval = Tx->X.Log2Canvas(Position);
  dc.DrawLine(canval, Tx->Y.CanvasStart,
              canval, Tx->Y.CanvasEnd);
}


/** This procedure proposes suitable printf format string for painting labels. */
const char *GetFormatStr(double Distance)
{
  Distance = fabs(Distance);
  if(Distance>50000) return("%.2g");
  if(Distance>=1000) return("%.0f");
  if(Distance>=100) return("%.1f");
  if(Distance<0.05) return("%g");
return("%.2f");
}


void XLabels::Paint(wxDC & dc)
{
double val;
double xAxisRTick = (Ax->End - Ax->Start)/(double)ticks;
wxCoord w,h;
const char *FormStr;
int CheckI;

  if(!Visible) return;

  dc.SetFont(LabelFont);	//set a same font as form  
  FormStr=GetFormatStr(Ax->Start-Ax->End);
  CheckI = (Ax->Start<Ax->End) ? 0 : ticks;
  
  for(int i=0; i<=ticks; ++i)
    {    
    val = Ax->Start + i*xAxisRTick;    
    if(i==CheckI) w=0;		//Do not center starting label
	     else dc.GetTextExtent(wxString::Format(wxT(FormStr),val),&w,&h);
    dc.DrawText(wxString::Format(FormStr,val),
                Ax->Tx->X.Log2Canvas(val)-w/2,
                Ax->Tx->Y.CanvasEnd);   // Ax->Tx->Y.Log2Canvas(Ax->Depth); !!
    }
}


void YLabels::Paint(wxDC & dc)
{
double val;
double yAxisRTick = (Ay->End - Ay->Start)/(double)ticks;
wxCoord w,h;
const char *FormStr;

  if(!Visible) return;

  dc.SetFont(LabelFont);	//set a same font as form
  dc.SetTextForeground(TextColor);
  FormStr=GetFormatStr(Ay->Start-Ay->End);

  for(int i=0; i<=ticks; ++i)
    {     
    val = Ay->End - i*yAxisRTick;
    dc.GetTextExtent(wxString::Format(wxT(FormStr),val),&w,&h);      
    dc.DrawText(
          wxString::Format(wxT(FormStr),val),
          Ay->Tx->X.CanvasStart-w-1, Ay->Tx->Y.Log2Canvas(val)-h/2 );
    }
}


void DataBuffer::AppendItem(double NewVal, int Position)
{
switch(Position)
  {
  case -1:	//insert item to the end of a queue
        data.push_back(NewVal);	
	return;
  case 0:	//insert item to the front of a queue	
        data.push_front(NewVal);
	return;
  }
return;
}


/** Erase everthing stored inside data buffer. */
void DataBuffer::Clear(void)
{
  data.clear();
}


void DataBuffer::DeleteItem(int Position)
{
switch(Position)
  {
  case -1:	//delete item from the end of a queue
        data.pop_back();	
	return;
  case 0:	//delete item from the front of a queue	
        data.pop_front();
	return;
  }
return;
}


/**
 * Reads value from a data buffer 
 * @param[in] Position  Index to a data buffer.
 *          When positive reads from a beginning
 *          When negative reads from the end */
double DataBuffer::GetValue(int Position)
{
  if(data.empty()) return 0;

  switch(Position)
    {
    case 0:  return data.front();
    case -1: return data.back();
    default: 
       if(Position>=0) return data.at(Position);
                  else return(*(data.end() - (-Position)));
    }
}


void Serie::Paint(wxDC & dc)
{
double x1,x2,y1,y2;
double x1Fix,x2Fix,y1Fix,y2Fix;
double CsFixX,CeFixX;		///< Normalised Canvas start and canvas end X.
double CsFixY,CeFixY;		///< Normalised Canvas start and canvas end Y.

std::deque <double>::iterator IterX;
std::deque <double>::iterator IterY;

  if(!Visible) return;
  dc.SetPen(LinePen);

  IterX=XValues->data.end();
  IterY=YValues->data.end();
  if(IterX==XValues->data.begin() || IterY==YValues->data.begin()) return;

  IterX--;  x1=Tx->X.Log2Canvas(*IterX);
  IterY--;  y1=Tx->Y.Log2Canvas(*IterY);
  if(IterX==XValues->data.begin() || IterY==YValues->data.begin()) return;

  if(Tx->X.CanvasStart<Tx->X.CanvasEnd)
    {CeFixX=Tx->X.CanvasEnd; CsFixX=Tx->X.CanvasStart;}
  else
    {CsFixX=Tx->X.CanvasEnd; CeFixX=Tx->X.CanvasStart;}

  if(Tx->Y.CanvasStart<Tx->Y.CanvasEnd)
    {CeFixY=Tx->Y.CanvasEnd; CsFixY=Tx->Y.CanvasStart;}
  else
    {CsFixY=Tx->Y.CanvasEnd; CeFixY=Tx->Y.CanvasStart;}

	/* Loop that iterates all line segments. */
  while(IterX!=XValues->data.begin() && IterY!=YValues->data.begin())
    {
    IterX--;  x2=*IterX;
    IterY--;  y2=*IterY;	   	//load sample values   

    if(IsNaN(&x2))
        {x1=x2;y1=y2;continue;}		//if NaN is detected - paint nothing
    x2=Tx->X.Log2Canvas(x2);
    if(IsNaN(&x1))
        {x1=x2;y1=y2;continue;}

    if(IsNaN(&y2))
	{x1=x2;y1=y2;continue;}		//if NaN is detected - paint nothing
    y2=Tx->Y.Log2Canvas(y2);
    if(IsNaN(&y1))
        {x1=x2;y1=y2;continue;}    

		//normalise and fix X range
    if(x2>x1) {x1Fix=x1;x2Fix=x2;  y1Fix=y1;y2Fix=y2;}
         else {x1Fix=x2;x2Fix=x1;  y1Fix=y2;y2Fix=y1;}

    x1=x2;
    y1=y2;

    if(x1Fix<CsFixX)	// Goes x before painting area?
      {
      if(x2Fix<CsFixX) break;	   //skip painting of line before region
			//Move x1 position to Tx->X.CanvasStart
      if(abs(x2Fix-x1Fix)>1e-10)        
        y1Fix = y1Fix + (CsFixX-x1Fix)*(y1Fix-y2Fix)/(x1Fix-x2Fix);        
      else
        y1Fix = (y1Fix+y2Fix)/2;
      x1Fix = CsFixX;
      }

    if(x2Fix>CeFixX)	// Goes x behind painting area?
      {
      if(x1Fix>CeFixX) continue;	//exit painting, when end of paintable region was reached

                       //Move x2 position to Tx->X.CanvasEnd
      if(abs(x2Fix-x1Fix)>1e-10)        
        y2Fix = y1Fix + (CeFixX-x1Fix)*(y1Fix-y2Fix)/(x1Fix-x2Fix);         
      else
        y2Fix = (y1Fix+y2Fix)/2;
      x2Fix = CeFixX;
      }

	//normalise and fix Y range
    if(y1Fix>y2Fix)
      {
      double flip;
      flip=y1Fix; y1Fix=y2Fix; y2Fix=flip;
      flip=x1Fix; x1Fix=x2Fix; x2Fix=flip;
      }

    if(y2Fix>CeFixY)		// Goes y above painting area?
      {
      if(y1Fix>CeFixY) continue;	//whole line is above paintable area
	  /* Calculate cross of line with 'y=CeFixY' */
      if(abs(y2Fix-y1Fix)>1e-10)
        x2Fix = (x1Fix-x2Fix)*(CeFixY-y1Fix)/(y1Fix-y2Fix) + x1Fix;
      else
        x2Fix = (x1Fix+x2Fix)/2;
      y2Fix = CeFixY;      
      }

    if(y1Fix<CsFixY)		// Goes y below painting area?
      {
      if(y2Fix<CsFixY) continue;	//whole line is below paintable area
	  /* Calculate cross of line with 'y=CsFixY' */
      if(abs(y2Fix-y1Fix)>1e-10)
        x1Fix = (x1Fix-x2Fix)*(CsFixY-y1Fix)/(y1Fix-y2Fix) + x1Fix;
      else
        x1Fix = (x1Fix+x2Fix)/2;
      y1Fix = CsFixY;
      }
		// All done, now paint a line
    dc.DrawLine(x1Fix,y1Fix,x2Fix,y2Fix);
    }
}


/////// Implementation of circular buffer /////////

CircularBuffer::CircularBuffer(unsigned IniSize)
{
  array = (double*)calloc(IniSize,sizeof(double));
  Size = (array==NULL)?0:IniSize;
  First=Last=0;
}


CircularBuffer::~CircularBuffer()
{
  if(array)
    {
    free(array); array=NULL;
    }
  Size=First=Last=0;
}


void CircularBuffer::Push(double Value)
{
  CriticalSectionExtRWLocker Lck(*this);
  if(array==NULL) return;
  array[Last]=Value;
  Last = (Last+1)%Size;
}


double CircularBuffer::Pop(void)
{
double Ret=0;
  CriticalSectionExtRWLocker Lck(*this);
  if(array==NULL) return(Ret);
  Ret=array[First];
  if(First!=Last)
    First = (First+1)%Size;
  return Ret;
}


unsigned CircularBuffer::Elements(void)
{
  CriticalSectionExtROLocker Lck(*this);
  if(Last>=First) return Last-First;
  return Size - (First-Last);
}


void CircularBuffer::Clear(void)
{
  CriticalSectionExtRWLocker Lck(*this);
  First=Last=0;
}