// Copyright (C) 1999-2015
// Smithsonian Astrophysical Observatory, Cambridge, MA, USA
// For conditions of distribution and use, see copyright notice in "copyright"

#include <tk.h>

#include "cpanda.h"
#include "fitsimage.h"

Cpanda::Cpanda(const Cpanda& a) : BasePanda(a), BaseEllipse(a) {}

Cpanda::Cpanda(Base* p, const Vector& ctr, 
	       double a1, double a2, int an,
	       double r1, double r2, int rn,
	       const char* clr, int* dsh, 
	       int wth, const char* fnt, const char* txt, 
	       unsigned short prop, const char* cmt, 
	       const List<Tag>& tg, const List<CallBack>& cb)
  : BasePanda(a1, a2, an), 
    BaseEllipse(p, ctr, 0, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb)
{
  numAnnuli_ = rn+1;
  annuli_ = new Vector[numAnnuli_];

  for (int ii=0; ii<numAnnuli_; ii++) {
    double r = ii*(r2-r1)/rn+r1;
    annuli_[ii] = Vector(r,r);
  }

  strcpy(type_, "panda");
  numHandle = 4 + numAnnuli_ + numAngles_;

  startAng_ = angles_[0];
  stopAng_ = angles_[numAngles_-1];

  updateBBox();
}

Cpanda::Cpanda(Base* p, const Vector& ctr, 
	       int an, double* a,
	       int rn, double* r,
	       const char* clr, int* dsh, 
	       int wth, const char* fnt, const char* txt, 
	       unsigned short prop, const char* cmt, 
	       const List<Tag>& tg, const List<CallBack>& cb)
  : BasePanda(an, a),
    BaseEllipse(p, ctr, 0, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb)
{
  numAnnuli_ = rn;
  annuli_ = new Vector[numAnnuli_];

  for (int ii=0; ii<numAnnuli_; ii++)
    annuli_[ii] = Vector(r[ii],r[ii]);
  sortAnnuli();

  strcpy(type_, "panda");
  numHandle = 4 + numAnnuli_ + numAngles_;

  startAng_ = angles_[0];
  stopAng_ = angles_[numAngles_-1];

  updateBBox();
}

void Cpanda::renderX(Drawable drawable, Coord::InternalSystem sys, RenderMode mode)
{
  BaseEllipse::renderX(drawable, sys, mode);

  GC lgc = renderXGC(mode);

  Vector r0 = annuli_[0];
  Vector r1 = annuli_[numAnnuli_-1];

  for (int ii=0; ii<numAngles_; ii++) {
    Vector rr0 = fwdMap(Vector(r0[0]*cos(-angles_[ii]),r0[1]*sin(-angles_[ii])),
			sys);
    Vector rr1 = fwdMap(Vector(r1[0]*cos(-angles_[ii]),r1[1]*sin(-angles_[ii])),
			sys);

    if (mode == SRC) {
      if (selected) {
	if (ii == 0)
	  XSetForeground(display, gc, parent->getColor("red"));
	else if (ii == numAngles_-1)
	  XSetForeground(display, gc, parent->getColor("blue"));
	else
	  XSetForeground(display, gc, color);
      }
      else
	  XSetForeground(display, gc, color);
    }

    XDrawLine(display, drawable, lgc, rr0[0], rr0[1], rr1[0], rr1[1]);    
  }
}

void Cpanda::renderPS(int mode)
{
  BaseEllipse::renderPS(mode);

  Vector r0 = annuli_[0];
  Vector r1 = annuli_[numAnnuli_-1];

  for (int ii=0; ii<numAngles_; ii++) {
    Vector rr0 = fwdMap(Vector(r0[0]*cos(-angles_[ii]),r0[1]*sin(-angles_[ii])),
			Coord::CANVAS);
    Vector rr1 = fwdMap(Vector(r1[0]*cos(-angles_[ii]),r1[1]*sin(-angles_[ii])),
			Coord::CANVAS);

    ostringstream str;
    str << "newpath " 
    	<< rr0.TkCanvasPs(parent->canvas) << "moveto"
    	<< rr1.TkCanvasPs(parent->canvas) << "lineto"
    	<< " stroke" << endl << ends;
    Tcl_AppendResult(parent->interp, str.str().c_str(), NULL);
  }
}

