/***************************************************************************
* graphbuf.h
*
*  array and carray type graph data buffer
*
*	buf  << x << y << "\n";
*
*	buf  << "title of graph"
*            << x  << "xunit"           << xmin | xmax << LOG
*     	     << y1 << "name of y1 data" << ymin | ymax << LOG
*     	     << y2 << "name of y2 data"  // multiple y axis data(optional)
*     	     << y3 << "name of y3 data"  //
*     	     << "yunit\n" ;              // must end with \n
*
*       plot << buf ;
*       arrayostream("fname") << buf ;
*       arrayostream("fname") >> buf ;
*
****************************************************************************/


#ifndef G__GRAPHBUF_H
#define G__GRAPHBUF_H

#ifndef G__ARRAY_H
class array;
#endif

#ifndef G__CARRAY_H
class carray;
#endif

#define G__ARRAYMAX 100

typedef unsigned char ISLOG;
ISLOG LIN=0;  // log flag linear
ISLOG LOG=1;  // log flag log

/**********************************************************
* graphbuf
**********************************************************/
class graphbuf {
public:
  enum DATATYPE { G__ARRAYTYPE, G__CARRAYTYPE, G__CARRAYTYPE_RE, G__CARRAYTYPE_IM };
  enum GRSTATUS { G__GRAPHBUF_PUSHING , G__GRAPHBUF_FIXED };

private:
  // data member
  int minn;                     // minimum of n[]
  int pout;                     // output pointer
  int status;                   // input status
  int iden[G__ARRAYMAX];        // identity, array, carray re,im
  int nplot;                    // number of data, input pointer
  int xlog,ylog;                // log flag
  double xmin,xmax,ymin,ymax;   // scale
  int n[G__ARRAYMAX];           // array of number of data
  
  char *title;                  // title of graph
  char *yunit;                  // y axis unit
  
  char *dataname[G__ARRAYMAX];  // name of data
public:
  double *pdat[G__ARRAYMAX];    // pointer to data array
  
public:
  // constructor, destructor, initialization
  graphbuf(void);
  ~graphbuf(void);
  void setnull(void);
  void freebuf(void);
  
  // assignment operator
  graphbuf& operator =(graphbuf& X);
  
  // ostream pipeline operator
  graphbuf& operator <<(array& a);
  graphbuf& operator <<(carray& a);
  graphbuf& operator <<(char *s);    // give name or title+do plot
  graphbuf& operator <<(double min); // specify min scale
  graphbuf& operator  |(double max); // specify max scale
  graphbuf& operator <<(ISLOG log);  // specify log scale
  virtual graphbuf& operator <<(char c);  // do plot
  
  // istream pipeline operator
  graphbuf& operator >>(array& a);
  graphbuf& operator >>(carray& a);
  graphbuf& operator >>(char *s);    // give name or title+do plot
  graphbuf& operator >>(double min); // dummy
  graphbuf& operator >>(ISLOG log);  // dummy
  graphbuf& operator >>(char c);     // 
  
  int isterminate(void) {
    if(G__GRAPHBUF_FIXED==status) return(1);
    else                          return(0);
  }
  
  int Nplot(void)   { return(nplot); }
  int Npoint(int i) { return(n[i]); }
  int Minn(void)    { return(minn); }
  char *Name(int i) { return(dataname[i]); }
  char *Title(void) { return(title); }
  char *Yunit(void) { return(yunit); }
  double *Pdat(int i) { return(pdat[i]); }
  double Dat(int i,int n) { return(pdat[i][n]); }
  int Xlog(void)    { return(xlog); }
  int Ylog(void)    { return(ylog); }
  double Xmin(void) { return(xmin); }
  double Xmax(void) { return(xmax); }
  double Ymin(void) { return(ymin); }
  double Ymax(void) { return(ymax); }
  int Pout(void) { return(pout); }
  int Status(void) { return(status); }
  int Iden(int i)  { return(iden[i]); }
  void setStatus(int stat) { status = stat; }
  
  void dumpdata(FILE *fp);
  void loaddata(FILE *fp);
} ;


// constructor
graphbuf::graphbuf(void)
{
  setnull();
  pout=0;
}

// initialization
void graphbuf::setnull(void)
{
	int i;
	for(i=0;i<G__ARRAYMAX;i++) {
		pdat[i] = NULL;
		n[i] = 0;
		dataname[i] = NULL;
	}
	title=NULL;
	yunit=NULL;
	nplot=0;
	status=G__GRAPHBUF_PUSHING;
	minn=2000000;
	xmin=xmax=ymin=ymax=0;
	xlog=ylog=0;
}

void graphbuf::freebuf(void)
{
	int i;
	for(i=nplot-1;i>=0;i--) {
		if(dataname[i]) free(dataname[i]);
		if(pdat[i])     free(pdat[i]);
	}
	if(yunit) free(yunit);
	if(title) free(title);
	setnull();
}


