
/* GraphClass by Marc Le Douarain */
/* based on example by Stefan Stuntz */


/*
** This is the instance data for our custom class.
*/

struct GraphClassData
{
	struct Str_AffiTraceGraph * GraphData;
};

#define SHOW_GRAPH 0x8022

void PrepCoeffForY(struct Str_AffiTraceGraph * Gfx,double ValMini,double ValMaxi,int YMini,int YMaxi)
{
	double a,b;
	a=(double)(YMaxi-YMini)/(double)(ValMaxi-ValMini);
	b=YMini-(a*ValMini);
  Gfx->CoeffAY=a;
	Gfx->CoeffBY=b;
}
int ConvertYValueToPix(struct Str_AffiTraceGraph *Gfx,double Val)
{
	int resY;
  resY=(int)(Gfx->CoeffAY*Val+Gfx->CoeffBY);
  return(resY);
}

void PrepCoeffForX(struct Str_AffiTraceGraph * Gfx,time_t ValMini,time_t ValMaxi,int XMini,int XMaxi)
{
	double a,b;
	a=(double)(XMaxi-XMini)/(double)(ValMaxi-ValMini);
	b=XMini-(a*ValMini);
	Gfx->CoeffAX=a;
	Gfx->CoeffBX=b;
}
int ConvertXValueToPix(struct Str_AffiTraceGraph *Gfx,time_t Val)
{
	int resX;
  resX=(int)(Gfx->CoeffAX*Val+Gfx->CoeffBX);
  return(resX);
}

void DrawDotted(struct RastPort * rp,int x_end,int y_end)
{
	int i,x_start,y_start,xtaille,ytaille;
	int steps;
	double c_x,c_y;
	x_start=rp->cp_x;
	y_start=rp->cp_y;
	ytaille=y_end-y_start;
	xtaille=x_end-x_start;
	if ( xtaille>=abs(ytaille) )
	{
		steps=xtaille;
		c_x=1;
		c_y=(double)(ytaille)/(double)steps;
	}
	else
	{
		steps=ytaille;
		c_y=1;
		c_x=(double)(xtaille)/(double)steps;
	}
	for(i=0;i<steps;i=i+3)
	{
		Move(rp,(int)(x_start+(double)i*c_x),(int)(y_start+(double)i*c_y));
		Draw(rp,(int)(x_start+(double)i*c_x),(int)(y_start+(double)i*c_y));
	}
}

time_t GetRound(time_t Val,time_t Pas)
{
	time_t T;
	T=Val/Pas;
	return(T*Pas+Pas);
}

int DrawYScale(struct RastPort * rp,int PxLeft,int PxYMini,int PxYMaxi,double ValYMini,double ValYMaxi)
{
	char tmp[50];
	char tmp2[50];
	int PxDateDeb;
  int DecalagePix;
	int DecalagePix2;
/*	SetAPen(_rp(obj),_dri(obj)->dri_Pens[HIGHLIGHTTEXTPEN]);*/
	sprintf(tmp,"%.4f",ValYMaxi);
	sprintf(tmp2,"%.4f",ValYMini);
	/* Length in pixels */
	DecalagePix=TextLength(rp,tmp,strlen(tmp));
	DecalagePix2=TextLength(rp,tmp2,strlen(tmp2));
	if (DecalagePix2>DecalagePix)
		DecalagePix=DecalagePix2;
	/* Displaying mini and maxi values for Y */
	Move(rp,PxLeft,PxYMaxi+2);
	Text(rp,tmp,strlen(tmp));
	Move(rp,PxLeft,PxYMini+4);
	Text(rp,tmp2,strlen(tmp2));
	PxDateDeb=PxLeft+DecalagePix+4;
	/* Drawing Y Axe */
	Move(rp,PxDateDeb,PxYMaxi);
	Draw(rp,PxDateDeb,PxYMini+3);
	return PxDateDeb;
}