#ifdef MAC_OSX_TK
void Cpanda::renderMACOSX()
{
  BaseEllipse::renderMACOSX();

  Vector r0 = annuli_[0];
  Vector r1 = annuli_[numAnnuli_-1];

  for (int ii=0; ii<numAngles_; ii++) {
    Vector rr0 = fwdMap(Vector(r0[0]*cos(-angles_[ii]),r0[1]*sin(-angles_[ii])),
			Coord::CANVAS);
    Vector rr1 = fwdMap(Vector(r1[0]*cos(-angles_[ii]),r1[1]*sin(-angles_[ii])),
			Coord::CANVAS);

    macosxDrawLine(rr0,rr1);
  }
}
#endif

#ifdef __WIN32
void Cpanda::renderWIN32()
{
  BaseEllipse::renderWIN32();

  Vector r0 = annuli_[0];
  Vector r1 = annuli_[numAnnuli_-1];

  for (int ii=0; ii<numAngles_; ii++) {
    Vector rr0 = fwdMap(Vector(r0[0]*cos(-angles_[ii]),
					 r0[1]*sin(-angles_[ii])),
				  Coord::CANVAS);
    Vector rr1 = fwdMap(Vector(r1[0]*cos(-angles_[ii]),
					 r1[1]*sin(-angles_[ii])),
				  Coord::CANVAS);

    win32DrawLine(rr0,rr1);
  }
}
#endif

// Support

void Cpanda::updateHandles()
{
  // handles are in canvas coords
  // we can't garantee that the annuli_ have been sorted yet
  if (handle)
    delete [] handle;
  handle = new Vector[numHandle];

  Vector max;
  for(int ii=0; ii<numAnnuli_; ii++)
    if (max[0]<annuli_[ii][0])
      max = annuli_[ii];
  Vector& r = max;

  handle[0] = fwdMap(Vector(-r[0],-r[1]),Coord::CANVAS);
  handle[1] = fwdMap(Vector( r[0],-r[1]),Coord::CANVAS);
  handle[2] = fwdMap(Vector( r[0], r[1]),Coord::CANVAS);
  handle[3] = fwdMap(Vector(-r[0], r[1]),Coord::CANVAS);

  for (int ii=0; ii<numAnnuli_; ii++)
    handle[ii+4] = fwdMap(Vector(annuli_[ii][0],0),Coord::CANVAS);

  Vector rr = annuli_[numAnnuli_-1];
  for (int ii=0; ii<numAngles_; ii++)
    handle[4+numAnnuli_+ii] = 
      fwdMap(Vector(rr[0]*cos(-angles_[ii]),rr[1]*sin(-angles_[ii])),Coord::CANVAS);
}

void Cpanda::edit(const Vector& v, int h)
{
  Matrix mm = bckMatrix();

  if (h<5) {
    // calc dist between edge of circle and handle
    double d = annuli_[numAnnuli_-1].length() - annuli_[numAnnuli_-1][0];

    for (int i=0; i<numAnnuli_; i++) {
      double r = ((v * mm).length() - d)/annuli_[numAnnuli_-1][0];
      annuli_[i] *= r;
    }
  }
  else if (h<(5+numAnnuli_)) {
    double d = (v * mm).length();
    annuli_[h-5] = Vector(d,d);
  }
  else {
    angles_[h-5-numAnnuli_] = -((v * mm).angle());
    sortAngles();
    startAng_ = angles_[0];
    stopAng_ = angles_[numAngles_-1];
  }
  
  updateBBox();
  doCallBack(CallBack::EDITCB);
}

void Cpanda::editEnd()
{
  sortAnnuli();
  sortAngles();
  startAng_ = angles_[0];
  stopAng_ = angles_[numAngles_-1];

  updateBBox();
  doCallBack(CallBack::EDITENDCB);
}

int Cpanda::addAnnuli(const Vector& v)
{
  Matrix mm = bckMatrix();
  double l = (v * mm).length();

  // we need to insert into the next to the last location
  // new size array
  Vector* old = annuli_;
  annuli_ = new Vector[numAnnuli_+1];

  // copy old values
  for (int i=0; i<numAnnuli_; i++)
    annuli_[i] = old[i];

  // save last
  annuli_[numAnnuli_] = old[numAnnuli_-1];

  // delete old
  if (old)
    delete [] old;

  // new size on end
  annuli_[numAnnuli_-1] = Vector(l,l);

  numAnnuli_++;
  numHandle++;

  // return handle number
  return 4+numAnnuli_-1;
}

