#include "projection.hh"

#include <QPainter>
#include <QPointF>

#include <iostream>
#include <cmath>

void autoscale(double& min, double& max, double& tick)
{
  //  std::cout << "autoscale in: " << min << ", " << max << ", " << tick << std::endl;
  double basescale=log10(tick);
  int ibasescale=basescale;
  double tickin=tick;
  tick=pow(10, ibasescale);
  if (10*tick<tickin)
    tick*=10;
  else if (5*tick<tickin)
    tick*=5;
  else if (2*tick<tickin)
    tick*=2;
  double low=0;
  while (low<min)
    low+=tick;
  while (low>min)
    low-=tick;
  double high=0;
  while (high>max)
    high-=tick;
  while (high<max)
    high+=tick;
  min=low;
  max=high;
  //std::cout << "autoscale out: " << min << ", " << max << ", " << tick << std::endl;
}

Projection::
Projection(QWidget* parent, Type type):
  QWidget(parent),
  ViewerBase(),
  type_(type)
{
  setMinimumSize(800,600);
  setContentsMargins(5, 5, 5, 5);
  autoScale();
  
}

void Projection::
addShape(const Shape& shape)
{
  shapes_.push_back(shape);
}
const Shape& Projection::
shape(std::vector<Shape>::size_type i) const
{
  return shapes_.at(i);
}
std::vector<Shape>::size_type Projection::
numShapes(void) const
{
  return shapes_.size();
}
void Projection::
clearShapes(void)
{
  shapes_.clear();
}

//update display
void Projection::
refresh(void)
{
  draw();
  update();
}