void DrawXScale(struct RastPort * rp,struct Str_AffiTraceGraph *Gfx,int PxYMini,int PxDateDeb,int PxDateFin,time_t TpsDeb,time_t TpsFin)
{
	time_t DiffTps;
  time_t Tab[5][3]={{24*3600,3600,1},{2*24*3600,2*3600,1},{7*24*3600,24*3600,2},{31*24*3600,24*3600,3}};
	time_t PasTps=0;
	int AffiEchelleTps=0;
	time_t TpsI;
	int i;
/*	SetAPen(_rp(obj),_dri(obj)->dri_Pens[HIGHLIGHTTEXTPEN]);*/
	/* Scale */
	Move(rp,PxDateDeb-3,PxYMini);
	Draw(rp,PxDateFin,PxYMini);
	/* Size of time */
	DiffTps=TpsFin-TpsDeb;
	PasTps=0;
	for (i=0;i<=3;i++)
	{
		if(DiffTps==Tab[i][0])
		{
			PasTps=Tab[i][1];
			AffiEchelleTps=Tab[i][2];
		}
	}
	/* Graduation */
	if (PasTps)
	{
		TpsI=GetRound(TpsDeb,PasTps);
		do
		{
			int X;
			char TmpD[20],TmpH[20],Grad[50];
			X=ConvertXValueToPix(Gfx,TpsI);
			Move(rp,X,PxYMini-2);
			Draw(rp,X,PxYMini+2);
			convert_long_to_asciidate(TpsI,TmpD,TmpH);
			if (AffiEchelleTps==1)
			{
				if (TmpH[0]!='0')
				{
					Grad[0]=TmpH[0];
					Grad[1]=TmpH[1];
					Grad[2]='\0';
				}
				else
				{
					Grad[0]=TmpH[1];
					Grad[1]='\0';
				}
				Move(rp,X-6,PxYMini+4+8);
				Text(rp,Grad,strlen(Grad));					
			}
			if (AffiEchelleTps==2)
			{
				Grad[0]=TmpD[0];
				Grad[1]=TmpD[1];
				Grad[2]=TmpD[2];
				Grad[3]=TmpD[3];
				Grad[4]=TmpD[4];
				Grad[5]='\0';
				Move(rp,X+10,PxYMini+4+8);
				Text(rp,Grad,strlen(Grad));
			}
			if (AffiEchelleTps==3)
			{
				Grad[0]=TmpD[0];
				Grad[1]=TmpD[1];
				Grad[2]='\0';
				Move(rp,X+2,PxYMini+4+8);
				Text(rp,Grad,strlen(Grad));
			}
			TpsI=TpsI+PasTps;
		}
		while(TpsI<=TpsFin);
	}
}

/*
** OM_SET method, we need to see if someone want to display a graph.
*/
SAVEDS ULONG mSet(struct IClass *cl,Object *obj,Msg msg)
{
	struct GraphClassData *Data = INST_DATA(cl,obj);
	struct TagItem *tags,*tag;

	for (tags=((struct opSet *)msg)->ops_AttrList;tag=NextTagItem(&tags);)
	{
		switch (tag->ti_Tag)
		{
			case SHOW_GRAPH:
				if (tag->ti_Data)
				{
					Data->GraphData = ((struct Str_AffiTraceGraph *)tag->ti_Data);
					MUI_Redraw(obj,MADF_DRAWOBJECT); /* redraw ourselves completely */
				}
				break;
		}
	}

	return(DoSuperMethodA(cl,obj,msg));
}


/*
** AskMinMax method will be called before the window is opened
** and before layout takes place. We need to tell MUI the
** minimum, maximum and default size of our object.
*/
SAVEDS ULONG mAskMinMax(struct IClass *cl,Object *obj,struct MUIP_AskMinMax *msg)
{
	/*
	** let our superclass first fill in what it thinks about sizes.
	** this will e.g. add the size of frame and inner spacing.
	*/

	DoSuperMethodA(cl,obj,msg);

	/*
	** now add the values specific to our object. note that we
	** indeed need to *add* these values, not just set them!
	*/

	msg->MinMaxInfo->MinWidth  += 100;
	msg->MinMaxInfo->DefWidth  += 120;
	msg->MinMaxInfo->MaxWidth  += 1280;

	msg->MinMaxInfo->MinHeight += 40;
	msg->MinMaxInfo->DefHeight += 90;
	msg->MinMaxInfo->MaxHeight += 800;

	return(0);
}


/*
** Draw method is called whenever MUI feels we should render
** our object. This usually happens after layout is finished
** or when we need to refresh in a simplerefresh window.
** Note: You may only render within the rectangle
**       _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj).
*/

