/****************************************************************************
  Module : Graph.cpp
  Contents : Method definitions for the grphWin and GraphPane classes.
  These functions control the display and behavior of a graph window.
****************************************************************************/
#include <stdio.h> //For sprintf
#include <math.h> //For sqrt
#include <v/vbrush.h>
#include <v/vcolor.h>
#include <v/vmenu.h>
#include <v/vfont.h>

#include "robapp.h"
#include "menus.h"
#include "proto.h"

/****************************************************************************
  Constructor: grphWin
  Parameters: name, the title of the window
  h, the height of the window
  w, the width of the window
  fd, the data set to be displayed by the window
****************************************************************************/

grphWin::grphWin(void)
	: vCmdWindow("Graph Display",700,400)
{
  mp = new vMenuPane(MainMenu);
  AddPane(mp);
  gp = new GraphPane();
  AddPane(gp);
  
  ShowWindow();
  gp->ShowVScroll(1);
  gp->ShowHScroll(1);
  gp->SetHScroll(20,50);
  gp->SetVScroll(10,0);

  gp->Resize(0,0);
}

/*******************************************************************************
  Destructor : grphWin
  *******************************************************************************/

grphWin::~grphWin(void)
{
  delete mp;
  delete gp;
}

void grphWin::CloseWin(void)
{
  myApp->CloseNotify(this);
  vCmdWindow::CloseWin();
}

void grphWin::SetDataSet(int x,int y) {
  gp->SetDataSet(x,y);
  if (myApp->Params.title[0] != '\0') {
    char title[40];
    sprintf(title,"Graph Display - %s",myApp->Params.title);
    SetTitle(title);
  }
  ShowWindow();
}

/****************************************************************************
  Constructor: GraphPane
  Parameters: fd, data set to be displayed.
****************************************************************************/

GraphPane::GraphPane(void)
	: vCanvasPane()
{
  //Setup values for displaying the window
  LeftMargin = 90;
  TopMargin = 10;
  FontHeight = 22;
  FontWidth = 6;
  Yextent = 1;
  Xextent = 1;
  Mark = 0.0;
  data1=0;
  data2=0;
}

/***************************************************************************
  Method: Resize
  Parameters: newH, the new height of the window
      	      newW, the new width of the window
  Purpose: Resize is called whenever the size of the window changes. It
   	   recalculates parameters for the graph. If it is called with
           newH == 0, it determines the height and width to use by itself.
           This kludge is used to properly set height and width before the
           first time the graph is displayed and after the canvas has been
           added to the window (Trust me, it has to work this way).
****************************************************************************/

void GraphPane::Resize(int newW, int newH)
{
  if (!newH) {
    Height = GetHeight();
    Width = GetWidth();
  }
  else {
    Height = newH;
    Width = newW;
  }

  BottomMargin = Height - 55;
  RightMargin = Width - 10;
  Xint = (RightMargin - LeftMargin) / Xextent;
  Yint = (BottomMargin - TopMargin) / Yextent;

  #ifdef __UNIX__
  if (newH) Redraw(0,0,0,0);
  #endif
}

/*****************************************************************************
    Method: SetDataSet
    Parameters: d1, the first data set, either orig or smooth
    		d2, model, if there is one
*****************************************************************************/
void GraphPane::SetDataSet(int d1, int d2)
{
  data1 = d1;
  data2 = d2;
  FailureDataSet* fd1;

  if (d1 == OrigData) fd1 = myApp->OrgData;
  else if (d1 == SmoothData) fd1 = myApp->SmthData;
  else if (d1 == ModelData) fd1 = myApp->ModData;

  Xextent = fd1->Failure_Data[fd1->Size - 1].time;

  //Yexent is one standard deviation from the average
  /*
 double avg = 0.0;
  for (int cnt = 0; cnt < fd1->Size; cnt++)
    avg += fd1->Failure_Data[cnt].intensity;
  avg /= fd1->Size;
  double std = 0.0;
  for (int cnt = 0; cnt < fd1->Size; cnt++)
    std += (avg - fd1->Failure_Data[cnt].intensity) *
      (avg - fd1->Failure_Data[cnt].intensity);
  std /= fd1->Size;
  std = sqrt(std);
  std *= 2.0;

  Yextent = 0.0;
  for (int cnt = 0; cnt < fd1->Size; cnt++)
    if ((fd1->Failure_Data[cnt].intensity <= avg + std) &&
	(fd1->Failure_Data[cnt].intensity > Yextent))
      Yextent = fd1->Failure_Data[cnt].intensity;
      */
  Yextent = fd1->Failure_Data[0].intensity;

for (int cnt = 1; cnt < fd1->Size; cnt++)
  if (fd1->Failure_Data[cnt].intensity > Yextent)
    Yextent = fd1->Failure_Data[cnt].intensity;

   Xint = (RightMargin - LeftMargin) / Xextent;
   Yint = (BottomMargin - TopMargin) / Yextent;
   Redraw(0,0,0,0);
}