int Cpanda::addAngles(const Vector& v)
{
  Matrix mm = bckMatrix();
  addAngle(-((v * mm).angle()));
  numHandle++;

  // return handle number
  return 4+numAnnuli_+numAngles_-1;
}

void Cpanda::setAnglesAnnuli(double a1, double a2, int an, 
			    Vector r1, Vector r2, int rn)
{
  numAnnuli_ = rn+1;
  if (annuli_)
    delete [] annuli_;
  annuli_ = new Vector[numAnnuli_];

  for (int i=0; i<numAnnuli_; i++)
    annuli_[i] = ((r2-r1)/rn)*i+r1;

  sortAnnuli();
  setAngles(a1,a2,an);
  startAng_ = angles_[0];
  stopAng_ = angles_[numAngles_-1];

  numHandle = 4 + numAnnuli_ + numAngles_;

  updateBBox();
  doCallBack(CallBack::EDITCB);
}

void Cpanda::setAnglesAnnuli(const double* a, int an, const Vector* r, int rn)
{
  numAnnuli_ = rn;
  if (annuli_)
    delete [] annuli_;
  annuli_ = new Vector[numAnnuli_];

  for (int i=0; i<numAnnuli_; i++)
    annuli_[i] = r[i];

  sortAnnuli();
  setAngles(an,a);
  startAng_ = angles_[0];
  stopAng_ = angles_[numAngles_-1];

  numHandle = 4 + numAnnuli_ + numAngles_;

  updateBBox();
  doCallBack(CallBack::EDITCB);
}

void Cpanda::deleteAnglesAnnuli(int h)
{
  if (h>4) {
    int hh = h-4-1;

    if (numAnnuli_>2 && hh<numAnnuli_) {
      // new annuli_ array
      Vector* old = annuli_;
      annuli_ = new Vector[numAnnuli_-1];

      // copy up to annuli_ in question
      for (int i=0; i<hh; i++)
	annuli_[i] = old[i];

      // copy remainder
      for (int i=hh; i<numAnnuli_-1; i++)
	annuli_[i] = old[i+1];

      if (old)
	delete [] old;
      numAnnuli_--;
    }
    else if (numAngles_>2 && hh<(numAnnuli_+numAngles_)) {
      hh -= numAnnuli_;
      deleteAngle(hh);
    }

    numHandle = 4 + numAnnuli_ + numAngles_;

    startAng_ = angles_[0];
    stopAng_ = angles_[numAngles_-1];

    updateBBox();
    doCallBack(CallBack::EDITCB);
  }
}

int Cpanda::isIn(const Vector& vv, Coord::InternalSystem sys, int nn, int aa)
{
  Vector pp = bckMap(vv,sys);
  return BaseEllipse::isIn(vv,sys,nn) && BasePanda::isIn(pp,aa);
}

void Cpanda::analysis(AnalysisTask mm, int which)
{
  switch (mm) {
  case PANDA:
    if (!analysisPanda_ && which) {
      addCallBack(CallBack::MOVECB, analysisPandaCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::EDITCB, analysisPandaCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::EDITENDCB, analysisPandaCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::UPDATECB, analysisPandaCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::DELETECB, analysisPandaCB_[1], 
		  parent->options->cmdName);
    }
    if (analysisPanda_ && !which) {
      deleteCallBack(CallBack::MOVECB, analysisPandaCB_[0]);
      deleteCallBack(CallBack::EDITCB, analysisPandaCB_[0]);
      deleteCallBack(CallBack::EDITENDCB, analysisPandaCB_[0]);
      deleteCallBack(CallBack::UPDATECB, analysisPandaCB_[0]);
      deleteCallBack(CallBack::DELETECB, analysisPandaCB_[1]);
    }

    analysisPanda_ = which;
    break;
  case STATS:
    if (!analysisStats_ && which) {
      addCallBack(CallBack::MOVECB, analysisStatsCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::EDITCB, analysisStatsCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::EDITENDCB, analysisStatsCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::UPDATECB, analysisStatsCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::DELETECB, analysisStatsCB_[1], 
		  parent->options->cmdName);
    }
    if (analysisStats_ && !which) {
      deleteCallBack(CallBack::MOVECB, analysisStatsCB_[0]);
      deleteCallBack(CallBack::EDITCB, analysisStatsCB_[0]);
      deleteCallBack(CallBack::EDITENDCB, analysisStatsCB_[0]);
      deleteCallBack(CallBack::UPDATECB, analysisStatsCB_[0]);
      deleteCallBack(CallBack::DELETECB, analysisStatsCB_[1]);
    }

    analysisStats_ = which;
    break;
  default:
    // na
    break;
  }
}