SAVEDS ULONG mDraw(struct IClass *cl,Object *obj,struct MUIP_Draw *msg)
{
	struct GraphClassData *Data = INST_DATA(cl,obj);
	struct Str_AffiTraceGraph *Graph;
	int i;
	int PxLeft;
  int PxDateDeb;
  int PxDateFin;
  int PxYMini;
  int PxYMaxi;

  double ValYMini=0;
  double ValYMaxi=0;

	PxLeft=_mleft(obj)+2;
	PxDateDeb=_mleft(obj)+12;
  PxDateFin=_mright(obj)-5;
	PxYMini=_mbottom(obj)-18;
	PxYMaxi=_mtop(obj)+5;

	Graph=Data->GraphData;
	/* Searching minis and maxis */
  if (Graph)
  {
		if (Graph->NbrSamples>0)
		{
			ValYMini=Graph->Valeurs[0];
			ValYMaxi=Graph->Valeurs[0];
			for(i=1;i<Graph->NbrSamples;i++)
			{
				if (ValYMini>Graph->Valeurs[i])
					ValYMini=Graph->Valeurs[i]; 
				if (ValYMaxi<Graph->Valeurs[i])
					ValYMaxi=Graph->Valeurs[i]; 
			}
  	} 
	}  

	/*
	** let our superclass draw itself first, area class would
	** e.g. draw the frame and clear the whole region. What
	** it does exactly depends on msg->flags.
	*/

	DoSuperMethodA(cl,obj,msg);

	/*
	** if MADF_DRAWOBJECT isn't set, we shouldn't draw anything.
	** MUI just wanted to update the frame or something like that.
	*/

	if (!(msg->flags & MADF_DRAWOBJECT))
		return(0);

	/*
	** ok, everything ready to render...
	*/


  if (Graph)
  {
		/* Drawing the scales */
SetAPen(_rp(obj),_dri(obj)->dri_Pens[HIGHLIGHTTEXTPEN]);
		if(Graph->NbrSamples>0)
		{
			PrepCoeffForY(Graph,ValYMini,ValYMaxi,PxYMini,PxYMaxi);
			PxDateDeb=DrawYScale(_rp(obj),PxLeft,PxYMini,PxYMaxi,ValYMini,ValYMaxi);
		}
		PrepCoeffForX(Graph,AffiGraphGene.Time32Deb,AffiGraphGene.Time32Fin,PxDateDeb,PxDateFin);
		DrawXScale(_rp(obj),Graph,PxYMini,PxDateDeb,PxDateFin,AffiGraphGene.Time32Deb,AffiGraphGene.Time32Fin);


  	/* Drawing the samples */
		if(Graph->NbrSamples>0)
		{
			int x,y;
			SetAPen(_rp(obj),_dri(obj)->dri_Pens[TEXTPEN]);
//			x=ConvertXValueToPix(TRUE,Graph->Horodates[0],AffiGraphGene.Time32Deb,AffiGraphGene.Time32Fin,PxDateDeb,PxDateFin);
//			y=ConvertYValueToPix(TRUE,Graph->Valeurs[0],ValYMini,ValYMaxi,PxYMini,PxYMaxi);
			x=ConvertXValueToPix(Graph,Graph->Horodates[0]);
			y=ConvertYValueToPix(Graph,Graph->Valeurs[0]);
  		Move(_rp(obj),x,y);
			for (i=1;i<Graph->NbrSamples;i++)
			{
//				x=ConvertXValueToPix(FALSE,Graph->Horodates[i],Graph->Horodates[0],Graph->Horodates[Graph->NbrSamples-1],PxDateDeb,PxDateFin);
//				y=ConvertYValueToPix(FALSE,Graph->Valeurs[i],ValYMini,ValYMaxi,PxYMini,PxYMaxi);
				x=ConvertXValueToPix(Graph,Graph->Horodates[i]);
				y=ConvertYValueToPix(Graph,Graph->Valeurs[i]);
				if ( (Graph->Horodates[i]-Graph->Horodates[i-1]) <= 60*60 )
					Draw(_rp(obj),x,y);
				else
					DrawDotted(_rp(obj),x,y);
			}
		}
	}

	return(0);
}


/*
** Here comes the dispatcher for our custom class. We only need to
** care about MUIM_AskMinMax and MUIM_Draw in this simple case.
** Unknown/unused methods are passed to the superclass immediately.
*/

#ifdef __GNUC__
SAVEDS ASM ULONG MyDispatcher(REG(a0,struct IClass *cl),REG(a2,Object *obj),REG(a1,Msg msg))
#else
SAVEDS ASM ULONG MyDispatcher(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg)
#endif
{
	switch (msg->MethodID)
	{
		case OM_SET         : return(mSet      (cl,obj,(APTR)msg));
		case MUIM_AskMinMax : return(mAskMinMax(cl,obj,(APTR)msg));
		case MUIM_Draw      : return(mDraw     (cl,obj,(APTR)msg));
	}

	return(DoSuperMethodA(cl,obj,msg));
}