// destructor
graphbuf::~graphbuf(void)
{
	freebuf();
}


/**************************************************************************
* operator overloading
**************************************************************************/
graphbuf& graphbuf::operator =(graphbuf& a)
{
	int i;
	if(G__GRAPHBUF_FIXED==status) {
		freebuf();
	}
	if(title) free(title);
	if(a.Title()) {
		title=malloc(strlen(a.Title())+1);
		strcpy(title,a.Title());
	}
	else title=NULL;

	if(yunit) free(yunit);
	if(a.Yunit()) {
		yunit=malloc(strlen(a.Yunit())+1);
		strcpy(yunit,a.Yunit());
	}
	else yunit=NULL;
	
	minn=a.Minn();
	xlog=a.Xlog();
	ylog=a.Ylog();
	xmin=a.Xmin();
	xmax=a.Xmax();
	ymin=a.Ymin();
	ymax=a.Ymax();
	nplot=a.Nplot();
	pout=a.Pout();
	status=a.Status();

	for(i=0;i<nplot;i++) {
		n[i]=a.Npoint(i);
		iden[i]=a.Iden(i);

		if(pdat[i]) free(pdat[i]);
		pdat[i] = malloc(n[i]*sizeof(double));
		memcpy((char*)pdat[i],(char*)a.pdat[i],n[i]*sizeof(double));

		if(dataname[i]) free(dataname);
		if(a.Name(i)) {
			dataname[i]=malloc(strlen(a.Name(i))+1);
			strcpy(dataname[i],a.Name(i));
		}
		else dataname[i]=NULL;
	}
	return(*this);
}


// add array or carray to plot
graphbuf& graphbuf::operator <<(array& a)
{
	if(G__GRAPHBUF_FIXED==status) {
		freebuf();
	}
	if(pdat[nplot]) free(pdat[nplot]);
	pdat[nplot] = malloc(a.n*sizeof(double));
	memcpy((char*)pdat[nplot],(char*)a.dat,a.n*sizeof(double));
	iden[nplot]=G__ARRAYTYPE;
	n[nplot]=a.n;
	if(minn>a.n) minn=a.n;
	++nplot;
	return(*this);
}

graphbuf& graphbuf::operator <<(carray& a)
{
	if(G__GRAPHBUF_FIXED==status) {
		freebuf();
	}
	// real part
	if(pdat[nplot]) free(pdat[nplot]);
	pdat[nplot] = malloc(a.n*sizeof(double));
	memcpy((char*)pdat[nplot],(char*)a.re,a.n*sizeof(double));
	iden[nplot]=G__CARRAYTYPE_RE;
	n[nplot]=a.n;
	if(minn>a.n) minn=a.n;
	++nplot;
	// imaginary part
	if(pdat[nplot]) free(pdat[nplot]);
	pdat[nplot] = malloc(a.n*sizeof(double));
	memcpy((char*)pdat[nplot],(char*)a.im,a.n*sizeof(double));
	iden[nplot]=G__CARRAYTYPE_IM;
	n[nplot]=a.n;
	++nplot;
	return(*this);
}


// add min scale information
graphbuf& graphbuf::operator <<(double min)
{
	if(1==nplot) { // on x data
		xmin=min;
	}
	else { // on y data
		ymin=min;
	}
	return(*this);
}

// add max scale information
graphbuf& graphbuf::operator |(double max)
{
	if(G__GRAPHBUF_PUSHING==status) {
		if(1==nplot) { // on x data
			xmax=max;
		}
		else { // on y data
			ymax=max;
		}
	}
	return(*this);
}

// add log information
graphbuf& graphbuf::operator <<(ISLOG log)
{
	if(1==nplot) xlog = (int)log;
	else         ylog = (int)log;
	return(*this);
}

// add title of plot
graphbuf& graphbuf::operator <<(char *s)
{
	if(G__GRAPHBUF_FIXED==status) {
		freebuf();
	}
	if(strcmp(s,"\n")==0) {
		*this << '\n';
	}
	else if(strlen(s)>0 && s[strlen(s)-1]=='\n') {
		if(yunit) free(yunit);
		yunit=malloc(strlen(s)+1);
		sprintf(yunit,"%s",s);
		yunit[strlen(s)-1]='\0';
		*this << '\n';
	}
	else {
		if(nplot>0) {
			if(G__ARRAYTYPE==iden[nplot-1]) {
				if(dataname[nplot-1]) free(dataname[nplot-1]);
				dataname[nplot-1]=malloc(strlen(s)+1);
				strcpy(dataname[nplot-1],s);
			}
			else {
				if(dataname[nplot-2]) free(dataname[nplot-2]);
				dataname[nplot-2]=malloc(strlen(s)+5);
				sprintf(dataname[nplot-2],"%s(re)",s);
				if(dataname[nplot-1]) free(dataname[nplot-1]);
				dataname[nplot-1]=malloc(strlen(s)+5);
				sprintf(dataname[nplot-1],"%s(im)",s);
			}
		}
		else  {
			if(title) free(title);
			title=malloc(strlen(s)+1);
			strcpy(title,s);
		}
	}
	return(*this);
}