void Cpanda::analysisPanda(Coord::CoordSystem sys)
{
  double* xx;
  double* yy;
  double* ee;

  BBox* bb = new BBox[numAnnuli_];
  for (int ii=0; ii<numAnnuli_; ii++) {
    Vector ll = -annuli_[ii] * Translate(center);
    Vector ur =  annuli_[ii] * Translate(center);
    bb[ii] = BBox(ll,ur) ;
  }

  int num = parent->markerAnalysisPanda(this, &xx, &yy, &ee, 
					 numAnnuli_-1, annuli_, 
					 numAngles_-1, angles_, 
					 bb, sys);
  analysisXYEResult(xx, yy, ee, num);
}

void Cpanda::analysisStats(Coord::CoordSystem sys, Coord::SkyFrame sky)
{
  ostringstream str;
  BBox* bb = new BBox[numAnnuli_];
  for (int ii=0; ii<numAnnuli_; ii++) {
    Vector ll = -annuli_[ii] * Translate(center);
    Vector ur =  annuli_[ii] * Translate(center);
    bb[ii] = BBox(ll,ur) ;
  }
  parent->markerAnalysisStats(this, str, numAnnuli_-1, numAngles_-1, bb, 
			      sys, sky);
  Tcl_AppendResult(parent->interp, str.str().c_str(), NULL);
}

// list
void Cpanda::list(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky,
		 Coord::SkyFormat format, int conj, int strip)
{
  int regular = 1;
  if (numAngles_>2) {
    double delta;
    if (angles_[1] > angles_[0])
      delta = angles_[1]-angles_[0];
    else
      delta = angles_[1]+M_TWOPI-angles_[0];

    for (int ii=2; ii<numAngles_; ii++) {
      double diff;
      if (angles_[ii] > angles_[ii-1])
	diff = angles_[ii]-angles_[ii-1];
      else
	diff = angles_[ii]+M_TWOPI-angles_[ii-1];

      if (!teq(diff,delta,FLT_EPSILON)) {
	regular = 0;
	break;
      }
    }
  }

  if (numAnnuli_>2) {
    double delta = annuli_[1][0]-annuli_[0][0];
    for (int i=2; i<numAnnuli_; i++) {
      double diff = annuli_[i][0]-annuli_[i-1][0];
      if (!teq(diff,delta,FLT_EPSILON)) {
	regular = 0;
	break;
      }
    }
  }

  if (regular)
    listA(str, sys, sky, format, conj, strip);
  else
    listB(str, sys, sky, format, conj, strip);
}

