// 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 "composite.h"
#include "fitsimage.h"

Composite::Composite(const Composite& a) : Marker(a) 
{
  members = a.members;
  global = a.global;
}

Composite::Composite(Base* p, const Vector& ctr, 
		     double ang, int gl,
		     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)
  : Marker(p, ctr, ang, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb)
{
  strcpy(type_, "composite");

  global = gl;

  handle = new Vector[4];
  numHandle = 4;

  updateBBox();
}

void Composite::x11(Drawable drawable, Coord::InternalSystem sys,
		    int tt, RenderMode mode, HandleMode hh)
{
  if (properties & HIDDEN)
    return;

  if (hh==HANDLES)
    renderXHandles(drawable);
  if (tt)
    renderXText(drawable, sys, mode);

  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    if (global)
      m->setComposite(colorName, lineWidth, highlited);
    m->x11(drawable, sys, tt, mode, hh);
    delete m;
    mk=mk->next();
  }
}

void Composite::ps(int mode, int tt)
{
  if (tt)
    renderPSText(mode);

  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    if (global)
      m->setComposite(colorName, lineWidth, highlited);
    m->ps(mode,tt);
    delete m;
    mk=mk->next();
  }
}

#ifdef MAC_OSX_TK
void Composite::macosx(int tt)
{
  if (tt)
    renderMACOSXText();

  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    if (global)
      m->setComposite(colorName, lineWidth, highlited);
    m->macosx(tt);
    delete m;
    mk=mk->next();
  }
}
#endif

#ifdef __WIN32
void Composite::win32(int tt)
{
  if (tt)
    renderWIN32Text();

  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    if (global)
      m->setComposite(colorName, lineWidth, highlited);
    m->win32(tt);
    delete m;
    mk=mk->next();
  }
}
#endif

// Support

void Composite::updateHandles()
{
  BBox bb(center * bckMatrix());
  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    for(int ii=0; ii<m->getNumHandle(); ii++)
      bb.bound(bckMap(m->getHandle(ii),Coord::CANVAS));
    delete m;
    mk=mk->next();
  }
  bb.expand(3); // a little more room around the edges

  handle[0] = fwdMap(bb.ll,Coord::CANVAS);
  handle[1] = fwdMap(bb.lr(),Coord::CANVAS);
  handle[2] = fwdMap(bb.ur,Coord::CANVAS);
  handle[3] = fwdMap(bb.ul(),Coord::CANVAS);
}

void Composite::updateCoords(const Matrix& mx)
{
  Marker* mk=members.head();
  while (mk) {
    Vector cc = center;
    mk->setComposite(fwdMatrix(), angle);
    mk->updateCoords(mx);
    center = cc*mx;
    mk->setComposite(bckMatrix(), -angle);
    center = cc;
    mk=mk->next();
  }

  Marker::updateCoords(mx);
}

int Composite::isIn(const Vector& v)
{
  if (!bbox.isIn(v))
    return 0;

  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    if (m->isIn(v)) {
      delete m;
      return 1;
    }
    delete m;

    mk=mk->next();
  }

  return 0;
}

void Composite::append(Marker* m)
{
  m->setComposite(bckMatrix(), -angle);
  members.append(m);
}

Marker* Composite::extract()
{
  Marker* mk=members.head();
  if (mk) {
    members.extractNext(mk);
    mk->setComposite(fwdMatrix(), angle);
  }
  return mk;
}

// list

void Composite::list(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky, 
		 Coord::SkyFormat format, int conj, int strip)
{
  if (!strip) {
    FitsImage* ptr = parent->findFits(sys,center);
    listPre(str, sys, sky, ptr, strip, 1);

    switch (sys) {
    case Coord::IMAGE:
    case Coord::PHYSICAL:
    case Coord::DETECTOR:
    case Coord::AMPLIFIER:
      {
	Vector v = ptr->mapFromRef(center,sys);
	str << type_ << '(' << setprecision(8) << v[0] << ',' << v[1] << ',' 
	    << radToDeg(parent->mapAngleFromRef(angle,sys)) << ')';
      }
      break;
    default:
      if (ptr->hasWCS(sys)) {
	if (ptr->hasWCSCel(sys)) {
	  switch (format) {
	  case Coord::DEGREES:
	    {
	      Vector v = ptr->mapFromRef(center,sys,sky);
	      str << type_ << '(' << setprecision(8) << v[0] << ',' << v[1] 
		  << ','
		  << radToDeg(parent->mapAngleFromRef(angle,sys,sky)) << ')';
	    }
	    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;
	      str << type_ << '(' << ra << ',' << dec << ',' 
		  << radToDeg(parent->mapAngleFromRef(angle,sys,sky)) << ')';
	    }
	    break;
	  }
	}
	else {
	  Vector v = ptr->mapFromRef(center,sys);
	  str << type_ << '(' << setprecision(8) << v[0] << ',' << v[1] << ','
	      << radToDeg(parent->mapAngleFromRef(angle,sys)) << ')';
	}
      }
    }
      
    str << " ||";
    str << " composite=" << global;
    listProperties(str, 0);
  }

  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    mk=mk->next();

    m->setComposite(fwdMatrix(), angle);
    m->list(str, sys, sky, format, (mk?1:0), strip);
    delete m;
  }
}

void Composite::listCiao(ostream& str, Coord::CoordSystem sys, int strip)
{
  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    mk=mk->next();

    m->setComposite(fwdMatrix(), angle);
    m->listCiao(str, sys, strip);
    delete m;
  }
}

void Composite::listPros(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky,
			 Coord::SkyFormat format, int strip)
{
  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    m->listPros(str, sys, sky, format, strip);
    delete m;

    mk=mk->next();
  }
}

void Composite::listSAOtng(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky,
			   Coord::SkyFormat format, int strip)
{
  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    m->listSAOtng(str, sys, sky, format, strip);
    delete m;

    mk=mk->next();
  }
}

void Composite::listSAOimage(ostream& str, int strip)
{
  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    m->listSAOimage(str, strip);
    delete m;

    mk=mk->next();
  }
}

void Composite::listXY(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky,
		       Coord::SkyFormat format, int strip)
{
  Marker* mk=members.head();
  while (mk) {
    Marker* m = mk->dup();
    m->setComposite(fwdMatrix(), angle);
    m->listXY(str, sys, sky, format, strip);
    delete m;

    mk=mk->next();
  }
}

