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

#include <tk.h>

#include "boxannulus.h"
#include "fitsimage.h"

BoxAnnulus::BoxAnnulus(Base* p, const Vector& ctr,
		       const Vector& s, 
		       double ang,
		       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)
  : BaseBox(p, ctr, ang, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb)
{
  numAnnuli_ = 1;
  annuli_ = new Vector[1];
  annuli_[0] = s;

  strcpy(type_,"boxannulus");
  numHandle = 4;

  updateBBox();
}

BoxAnnulus::BoxAnnulus(Base* p, const Vector& ctr,
		       const Vector& inner, const Vector& outer, int num,
		       double ang)
  : BaseBox(p, ctr, ang)
{
  numAnnuli_ = num+1;
  annuli_ = new Vector[numAnnuli_];

  for (int i=0; i<numAnnuli_; i++)
    annuli_[i] = ((outer-inner)/num)*i+inner;

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

  updateBBox();
}

BoxAnnulus::BoxAnnulus(Base* p, const Vector& ctr,
		       const Vector& inner, const Vector& outer, int num,
		       double ang,
		       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)
  : BaseBox(p, ctr, ang, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb)
{
  numAnnuli_ = num+1;
  annuli_ = new Vector[numAnnuli_];

  for (int i=0; i<numAnnuli_; i++)
    annuli_[i] = ((outer-inner)/num)*i+inner;

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

  updateBBox();
}