void Cpanda::listA(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky,
		  Coord::SkyFormat format, int conj, int strip)
{
  FitsImage* ptr = parent->findFits(sys,center);
  listPre(str, sys, sky, ptr, strip, 0);

  switch (sys) {
  case Coord::IMAGE:
  case Coord::PHYSICAL:
  case Coord::DETECTOR:
  case Coord::AMPLIFIER:
    {
      double ang1 = radToDeg(parent->mapAngleFromRef(angles_[0],sys));
      double ang2 = radToDeg(parent->mapAngleFromRef(angles_[numAngles_-1],sys));
      if (ang2<=ang1+FLT_EPSILON)
	ang2 += 360;

      Vector v = ptr->mapFromRef(center,sys);
      str << type_ << '(' << setprecision(8) << v[0] << ',' << v[1] << ','
	  << ang1 << ',' << ang2 << ',' << numAngles_-1 << ','
	  << ptr->mapLenFromRef(annuli_[0][0],sys) << ','
	  << ptr->mapLenFromRef(annuli_[numAnnuli_-1][0],sys) << ','
	  << numAnnuli_-1 << ')';
    }
    break;
  default:
    if (ptr->hasWCS(sys)) {
      if (ptr->hasWCSCel(sys)) {
	switch (format) {
	case Coord::DEGREES:
	  {
	    double ang1 = radToDeg(parent->mapAngleFromRef(angles_[0],sys,sky));
	    double ang2 = radToDeg(parent->mapAngleFromRef(angles_[numAngles_-1],sys,sky));

	    if (ang2<=ang1+FLT_EPSILON)
	      ang2 += 360;

	    Vector v = ptr->mapFromRef(center,sys,sky);
	    str << type_ << '(' << setprecision(8) << v[0] << ',' << v[1] << ','
		<< ang1 << ',' << ang2 <<',' << numAngles_-1 << ','
		<< ptr->mapLenFromRef(annuli_[0][0],sys,Coord::ARCSEC) << '"' << ','
		<< ptr->mapLenFromRef(annuli_[numAnnuli_-1][0],sys,Coord::ARCSEC)<< '"'<<','
		<< numAnnuli_-1 << ')';
	  }
	  break;
	case Coord::SEXAGESIMAL:
	  {
	    char buf[64];
	    ptr->mapFromRef(center,sys,sky,format,buf,64);
	    char ra[16];
	    char dec[16];
	    string x(buf);
	    istringstream wcs(x);
	    wcs >> ra >> dec;

	    double ang1 = radToDeg(parent->mapAngleFromRef(angles_[0],sys,sky));
	    double ang2 = radToDeg(parent->mapAngleFromRef(angles_[numAngles_-1],sys,sky));

	    if (ang2<=ang1+FLT_EPSILON)
	      ang2 += 360;

	    str << type_ << '(' << ra << ',' << dec << ','
		<< ang1 << ',' << ang2 <<',' << numAngles_-1 << ','
		<< ptr->mapLenFromRef(annuli_[0][0],sys,Coord::ARCSEC) << '"' << ','
		<< ptr->mapLenFromRef(annuli_[numAnnuli_-1][0],sys,Coord::ARCSEC)<< '"'<<','
		<< numAnnuli_-1 << ')';
	  }
	  break;
	}
      }
      else {
	double ang1 = radToDeg(parent->mapAngleFromRef(angles_[0],sys));
	double ang2 = radToDeg(parent->mapAngleFromRef(angles_[numAngles_-1],sys));

	if (ang2<=ang1+FLT_EPSILON)
	  ang2 += 360;

	Vector v = ptr->mapFromRef(center,sys);
	str << type_ << '(' << setprecision(8) << v[0] << ',' << v[1] << ','
	    << ang1 << ',' << ang2 <<',' << numAngles_-1 << ','
	    << ptr->mapLenFromRef(annuli_[0][0],sys) << ','
	    << ptr->mapLenFromRef(annuli_[numAnnuli_-1][0],sys)<< ','
	    << numAnnuli_-1 << ')';
      }
    }
  }

  listPost(str, conj, strip);
}