/****************************************************************************
  Method : Redraw
  Parameters : sx, the left most column that needs to be redrawn
  sy, the top most row that needs to be redrawn
  height, the height of the area to be redrawn
  width, the width of the area to be redrawn
  Purpose : Redraw is called whenever the graph pane has to be redrawn
****************************************************************************/

void GraphPane::Redraw(int, int, int, int)
{
  //temp variables
  int x,y;
  int len;
  char str[128];
  if (!data1 && !data2) return;
  //SetBackground(vStdColors[vC_White]);
  Clear();
  vBrush mybrush;
  mybrush.SetColor(vStdColors[vC_MedGray]);
  SetBrush(mybrush);
  DrawRectangle(LeftMargin+2,TopMargin,RightMargin-LeftMargin-2,
		BottomMargin-TopMargin-2);

 //Print out the left hand label
  vFont myfont(vfFixed,14);
  SetFont(myfont);
  y = (BottomMargin - TopMargin) / 2 + TopMargin;
  DrawText(2,y - (FontHeight + 6) * 4,"  I");
  DrawText(2,y - (FontHeight + 6) * 3,"F N");
  DrawText(2,y - (FontHeight + 6) * 2,"A T");
  DrawText(2,y - (FontHeight + 6),"I E");
  DrawText(2,y,"L N");
  DrawText(2,y + (FontHeight + 6),"U S");
  DrawText(2,y + (FontHeight + 6) * 2,"R I");
  DrawText(2,y + (FontHeight + 6) * 3,"E T");
  DrawText(2,y + (FontHeight + 6) * 4,"  Y");

  myfont.SetFontValues(vfSerif,12);
  SetFont(myfont);
  //Print out bottom label
  x = (((Width - LeftMargin) / 2) - (FontWidth * 2)) + LeftMargin;
  y = BottomMargin + (FontHeight << 1);
  DrawText(x,y-15,"Time");

  y = Height - 5;
  x = LeftMargin + 2;
  double isect = 0.0;
  double bogus = 0.0; //Compute intersection of model, mark
  
  if (myApp->ModData)
    myApp->Params.predfunc(myApp->Params.beta0,myApp->Params.beta1,isect,Mark,bogus);
  len = sprintf(str,"Intensity: %.4f        Time: %.2f       Beta 0: %9e       Beta 1: %9e",
		Mark,isect,myApp->Params.beta0,myApp->Params.beta1);
  DrawText(x,y,str);

  //Print out the bottom scale
  x = LeftMargin - FontWidth * 5;
  y = BottomMargin + FontHeight;

  DrawText(x,y,"0.00");

  x = (RightMargin - LeftMargin) / 3 + LeftMargin;
  len = sprintf(str,"%.2f",Xextent/3);
  DrawText(x - (FontWidth * (len / 2))+3,y,str);
  DrawLine(x,BottomMargin - 2,x,BottomMargin + 4); //Tick line

  x = (RightMargin - LeftMargin) * 2/3 + LeftMargin;
  len = sprintf(str,"%.2f",Xextent * 2/3);
  DrawText(x - (FontWidth * (len / 2))+3,y,str);
  DrawLine(x,BottomMargin - 2,x,BottomMargin + 4); //Tick line

  x = RightMargin;
  len = sprintf(str,"%.2f",Xextent);
  DrawText(x - FontWidth * len,y,str);
  DrawLine(x,BottomMargin - 2,x,BottomMargin + 4); //Tick line

  //Print left hand scale
  y = (BottomMargin - TopMargin) * 2/3 + TopMargin;
  len = sprintf(str,"%.4f",Yextent / 3);
  x = LeftMargin - FontWidth * len - 4;
  DrawText(x,y + FontHeight / 2-5,str);
  DrawLine(LeftMargin - 3,y,LeftMargin + 3,y); //Tick line

  y = (BottomMargin - TopMargin) / 3 + TopMargin;
  len = sprintf(str,"%.4f",Yextent * 2/3);
  x = LeftMargin - FontWidth * len - 4;
  DrawText(x,y + FontHeight / 2-5,str);
  DrawLine(LeftMargin - 3,y,LeftMargin + 3,y); //Tick line

  y = TopMargin - FontHeight / 2;
  len = sprintf(str,"%.4f",Yextent);
  x = LeftMargin - FontWidth * len - 4;
  DrawText(x,y + FontHeight-2,str);
  DrawLine(LeftMargin - 3,TopMargin,LeftMargin + 3,TopMargin);

  //Finally, graph out each dat set
  vPen p1(0,0,128);
  SetPen(p1);
  PlotLine(data1);
  vPen p2(0,128,0);
  SetPen(p2);
  PlotLine(data2);
  vPen p3(100,0,0,1);
  SetPen(p3);
  y = int(BottomMargin - (Yint * Mark));
  DrawLine(LeftMargin+3,y,RightMargin-3,y);
}