// do plot or print
graphbuf& graphbuf::operator <<(char c)
{
	status=G__GRAPHBUF_FIXED;
	return(*this);
}


/**********************************************************
* output
**********************************************************/

// add array or carray 
graphbuf& graphbuf::operator >>(array& a)
{
	if(pout>nplot) {
		cerr << "Error: no more data in graphbuf\n";
		return(*this);
	}
	a = array(pdat[pout],n[pout]);
	++pout;
	return(*this);
}

graphbuf& graphbuf::operator >>(carray& a)
{
	if(pout>nplot-1) {
		cerr << "Error: no more data in graphbuf\n";
		return(*this);
	}
	a = carray(pdat[pout],pdat[pout+1],n[pout]);
	pout+=2;
	return(*this);
}

// dummy 
graphbuf& graphbuf::operator >>(double d)
{
	return(*this);
}

#ifdef NEVER
graphbuf& graphbuf::operator |(double d)
{
	return(*this);
}
#endif

graphbuf& graphbuf::operator >>(ISLOG log)
{
	return(*this);
}

// reset ouput pointer
graphbuf& graphbuf::operator >>(char *s)
{
	pout=0;
	return(*this);
}

graphbuf& graphbuf::operator >>(char c)
{
	pout=0;
	return(*this);
}


/********************************************************************
*  binary dump
********************************************************************/
void graphbuf::dumpdata(FILE *fp)
{
	int i;
	int len;

	fwrite(&nplot,sizeof(nplot),1,fp);
	fwrite(&xlog,sizeof(xlog),1,fp);
	fwrite(&ylog,sizeof(ylog),1,fp);
	fwrite(&xmin,sizeof(xmin),1,fp);
	fwrite(&xmax,sizeof(xmax),1,fp);
	fwrite(&ymin,sizeof(ymin),1,fp);
	fwrite(&ymax,sizeof(ymax),1,fp);
	fwrite(&minn,sizeof(minn),1,fp);
	fwrite(&pout,sizeof(pout),1,fp);
	fwrite(&status,sizeof(status),1,fp);
	fwrite(&n,sizeof(n),1,fp);
	fwrite(&iden,sizeof(iden),1,fp);

	if(title) len=strlen(title);
	else      len=0;
	fwrite(&len,sizeof(len),1,fp);
	if(len) fwrite(title,len+1,1,fp);
	

	if(yunit) len=strlen(yunit);
	else      len=0;
	fwrite(&len,sizeof(len),1,fp);
	if(len) fwrite(yunit,len+1,1,fp);

	for(i=0;i<nplot;i++) {
		fwrite(pdat[i],n[i]*sizeof(double),1,fp);

		if(dataname[i]) len=strlen(dataname[i]);
		else            len=0;
		fwrite(&len,sizeof(len),1,fp);
		if(len) fwrite(dataname[i],len+1,1,fp);
	}
}


/********************************************************************
*  binary load
********************************************************************/
void graphbuf::loaddata(FILE *fp)
{
	int i;
	int len;

	fread(&nplot,sizeof(nplot),1,fp);
	fread(&xlog,sizeof(xlog),1,fp);
	fread(&ylog,sizeof(ylog),1,fp);
	fread(&xmin,sizeof(xmin),1,fp);
	fread(&xmax,sizeof(xmax),1,fp);
	fread(&ymin,sizeof(ymin),1,fp);
	fread(&ymax,sizeof(ymax),1,fp);
	fread(&minn,sizeof(minn),1,fp);
	fread(&pout,sizeof(pout),1,fp);
	fread(&status,sizeof(status),1,fp);
	fread(&n,sizeof(n),1,fp);
	fread(&iden,sizeof(iden),1,fp);

	fread(&len,sizeof(len),1,fp);
	if(title) {
		free(title);
		title=NULL;
	}
	if(len) {
		title=malloc(len+1);
		fread(title,len+1,1,fp);
	}

	fread(&len,sizeof(len),1,fp);
	if(yunit) {
		free(yunit);
		yunit=NULL;
	}
	if(len) {
		yunit=malloc(len+1);
		fread(yunit,len+1,1,fp);
	}

	for(i=0;i<nplot;i++) {
		if(pdat[i]) free(pdat[i]);
		pdat[i]=malloc(n[i]*sizeof(double));
		fread(pdat[i],n[i]*sizeof(double),1,fp);

		fread(&len,sizeof(len),1,fp);
		if(dataname[i]) {
			free(dataname[i]);
			dataname[i]=NULL;
		}
		if(len) {
			dataname[i]=malloc(len+1);
			fread(dataname[i],len+1,1,fp);
		}
	}
}

#endif