void Cpanda::listB(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky,
		  Coord::SkyFormat format, int conj, int strip)
{
  FitsImage* ptr = parent->findFits(sys,center);

  switch (sys) {
  case Coord::IMAGE:
  case Coord::PHYSICAL:
  case Coord::DETECTOR:
  case Coord::AMPLIFIER:
    {
      Vector v = ptr->mapFromRef(center,sys);
      for (int j=1; j<numAngles_; j++) {
	double ang1 = radToDeg(parent->mapAngleFromRef(angles_[j-1],sys));
	double ang2 = radToDeg(parent->mapAngleFromRef(angles_[j],sys));

	if (ang2<=ang1+FLT_EPSILON)
	  ang2 += 360;

	for (int i=1; i<numAnnuli_; i++) {
	  listPre(str, sys, sky, ptr, strip, 0);

	  str << type_ << '(' << setprecision(8) 
	      << v[0] << ',' << v[1] << ','
	      << ang1 << ',' << ang2 << ",1,"
	      << ptr->mapLenFromRef(annuli_[i-1][0],sys) << ',' 
	      << ptr->mapLenFromRef(annuli_[i][0],sys) << ",1)";

	  if (!strip) {
	    if (conj)
	      str << " ||";

	    str << " # panda=";
	    if (i==1 && j==1 && !strip) {
	      str << '(';
	      for (int k=0; k<numAngles_; k++)
		str << radToDeg(parent->mapAngleFromRef(angles_[k],sys))
		    << ((k<numAngles_-1) ? ' ' : ')');
	      str << '(';
	      for (int k=0; k<numAnnuli_; k++)
		str << ptr->mapLenFromRef(annuli_[k][0],sys) 
		    << ((k<numAnnuli_-1) ? ' ' : ')');
	      
	      listProps(str);
	    }
	    else
	      str << "ignore";

	    str << (strip ? ';' : '\n');
	  }
	  else {
	    if (conj)
	      str << "||";
	    else
	      str << ";";
	  }
	}
      }
    }
    break;
  default:
    if (ptr->hasWCS(sys)) {

      if (ptr->hasWCSCel(sys)) {
	switch (format) {
	case Coord::DEGREES:
	  {
	    Vector v = ptr->mapFromRef(center,sys,sky);
	    for (int j=1; j<numAngles_; j++) {
	      double ang1 = radToDeg(parent->mapAngleFromRef(angles_[j-1],sys,sky));
	      double ang2 = radToDeg(parent->mapAngleFromRef(angles_[j],sys,sky));

	      if (ang2<=ang1+FLT_EPSILON)
		ang2 += 360;

	      for (int i=1; i<numAnnuli_; i++) {
		listPre(str, sys, sky, ptr, strip, 0);

		str << type_ << '(' << setprecision(8) 
		    << v[0] << ',' << v[1] << ','
		    << ang1 << ',' << ang2 << ",1,"
		    << ptr->mapLenFromRef(annuli_[i-1][0],sys,Coord::ARCSEC) << '"' <<',' 
		    << ptr->mapLenFromRef(annuli_[i][0],sys,Coord::ARCSEC) << '"' <<",1)";
		
		if (!strip) {
		  if (conj)
		    str << " ||";

		  str << " # panda=";
		  if (i==1 && j==1 && !strip) {
		    str << '(';
		    for (int k=0; k<numAngles_; k++)
		      str << 
			radToDeg(parent->mapAngleFromRef(angles_[k],sys,sky))
			  << ((k<numAngles_-1) ? ' ' : ')');
		    str << '(';
		    for (int k=0; k<numAnnuli_; k++)
		      str << ptr->mapLenFromRef(annuli_[k][0],sys,Coord::ARCSEC) << '"'
			  << ((k<numAnnuli_-1) ? ' ' : ')');

		    listProps(str);
		  }
		  else
		    str << "ignore";

		  str << (strip ? ';' : '\n');
		}
		else {
		  if (conj)
		    str << "||";
		  else
		    str << ";";
		}
	      }
	    }
	  }
	  break;
	case Coord::SEXAGESIMAL:
	  {
	    char buf[64];
	    ptr->mapFromRef(center,sys,sky,format,buf,64);
	    char ra[16];
	    char dec[16];
	    string x(buf);
	    istringstream wcs(x);
	    wcs >> ra >> dec;

	    for (int j=1; j<numAngles_; j++) {
	      double ang1 = radToDeg(parent->mapAngleFromRef(angles_[j-1],sys,sky));
	      double ang2 = radToDeg(parent->mapAngleFromRef(angles_[j],sys,sky));

	      if (ang2<=ang1+FLT_EPSILON)
		ang2 += 360;

	      for (int i=1; i<numAnnuli_; i++) {

		listPre(str, sys, sky, ptr, strip, 0);

		str << type_ << '(' << ra << ',' << dec << ','
		    << ang1 << ',' << ang2 << ",1,"
		    << ptr->mapLenFromRef(annuli_[i-1][0],sys,Coord::ARCSEC) << '"' <<',' 
		    << ptr->mapLenFromRef(annuli_[i][0],sys,Coord::ARCSEC) << '"' <<",1)";

		if (!strip) {
		  if (conj)
		    str << " ||";

		  str << " # panda=";
		  if (i==1 && j==1 && !strip) {
		    str << '(';
		    for (int k=0; k<numAngles_; k++)
		      str << 
			radToDeg(parent->mapAngleFromRef(angles_[k],sys,sky))
			  << ((k<numAngles_-1) ? ' ' : ')');
		    str << '(';
		    for (int k=0; k<numAnnuli_; k++)
		      str << ptr->mapLenFromRef(annuli_[k][0],sys,Coord::ARCSEC) << '"'
			  << ((k<numAnnuli_-1) ? ' ' : ')');

		    listProps(str);
		  }
		  else
		    str << "ignore";

		  str << (strip ? ';' : '\n');
		}
		else {
		  if (conj)
		    str << "||";
		  else
		    str << ";";
		}
	      }
	    }
	  }
	  break;
	}
	break;
      }
      else {
	Vector v = ptr->mapFromRef(center,sys);
	for (int j=1; j<numAngles_; j++) {
	  double ang1 = radToDeg(parent->mapAngleFromRef(angles_[j-1],sys));
	  double ang2 = radToDeg(parent->mapAngleFromRef(angles_[j],sys));

	  if (ang2<=ang1+FLT_EPSILON)
	    ang2 += 360;

	  for (int i=1; i<numAnnuli_; i++) {
	    listPre(str, sys, sky, ptr, strip, 0);

	    str << type_ << '(' << setprecision(8) 
		<< v[0] << ',' << v[1] << ','
		<< ang1 << ',' << ang2 << ",1,"
		<< ptr->mapLenFromRef(annuli_[i-1][0],sys) <<',' 
		<< ptr->mapLenFromRef(annuli_[i][0],sys) <<",1)";

	    if (!strip) {
	      if (conj)
		str << " ||";

	      str << " # panda=";
	      if (i==1 && j==1 && !strip) {
		str << '(';
		for (int k=0; k<numAngles_; k++)
		  str << radToDeg(parent->mapAngleFromRef(angles_[k],sys))
		      << ((k<numAngles_-1) ? ' ' : ')');
		str << '(';
		for (int k=0; k<numAnnuli_; k++)
		  str << ptr->mapLenFromRef(annuli_[k][0],sys) 
		      << ((k<numAnnuli_-1) ? ' ' : ')');

		listProps(str);
	      }
	      else
		str << "ignore";

	      str << (strip ? ';' : '\n');
	    }
	    else {
	      if (conj)
		str << "||";
	      else
		str << ";";
	    }
	  }
	}
      }
    }
  }
}