/*****************************************************************************
  Method: PlotLine
  Purpose: PlotLine is called after the background for the graph has been
  drawn. It is responsible for drawing the actual graph.
*****************************************************************************/

void GraphPane::PlotLine(int ds)
{
  if (!ds) return;
  int xs,ys,xe,ye;
  FailureDataSet* fd;
  if (ds == OrigData) fd = myApp->OrgData;
  else if (ds == SmoothData) fd = myApp->SmthData;
  else if (ds == ModelData) fd = myApp->ModData;

  xs = int(LeftMargin + (fd->Failure_Data[0].time * Xint));
  ys = int(BottomMargin - (fd->Failure_Data[0].intensity * Yint));
  if (ys < TopMargin) ys = TopMargin;
  if (ys > BottomMargin) ys = BottomMargin;

  for (int cnt = 0; cnt < fd->Size - 1; cnt++) {
    xe = int(LeftMargin + (fd->Failure_Data[cnt + 1].time * Xint));
    ye = int(BottomMargin - (fd->Failure_Data[cnt + 1].intensity * Yint));
    if (xe > RightMargin) xe = RightMargin;
    if (ye < TopMargin) ye = TopMargin;
    if (ye > BottomMargin) ye = BottomMargin;
    DrawLine(xs,ys,xe,ye);
    xs = xe;
    if (xs > RightMargin) return;
    ys = ye;
  }
}

/*********************************************************************
  Method : VScroll
  Parameters : step, the amount of scrolling being done
  Purpose : When the users clicks on the up or down arrows of the
  vertical scroll bar in the graph pane, this function moves the
  intensity mark up or down.
*********************************************************************/

void GraphPane::VScroll(int step)
{
  int top,shown;
  GetVScroll(shown,top);
  SetVScroll(shown,top + step);
  Mark  = float(100 - top) / 100.0 * Yextent;
  Redraw(0,0,0,0);
}

/***********************************************************
  Method : VPage
  Parameters : shown, dummay variable for V toolkit
               top, the new place to put the intensity mark
  Purpose : Called by V when users adjust vertical scrollbar directly,
  this method adjusts the intensity mark.
*****************************************************************/
void GraphPane::VPage(int shown, int top)
{
  SetVScroll(shown,top);
  Mark = float(100 - top) / 100.0 * Yextent;
  Redraw(0,0,0,0);
}

/***********************************************************
  Method : HScroll
  Parameters : step, dummy variable used by V
  Purpose : To expand or contract a model as specified by the
  user moving the horizontal scroll bar.
*****************************************************************/
void GraphPane::HScroll(int step)
{
  double tmp1,tmp2;

  if (!(data1 == ModelData || data2 == ModelData)) return;
  int top,shown;
  GetHScroll(shown,top);
  if (step > 0) {
    if (!ExpandModel(myApp->OrgData,myApp->ModData,myApp->Params.beta0,
		     myApp->Params.beta1,myApp->Params.modfunc)) {
      Message("Out of Memory!");
      myApp->Exit();
    }
    if (myApp->win1->GetValue(m_Recal)) {
      tmp1 = 0.0;
      if (myApp->Params.mtype == Logarithmic) tmp1 = myApp->StatMet.Alpha;
      Recalibrate(myApp->OrgData,myApp->ModData,
		  myApp->Params.modfunc,myApp->Params.fitfunc,tmp1);
    }
  }
  else if (step < 0)
   ShrinkModel(myApp->ModData);
  SetDataSet(ModelData,data2);
  myApp->win2->SetDataSet(ModelData);
  if (myApp->win7) myApp->win7->SetDataSet(ModelData,data2);
}