void Projection::
autoScale(void)
{
  double minx, maxx, miny, maxy, minz, maxz;
  if (shapes_.size())
    {
      minx=maxx=shapes_.at(0).points.at(0).x;
      miny=maxy=shapes_.at(0).points.at(0).y;
      minz=maxz=shapes_.at(0).points.at(0).z;
      for (int i=0; i<shapes_.size(); i++)
	{
	  for (int j=0; j<shapes_.at(i).points.size(); j++)
	    {
	      ThreeVector& v=shapes_.at(i).points.at(j);
	      if (v.x<minx)
		minx=v.x;
	      if (v.x>maxx)
		maxx=v.x;
	      if (v.y<miny)
		miny=v.y;
	      if (v.y>maxy)
		maxy=v.y;
	      if (v.z<minz)
		minz=v.z;
	      if (v.z>maxz)
		maxz=v.z;
	      
	    }
	}
      
      if (type_==XY || type_==XZ)
	{
	  hmin_=minx;
	  hmax_=maxx;
	}
      if (type_==XY)
	{
	  vmin_=miny;
	  vmax_=maxy;
	}
      if (type_==YZ)
	{
	  hmin_=miny;
	  hmax_=maxy;
	}
      if (type_==XZ || type_==YZ)
	{
	  vmin_=minz;
	  vmax_=maxz;
	}
      hstep_=(hmax_-hmin_)/5;      
      vstep_=(vmax_-vmin_)/5;
      autoscale(hmin_, hmax_, hstep_);
      autoscale(vmin_, vmax_, vstep_);
      
    }
  else
    {
      hmin_=vmin_=-10;
      hmax_=vmax_=10;
      hstep_=vstep_=2;
    }
  
}
void Projection::
zoomIn(void)
{
  double hrange=hmax_-hmin_;
  double vrange=vmax_-vmin_;
  hmin_+=hrange/4;
  hmax_-=hrange/4;
  hstep_/=2;
  vmin_+=vrange/4;
  vmax_-=vrange/4;
  vstep_/=2;
  //autoscale(hmin_, hmax_, hstep_);
  //autoscale(vmin_, vmax_, vstep_);
}
void Projection::
zoomOut(void)
{
  double hrange=hmax_-hmin_;
  double vrange=vmax_-vmin_;
  hmin_-=hrange/2;
  hmax_+=hrange/2;
  hstep_*=2;
  vmin_-=vrange/2;
  vmax_+=vrange/2;
  vstep_*=2;
  //autoscale(hmin_, hmax_, hstep_);
  //autoscale(vmin_, vmax_, vstep_);
}
void Projection::
move(int h, int v)
{
  hmin_+=h*hstep_;
  hmax_+=h*hstep_;
  vmin_+=v*vstep_;
  vmax_+=v*vstep_;
}
void Projection::
draw(void)
{
  QPainter painter(&pixmap_);
  painter.setBrush(Qt::NoBrush);
  if (painter.isActive())
    {
      pixmap_.fill(Qt::black);
      
      const int leftedge=80;
      const int topedge=15;
      const int rightedge=35;
      const int bottomedge=40;
      const int ticklen=5;

      rect_=QRect(contentsRect().left()+leftedge, contentsRect().top()+topedge, contentsRect().width()-leftedge-rightedge, contentsRect().height()-topedge-bottomedge);

      //  std::cout << hmin_ << ", " << hmax_ << std::endl;
      //  std::cout << rect_.width() << ", " << rect_.height() << "  "  << scale_ << " mm/pixel\n";

      double hscale=(hmax_-hmin_)/rect_.width();
      double vscale=(vmax_-vmin_)/rect_.height();
      //  std::cout << "scales:  " << hscale << ", " << vscale << std::endl;
      if (vscale<hscale)
	{
	  scale_=hscale;
	  int diff=rect_.height()-rect_.height()*(vscale/hscale);
	  rect_=QRect(rect_.left(), rect_.top()+diff/2, rect_.width(), rect_.height()-diff);
	}
      else
	{
	  scale_=vscale;
	  int diff=rect_.width()-rect_.width()*(hscale/vscale);
	  rect_=QRect(rect_.left()+diff/2, rect_.top(), rect_.width()-diff, rect_.height());
	}

      // std::cout << rect_.left() << ", " << rect_.bottom() << "  "  << scale_ << " mm/pixel\n";

      //detector components
      painter.setPen(Qt::blue);
      painter.setBrush(Qt::darkBlue);
      for (int s=0; s<shapes_.size(); s++)
	{
	  const Shape& shape=shapes_.at(s);
	  //repeat until end of coordinate index array
	  int i=0;
	  bool moved=false;
	  QPainterPath path;
	  while (i<shape.connections.size())
	    {
	      //create a polygon with arbitrary number of sides
	      path=QPainterPath();
	      //repeat until we see a -1 in the coordinate index, indicating end of polygon, or until we reach the end of the coordinate index
	      while (i<shape.connections.size() && shape.connections.at(i)!=-1)
		{
		  double h;
		  double v;
		  if (type_==XY || type_==XZ)
		    h=shape.points.at(shape.connections.at(i)).x;
		  if (type_==XY)
		    v=shape.points.at(shape.connections.at(i)).y;
		  if (type_==YZ)
		    h=shape.points.at(shape.connections.at(i)).y;
		  if (type_==XZ || type_==YZ)
		    v=shape.points.at(shape.connections.at(i)).z;
	      
		  int ph=rect_.left()+(h-hmin_)/scale_;
		  int pv=rect_.bottom()-(v-vmin_)/scale_;
		  if (moved)
		    path.lineTo(ph, pv);
		  else
		    {
		      path.moveTo(ph, pv);
		      moved=true;
		    }
		  //move to the next point
		  i++;
		}
	      moved=false;
	      painter.drawPath(path);
	      //we've hit a -1, move on to the next coordinate index point
	      i++;
	    }

	}


      if (showHits_)
	{
	  for (int sel=0; sel<=1; sel++)
	    {
	      for (int i=0; i<hits_.size(); i++)
		{
		  const Hit& hit=hits_[i];
		  if (hit.show)
		    {
		  if (hit.selected)
		    {
		      painter.setBrush(Qt::red);
		      painter.setPen(Qt::red);
		    }
		  else
		    {
		      painter.setPen(Qt::green);
		      painter.setBrush(Qt::NoBrush);
		    }
		  double h, v;
		  if (type_==XY || type_==XZ)
		    h=hit.x;
		  if (type_==XY)
		    v=hit.y;
		  if (type_==YZ)
		    h=hit.y;
		  if (type_==XZ || type_==YZ)
		    v=hit.z;
		  int ph=rect_.left()+(h-hmin_)/scale_;
		  int pv=rect_.bottom()-(v-vmin_)/scale_;
		  int pr=hit.value*pow(hitScale_, 0.33333)*(3./4)/scale_;
		  if (hit.selected && sel)
		    {
		      if (pr<1) pr=1;
		      painter.drawEllipse(ph-pr, pv-pr, pr*2, pr*2);

		    }
		  else if (!hit.selected && !sel)
		    painter.drawArc(ph-pr, pv-pr, pr*2, pr*2, 0, 360*16);
		}
		}
	    }
	}
      if (showTracks_)
	{
	  for (int sel=0; sel<=1; sel++)
	    {
	      for (int i=0; i<tracks_.size(); i++)
		{
		  const Track& track=tracks_.at(i);
		  if (track.show)
		    {
		  if (track.selected)
		    {
		      painter.setBrush(Qt::red);
		      painter.setPen(Qt::red);
		    }
		  else
		    {
		      painter.setPen(Qt::magenta);
		      painter.setBrush(Qt::NoBrush);
		    }
		  double h1, h2, v1, v2;
		  if (type_==XY || type_==XZ)
		    {
		      h1=track.start.x;
		      h2=track.end.x;
		    }
		  if (type_==XY)
		    {
		      v1=track.start.y;
		      v2=track.end.y;
		    }
		  if (type_==YZ)
		    {
		      h1=track.start.y;
		      h2=track.end.y;
		    }
		  if (type_==XZ || type_==YZ)
		    {
		      v1=track.start.z;
		      v2=track.end.z;
		    }
		  int ph1=rect_.left()+(h1-hmin_)/scale_;
		  int ph2=rect_.left()+(h2-hmin_)/scale_;
		  int pv1=rect_.bottom()-(v1-vmin_)/scale_;
		  int pv2=rect_.bottom()-(v2-vmin_)/scale_;
		  double r=sqrt((ph2-ph1)*(ph2-ph1)+(pv2-pv1)*(pv2-pv1));
		  double pr=track.delta*pow(trackScale_/r/M_PI, 0.5)/scale_;
		  if (track.selected && sel && pr<1)
		    pr=1;
		  int prh=pr*(pv2-pv1)/r;
		  int prv=-pr*(ph2-ph1)/r;
		  QPointF points[4];
		  points[0]=QPointF(ph1-prh, pv1-prv);
		  points[1]=QPointF(ph1+prh, pv1+prv);
		  points[2]=QPointF(ph2+prh, pv2+prv);
		  points[3]=QPointF(ph2-prh, pv2-prv);
		  if ((!track.selected && !sel) || (track.selected && sel))
		    painter.drawPolygon(points, 4);
		}
	    }
	    }
	}

      //black around outside
      painter.setPen(Qt::black);
      painter.setBrush(Qt::black);
      painter.drawRect(0, 0, rect_.left(), pixmap_.height());
      painter.drawRect(0, 0, pixmap_.width(), rect_.top());
      painter.drawRect(rect_.right(), 0, pixmap_.width()-rect_.right(), pixmap_.height());
      painter.drawRect(0, rect_.bottom(), pixmap_.width(), pixmap_.height()-rect_.bottom());
      //axes
      painter.setPen(Qt::white);
    
      painter.drawLine(rect_.left(), rect_.bottom(), rect_.left(), rect_.top());
      painter.drawLine(rect_.left(), rect_.bottom(), rect_.right(), rect_.bottom());

      QFontMetrics fm=painter.fontMetrics();
      for (double h=hmin_; h<hmax_+hstep_/2; h+=hstep_)
	{
	  int p=rect_.left()+(h-hmin_)/scale_;
	  painter.drawLine(p, rect_.bottom(), p, rect_.bottom()+ticklen);
	  QString mark=QString::number(h);
	  painter.drawText(p-fm.width(mark)/2, rect_.bottom()+ticklen+1+fm.ascent(), mark);
	}
      for (double v=vmin_; v<vmax_+vstep_/2; v+=vstep_)
	{
	  int p=rect_.bottom()-(v-vmin_)/scale_;
	  painter.drawLine(rect_.left(), p, rect_.left()-ticklen, p);
	  QString mark=QString::number(v);
	  painter.drawText(rect_.left()-ticklen-1-fm.width(mark), p+fm.ascent()/2, mark);
	}

    }
  //  painter.setBrush(Qt::black);
  //  painter.setPen(Qt::black);
  //  painter.drawRect(contentsRect());
}
//virtual function overrides
void Projection::
paintEvent(QPaintEvent*)
{
  QPainter painter(this);
  if (painter.isActive())
    {
      painter.eraseRect(rect());
      painter.drawPixmap(contentsRect(), pixmap_, pixmap_.rect());
    }
}
void Projection::
resizeEvent(QResizeEvent*)
{
  pixmap_=QPixmap(contentsRect().size());
  draw();
}

//draw to pixmap