void Cpanda::listXML(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky, 
		     Coord::SkyFormat format)
{
  FitsImage* ptr = parent->findFits(sys,center);

  XMLRowInit();
  XMLRow(XMLSHAPE,type_);

  XMLRowCenter(ptr,sys,sky,format);
  XMLRowRadiusX(ptr,sys,annuli_,numAnnuli_);
  XMLRowAng(sys,sky,angles_,numAngles_);

  XMLRowProps(ptr,sys);
  XMLRowEnd(str);
}

void Cpanda::listCiao(ostream& str, Coord::CoordSystem sys, int strip)
{
  FitsImage* ptr = parent->findFits();

  switch (sys) {
  case Coord::IMAGE:
  case Coord::PHYSICAL:
  case Coord::DETECTOR:
  case Coord::AMPLIFIER:
    {
      Vector v = ptr->mapFromRef(center,Coord::PHYSICAL);
      for (int i=0; i<numAnnuli_-1; i++)
	for (int j=0; j<numAngles_-1; j++) {
	  listCiaoPre(str);

	  double ang1 = radToDeg(angles_[j]);
	  double ang2 = radToDeg(angles_[j+1]);

	  if (ang2<=ang1+FLT_EPSILON)
	    ang2 += 360;

	  str << "pie(" << setprecision(8) << v[0] << ',' << v[1] << ','
	      << ptr->mapLenFromRef(annuli_[i][0],Coord::PHYSICAL) << ','
	      << ptr->mapLenFromRef(annuli_[i+1][0],Coord::PHYSICAL) << ','
	      << ang1 << ',' << ang2 << ')';

	  listCiaoPost(str, strip);
	}
    }
    break;
  default:
    if (ptr->hasWCSCel(sys)) {

      char buf[64];
      ptr->mapFromRef(center,sys,Coord::FK5,Coord::SEXAGESIMAL, buf, 64);
      char ra[16];
      char dec[16];
      string x(buf);
      istringstream wcs(x);
      wcs >> ra >> dec;

      for (int i=0; i<numAnnuli_-1; i++)
	for (int j=0; j<numAngles_-1; j++) {
	  listCiaoPre(str);

	  double ang1 = radToDeg(angles_[j]);
	  double ang2 = radToDeg(angles_[j+1]);

	  if (ang2<=ang1+FLT_EPSILON)
	    ang2 += 360;

	  str << "pie(" << ra << ',' << dec << ','
	      << ptr->mapLenFromRef(annuli_[i][0],sys,Coord::ARCMIN) << '\'' << ','
	      << ptr->mapLenFromRef(annuli_[i+1][0],sys,Coord::ARCMIN) << '\'' << ','
	      << ang1 << ',' << ang2 << ')';

	  listCiaoPost(str, strip);
	}
    }
  }
}

// special composite funtionallity

void Cpanda::setComposite(const Matrix& mx, double aa)
{
  center *= mx;
  updateBBox();
}