BoxAnnulus::BoxAnnulus(Base* p, const Vector& ctr, 
		       int an, Vector* s,
		       double ang, 
		       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)
  : BaseBox(p, ctr, ang, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb)
{
  numAnnuli_ = an;
  annuli_ = new Vector[numAnnuli_];

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

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

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

BoxAnnulus::BoxAnnulus(const BoxAnnulus& a) : BaseBox(a) {}

void BoxAnnulus::editBegin(int h)
{
  if (h<5) {
    switch (h) {
    case 1:
      return;
    case 2:
      annuli_[numAnnuli_-1] = Vector(-annuli_[numAnnuli_-1][0],annuli_[numAnnuli_-1][1]);
      return;
    case 3:
      annuli_[numAnnuli_-1] = -annuli_[numAnnuli_-1];
      return;
    case 4:
      annuli_[numAnnuli_-1] = Vector(annuli_[numAnnuli_-1][0],-annuli_[numAnnuli_-1][1]);
      return;
    }
  }

  doCallBack(CallBack::EDITBEGINCB);
}

void BoxAnnulus::edit(const Vector& v, int h)
{
  Matrix mm = bckMatrix();
  Matrix nn = mm.invert();

  // This sizes about the opposite node
  if (h<5) {
    Vector o = annuli_[numAnnuli_-1];
    Vector n = (annuli_[numAnnuli_-1]/2) - (v*mm);

    // don't go thru opposite node
    if (n[0]!=0 && n[1]!=0) {
      Vector ov = annuli_[numAnnuli_-1]/2 * nn;
      annuli_[numAnnuli_-1] = n;
      Vector nv = annuli_[numAnnuli_-1]/2 * nn;
      center -= nv-ov;

      for (int i=0; i<numAnnuli_-1; i++) {
	annuli_[i][0] *= fabs(n[0]/o[0]);
	annuli_[i][1] *= fabs(n[1]/o[1]);
      }
    }
  }
  else {
    // we must have some length
    double l = (v * mm * 2).length();
    annuli_[h-5] = annuli_[numAnnuli_-1] * l/annuli_[numAnnuli_-1][0];
  }

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

void BoxAnnulus::editEnd()
{
  for (int i=1; i<numAnnuli_; i++)
    annuli_[i] = annuli_[i].abs();
  sortAnnuli();

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

int BoxAnnulus::addAnnuli(const Vector& v)
{
  Matrix mm = bckMatrix();
  double l = (v * mm * 2).length();
  Vector rr = annuli_[numAnnuli_-1] * l/annuli_[numAnnuli_-1][0];

  return insertAnnuli(rr);
}

void BoxAnnulus::analysis(AnalysisTask mm, int which)
{
  switch (mm) {
  case RADIAL:
    if (!analysisRadial_ && which) {
      addCallBack(CallBack::MOVECB, analysisRadialCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::EDITCB, analysisRadialCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::EDITENDCB, analysisRadialCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::ROTATECB, analysisRadialCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::UPDATECB, analysisRadialCB_[0], 
		  parent->options->cmdName);
      addCallBack(CallBack::DELETECB, analysisRadialCB_[1], 
		  parent->options->cmdName);
    }
    if (analysisRadial_ && !which) {
      deleteCallBack(CallBack::MOVECB, analysisRadialCB_[0]);
      deleteCallBack(CallBack::EDITCB, analysisRadialCB_[0]);
      deleteCallBack(CallBack::EDITENDCB, analysisRadialCB_[0]);
      deleteCallBack(CallBack::ROTATECB, analysisRadialCB_[0]);
      deleteCallBack(CallBack::UPDATECB, analysisRadialCB_[0]);
      deleteCallBack(CallBack::DELETECB, analysisRadialCB_[1]);
    }

    analysisRadial_ = 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::ROTATECB, 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::ROTATECB, analysisStatsCB_[0]);
      deleteCallBack(CallBack::UPDATECB, analysisStatsCB_[0]);
      deleteCallBack(CallBack::DELETECB, analysisStatsCB_[1]);
    }

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

void BoxAnnulus::analysisRadial(char* xname, char* yname, char* ename,
				Coord::CoordSystem sys)
{
  double* xx;
  double* yy;
  double* ee;

  BBox* bb = new BBox[numAnnuli_];
  Matrix mm = Rotate(angle) * Translate(center);

  for (int ii=0; ii<numAnnuli_; ii++) {
    // during resize, annuli_ can be negative
    Vector vv = annuli_[ii].abs();
    bb[ii] = BBox(-vv * mm);
    bb[ii].bound( vv * mm);
    bb[ii].bound(Vector( vv[0],-vv[1]) * mm);
    bb[ii].bound(Vector(-vv[0], vv[1]) * mm);
  }

  int num = parent->markerAnalysisRadial(this, &xx, &yy, &ee,
					 numAnnuli_-1, annuli_, 
					 bb, sys);
  analysisXYEResult(xname, yname, ename, xx, yy, ee, num);
}

void BoxAnnulus::analysisStats(Coord::CoordSystem sys, Coord::SkyFrame sky)
{
  ostringstream str;
  BBox* bb = new BBox[numAnnuli_];
  Matrix mm = Rotate(angle) * Translate(center);

  for (int ii=0; ii<numAnnuli_; ii++) {
    // during resize, annuli_ can be negative
    Vector vv = annuli_[ii].abs();
    bb[ii] = BBox(-vv * mm);
    bb[ii].bound( vv * mm);
    bb[ii].bound(Vector( vv[0],-vv[1]) * mm);
    bb[ii].bound(Vector(-vv[0], vv[1]) * mm);
  }

  parent->markerAnalysisStats(this, str, numAnnuli_-1, bb, sys, sky);
  str << ends;
  Tcl_AppendResult(parent->interp, str.str().c_str(), NULL);
}

// list

void BoxAnnulus::list(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);

  str << "box(";
  ptr->listFromRef(str,center,sys,sky,format);
  for (int ii=0; ii<numAnnuli_; ii++) {
    str << ',';
    if (ptr->hasWCSCel(sys))
      str << setunit('"');
    ptr->listLenFromRef(str,annuli_[ii],sys,Coord::ARCSEC);
  }
  str << ',';
  parent->listAngleFromRef(str,angle,sys,sky);
  str << ')';

  listPost(str, conj, strip);
}

void BoxAnnulus::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);
  XMLRowRadius(ptr,sys,annuli_,numAnnuli_);
  XMLRowAng(sys,sky);

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

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

  switch (sys) {
  case Coord::IMAGE:
  case Coord::DETECTOR:
  case Coord::AMPLIFIER:
    sys = Coord::IMAGE;
  case Coord::PHYSICAL:
    for (int ii=0; ii<numAnnuli_; ii++) {
      coord.listProsCoordSystem(str,sys,sky);
      str << "; box ";
      ptr->listFromRef(str,center,sys);
      str << ' ';
      ptr->listLenFromRef(str,annuli_[ii],Coord::IMAGE);
      str << ' ';
      parent->listAngleFromRef(str,angle,Coord::IMAGE);

      if (ii!=0) {
	str << " & !box ";
	ptr->listFromRef(str,center,sys);
	str << ' ';
	ptr->listLenFromRef(str,annuli_[ii-1],Coord::IMAGE);
	str << ' ';
	parent->listAngleFromRef(str,angle,Coord::IMAGE);
      }
      listProsPost(str, strip);
    }
    break;
  default:
    for (int ii=0; ii<numAnnuli_; ii++) {
      coord.listProsCoordSystem(str,sys,sky);
      str << "; box ";
      if (format == Coord::DEGREES)
	str << setunit('d');
      ptr->listFromRef(str,center,sys,sky,format);
      str << ' ';
      str << setunit('"');
      ptr->listLenFromRef(str,annuli_[ii],sys,Coord::ARCSEC);
      str << ' ';
      parent->listAngleFromRef(str,angle,Coord::IMAGE);

      if (ii!=0) {
	str << " & !box ";
	if (format == Coord::DEGREES)
	  str << setunit('d');
	ptr->listFromRef(str,center,sys,sky,format);
	str << ' ';
	str << setunit('"');
	ptr->listLenFromRef(str,annuli_[ii-1],sys,Coord::ARCSEC);
	str << ' ';
	parent->listAngleFromRef(str,angle,Coord::IMAGE);
      }
      listProsPost(str, strip);
    }
    break;
  }
}

void BoxAnnulus::listSAOimage(ostream& str, int strip)
{
  FitsImage* ptr = parent->findFits();
  listSAOimagePre(str);

  for (int ii=0; ii<numAnnuli_; ii++) {
    str << "box(";
    ptr->listFromRef(str,center,Coord::IMAGE);
    str << ',';
    ptr->listLenFromRef(str,annuli_[ii],Coord::IMAGE);
    str << ',';
    parent->listAngleFromRef(str,angle,Coord::IMAGE);
    str << ')';

    if (ii!=0) {
      str << " & !box(";
      ptr->listFromRef(str,center,Coord::IMAGE);
      str << ',';
      ptr->listLenFromRef(str,annuli_[ii-1],Coord::IMAGE);
      str << ',';
      parent->listAngleFromRef(str,angle,Coord::IMAGE);
      str << ')';
    }
    
    listSAOimagePost(str, strip);
  }
}