/*  FSLView - 2D/3D Interactive Image Viewer

    James Saunders, David Flitney and Stephen Smith, FMRIB Image Analysis Group

    Copyright (C) 2002-2012 University of Oxford  */

/*  CCOPYRIGHT */

//#define DEBUGGING
#include "tracker.h"

#include <boost/shared_ptr.hpp>

#include <qwt_plot_curve.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_grid.h>
#include <qwt_symbol.h>
//Added by qt3to4:
#include <QMouseEvent>

#include "singleserieswidget.h"
#include "timeserieswidget.h"

SingleSeriesPlot::SingleSeriesPlot(Image::Handle i,
		Cursor::Handle c,
		PlotOptions::Handle p,
		QWidget *parent):
		QwtPlot(parent),m_image(i),m_cursor(c),
		m_options(p),m_enabled(true),
		m_percent(false),m_axisDisplay(true),m_demean(false),
		m_causedCursorUpdate(false)
{
	constructor();
}

//SingleSeriesPlot::SingleSeriesPlot(Image::Handle i,
//                                       Cursor::Handle c,
//                                       GraphManager::Handle g,
//                                       PlotOptions::Handle p,
//				       QWidget *parent):
//  TimeSeriesPlot(parent),m_image(i),m_cursor(c),m_graphManager(g),
//  m_options(p),m_enabled(true),
//  m_percent(false),m_axisDisplay(true),m_demean(false),
//  m_causedCursorUpdate(false)
//{
//  constructor();
//}

QSizePolicy SingleSeriesPlot::sizePolicy()
{
  return QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
}

void SingleSeriesPlot::constructor()
{ 
  TRACKER("SingleSeriesPlot::constructor");

//  if(m_graphManager){m_graphManager->attach(this);}
  
  m_curveDataList = CurveDataList::create();

  m_marker = new QwtPlotMarker();
  m_marker->setLineStyle(QwtPlotMarker::VLine);
  m_marker->setLabelAlignment(Qt::AlignRight | Qt::AlignBottom);
  m_marker->setLinePen(QPen(QColor(Qt::green), 0, Qt::DashDotLine));
  m_marker->setSymbol( QwtSymbol(QwtSymbol::Diamond,
      QColor(Qt::yellow), QColor(Qt::green), QSize(7,7)));
  m_marker->attach(this);

  m_grid = new QwtPlotGrid();

  setMinimumSize(50, 50);
  setMargin(10);

  m_cursor->attach(this);
  if(m_options->getModelFit())
    m_options->getModelFit()->attach(this);
  
  setGraphOptions();

  setAutoReplot(true);
}

void SingleSeriesPlot::setGraphOptions()
{
  TRACKER("SingleSeriesPlot::setGraphOptions");

  std::string title =  m_image->getInfo()->inqImageName();
  if(m_options->inqTitle())
    {
      QString preTitle("Timeseries - ");
      setTitle(preTitle + title.c_str());
    }
  else
    {
      setTitle("");
    }

  if(m_options->inqXLabel()){setAxisTitle(xBottom, "Time");}
  else                      {setAxisTitle(xBottom,"");}
  if(m_options->inqYLabel()){setAxisTitle(yLeft,   "Value");}
  else                      {setAxisTitle(yLeft,"");}

  setCanvasBackground(QColor(Qt::gray));

  //  setAxisMargins(QwtPlot::yLeft, 0, 0);
  setMargin(0);

  enableAxis(xBottom,m_options->inqXNums());
  enableAxis(yLeft,  m_options->inqYNums());
  m_grid->enableX(m_options->inqXGrid());
  m_grid->enableY(m_options->inqYGrid());

  if(!m_options->inqXNums() && !m_options->inqYNums() &&
     !m_options->inqXGrid() && !m_options->inqYGrid())
    {
      m_axisDisplay = false;
    }
  else
    {
      m_axisDisplay = true;
    }
}

SingleSeriesPlot::~SingleSeriesPlot()
{  
  TRACKER("SingleSeriesPlot::~SingleSeriesPlot");
  m_cursor->detach(this);
  if(m_options->getModelFit()) m_options->getModelFit()->detach(this);
//  if(m_graphManager) m_graphManager->detach(this);
}


bool SingleSeriesPlot::addTimeSeries(const TimeSeries::Handle &timeSeries,
                                       bool browse)
{  
  TRACKER("SingleSeriesPlot::addTimeSeries");
  CurveData::Handle curveData = CurveData::create(timeSeries,browse);
  return m_curveDataList->push_back(curveData);
}

void SingleSeriesPlot::setLastCurveActive(bool setCursor)
{   
  TRACKER("SingleSeriesPlot::setLastCurveActive");
  m_curveDataList->setAllInActive();
  setActiveCurve(m_curveDataList->back(),setCursor);
}

void SingleSeriesPlot::setAllInActive()
{  
  TRACKER("SingleSeriesPlot::setAllInActive");
  m_curveDataList->setAllInActive();
}

void SingleSeriesPlot::remTimeSeries(bool browse)
{ 
  TRACKER("SingleSeriesPlot::remTimeSeries");
  if(browse){ m_curveDataList->removeBrowse();}
  else      { m_curveDataList->removeActive();}
}

void SingleSeriesPlot::remAllTimeSeries()
{
  m_curveDataList->removeAll();
}

void SingleSeriesPlot::setEnabled(bool state)
{  
  TRACKER("SingleSeriesPlot::setEnabled");
  m_enabled = state;
}

void SingleSeriesPlot::axisDisplay(bool y)
{ 
  TRACKER("SingleSeriesPlot::axisDisplay");
  if(m_enabled)
    {
      m_axisDisplay = y;
      m_options->setGrids(m_axisDisplay, m_axisDisplay);
      m_options->setNums(m_axisDisplay, m_axisDisplay);
      setGraphOptions();
      redraw();
    }
  
}

void SingleSeriesPlot::startPlotProcess()
{  
  TRACKER("SingleSeriesPlot::startPlotProcess");

  setAxisAutoScale(QwtPlot::yLeft);
  plotAllTimeSeries();
}

void SingleSeriesPlot::plotAllTimeSeries()
{   
	TRACKER("SingleSeriesPlot::plotAllTimeSeries");

	const bool doReplot = autoReplot();
	setAutoReplot(false);

	//clear();
	detachItems(QwtPlotItem::Rtti_PlotCurve, true);

	for(CurveDataList::It i = m_curveDataList->begin();
			i != m_curveDataList->end();
			i++)
	{
		plotTimeSeries(*i);
	}

	setAutoReplot(doReplot);

	replot();
}

void SingleSeriesPlot::plotTimeSeries(CurveData::Handle cd)
{  
	TRACKER("SingleSeriesPlot::plotTimeSeries");
	TimeSeries::Handle ts = cd->inqTimeSeries();

	int volCount = ts->inqVolCount();
	QwtPlotCurve *curve = new QwtPlotCurve("Timeseries");

	cd->setCurve(curve);

	if(cd->inqIsActive())
		curve->setPen( QPen(Qt::green) );
	else
		curve->setPen( QPen(Qt::blue) );

	switch(cd->inqIndex())
	{
	case CurveData::Null:                                              break;
	case CurveData::FiltFunc:  curve->setPen( QPen(Qt::red) );         break;
	case CurveData::Full:      curve->setPen( QPen(Qt::blue) );        break;
	case CurveData::Cope1:     curve->setPen( QPen(Qt::green) );       break;
	case CurveData::Cope2:     curve->setPen( QPen(Qt::cyan) );        break;
	case CurveData::Cope3:     curve->setPen( QPen(Qt::darkMagenta) ); break;
	case CurveData::Cope4:     curve->setPen( QPen(Qt::darkGreen) );   break;
	case CurveData::PE:        curve->setPen( QPen(Qt::green) );       break;
	}

	double *x = new double[volCount];
	double *y = new double[volCount];
	double mean = ts->mean();

	setAxisAutoScale(QwtPlot::yLeft);

	if(!m_demean)
	{
		for(int n = 0;n < volCount;++n)
		{
			*(x + n) = n;
			*(y + n) = ts->value(n);
		}
	}
	else
	{
		for(int n = 0;n < volCount;++n)
		{
			*(x + n) = n;
			if (!m_percent)
				*(y + n) = ts->value(n) - mean;
			else
				*(y + n) = ((ts->value(n) - mean) / mean) * 100.0;
		}
	}

	curve->setData(x, y, volCount);
	curve->attach(this);

	delete [] x;
	delete [] y;
}

void SingleSeriesPlot::selectNearestCurve(const QwtDoublePoint& p)
{
	double dist;
	int x( transform(QwtPlot::xBottom, p.x()) );
	int y( transform(QwtPlot::yLeft, p.y()) );

	CurveData::Handle closestCurve(m_curveDataList->closestCurve(x, y, dist));
	if(dist < 10)
	{
		m_curveDataList->setAllInActive();
		setActiveCurve(closestCurve, true);
		startPlotProcess();
	}
}

void SingleSeriesPlot::drawMarker(const QwtDoublePoint& p)
{
    CurveData::Handle activeCurve = m_curveDataList->getActiveData();
	if(isValidCurveData(activeCurve))
	{
		float x( short(p.x()) );
		float y( activeCurve->inqYValue(x) );

		if(m_demean) {
			float mean(activeCurve->inqTimeSeries()->mean());
			if(!m_percent)
				y = y - mean;
			else
				y = ((y - mean) / mean) * 100.0;
		}

		emit intensityChanged(x, y);

		m_marker->setValue(x, y);
		m_marker->setVisible(true);

		replot();
		setCursorVolume(x);
	}
}

//void SingleSeriesPlot::mouseMoved(const QMouseEvent & e)
//{
//  TRACKER("SingleSeriesPlot::mouseMoved");
//  if(m_options->inqFeedback())
//    {
//      drawMarker(e.x(),e.y());
//      setCursorVolume(short(invTransform(QwtPlot::xBottom,e.x())));
//    }
//}
//
//void SingleSeriesPlot::mouseReleased(const QMouseEvent & e)
//{
//  TRACKER("SingleSeriesPlot::mouseReleased");
//  if(m_options->inqFeedback())
//    {
//      drawMarker(e.x(),e.y());
//      setCursorVolume(short(invTransform(QwtPlot::xBottom,e.x())));
//    }
//}
//
//void SingleSeriesPlot::mousePressed(const QMouseEvent & e)
//{
//  TRACKER("SingleSeriesPlot::mousePressed");
//  if(m_options->inqFeedback())
//    {
//      double dist;
//      CurveData::Handle closeCurve(m_curveDataList->closestCurve(e.x(),e.y(),dist));
//      if(dist < 10)
//        {
//          m_curveDataList->setAllInActive();
//          setActiveCurve(closeCurve,true);
//          startPlotProcess();
//        }
//      else
//        {
//          drawMarker(e.x(),e.y());
//          setCursorVolume(short(invTransform(QwtPlot::xBottom,e.x())));
//        }
//    }
//}

void SingleSeriesPlot::setActiveCurve(CurveData::Handle curve,bool setCursor)
{  
  TRACKER("SingleSeriesPlot::setActiveCurve");
 if(isValidCurveData(curve))
    {
      curve->setIsActive(true);
      TimeSeries::Handle ts = curve->inqTimeSeries();
      if(setCursor)
        {
          m_causedCursorUpdate = true;
          m_cursor->setCursor(ts->inqX()-m_options->inqXOffset(),
                              ts->inqY()-m_options->inqYOffset(),
                              ts->inqZ()-m_options->inqZOffset());

                  
          m_causedCursorUpdate = false;
        }
    }
}

void SingleSeriesPlot::setCursorVolume(short vol)
{   
  TRACKER("SingleSeriesPlot::setCursorVolume");
  CurveData::Handle active = m_curveDataList->getActiveData();      
  
  if(isValidCurveData(active))
    {
      TimeSeries::Handle ts = active->inqTimeSeries();
      m_causedCursorUpdate = true;
      m_cursor->setCursor(ts->inqX()-m_options->inqXOffset(),
                          ts->inqY()-m_options->inqYOffset(),
                          ts->inqZ()-m_options->inqZOffset(),
                          vol);
      m_causedCursorUpdate = false;
    }
}

void SingleSeriesPlot::redraw()
{  
  TRACKER("SingleSeriesPlot::redraw");
  startPlotProcess();
}

void SingleSeriesPlot::addTimeSeries()
{ 
  TRACKER("SingleSeriesPlot::addTimeSeries");
 
  if(m_enabled)
    {
      if (m_image->getInfo()->isValidCoordinate(
                  m_cursor->inqX() + m_options->inqXOffset(),
                  m_cursor->inqY() + m_options->inqYOffset(),
                  m_cursor->inqZ() + m_options->inqZOffset()))
        {

          if(!m_options->inqFeatMode())
           {
              TimeSeries::Handle timeSeries =  
                m_image->getTimeSeries(m_cursor->inqX() + m_options->inqXOffset(),
                                       m_cursor->inqY() + m_options->inqYOffset(),
                                       m_cursor->inqZ() + m_options->inqZOffset());
              addTimeSeries(timeSeries,false);
            }

          setAllInActive();
          redraw();
        }
    }
}

void SingleSeriesPlot::remTimeSeries()
{  
  TRACKER("SingleSeriesPlot::remTimeSeries");
  if(m_enabled)
    {
      remTimeSeries(false);
      setLastCurveActive(true);
      redraw();
    }
}

void SingleSeriesPlot::update(ModelFit *m)
{
  update(m_cursor);
}

void SingleSeriesPlot::update(const Cursor::Handle& c)
{  
	TRACKER("SingleSeriesPlot::update Cursor");
	if(m_enabled)
	{
		unsigned short x(m_cursor->inqX() + m_options->inqXOffset());
		unsigned short y(m_cursor->inqY() + m_options->inqYOffset());
		unsigned short z(m_cursor->inqZ() + m_options->inqZOffset());

		MESSAGE(QString("Location x(%1) y(%2) z(%3)").arg(x).arg(y).arg(z));

		bool validTimeSeries = m_image->getInfo()->isValidCoordinate(x, y, z);

		if(!inqCausedCursorUpdate())
		{
			if(validTimeSeries)
			{
				if(m_options->inqFeatMode())
				{
					remTimeSeries(true);
					remTimeSeries(true);
					remTimeSeries(true);
					//remTimeSeries(true);
					//remTimeSeries(true);

					ModelFit::Handle model(m_options->getModelFit());

					TimeSeries::Handle timeSeries(m_image->getTimeSeries(x, y, z));
					addFeatSeries(timeSeries, CurveData::FiltFunc);

					if(m_options->showFull())
						addFeatSeries(model->fullModel(x, y, z, timeSeries->mean()), CurveData::Full);

					if(m_options->showPartial())
						if(model->copePe())
							addFeatSeries(model->CopeCurve(x, y, z, timeSeries->mean()), CurveData::Cope1);
						else
							addFeatSeries(model->peCurve(x, y, z, timeSeries->mean()), CurveData::PE);

					// 		  addFeatSeries(model->perCentChange(x, y, z, timeSeries->mean()), CurveData::Cope3);
				}
				else
				{
					remTimeSeries(true);
					if(addTimeSeries(m_image->getTimeSeries(x, y, z), true))
						setLastCurveActive(false);
				}
			}
			redraw();
		}
	}
}

bool SingleSeriesPlot::addFeatSeries(const TimeSeries::Handle & ts,
                                       int index)
{  
  TRACKER("SingleSeriesPlot::addFeatSeries");
  bool browse(true);
  CurveData::Handle curveData = CurveData::create(ts,browse,index);
  return m_curveDataList->push_back(curveData);
}

struct PlotOptions::Implementation
{
  Implementation()
  {
    m_title = true;
    m_xGrid = true;
    m_yGrid = true;
    m_xNums = true;
    m_yNums = true;
    m_xLabel = true;
    m_yLabel = true;
    m_xOffset = 0;
    m_yOffset = 0;
    m_zOffset = 0;
    m_feedback = true;
    m_addRemEnabled = true;
    m_modelEnabled = true;
  };  

  bool m_title;
  bool m_xGrid;
  bool m_yGrid;
  bool m_xNums;
  bool m_yNums;
  bool m_xLabel;
  bool m_yLabel;
  int  m_xOffset;
  int  m_yOffset;
  int  m_zOffset;
  bool m_feedback;
  bool m_addRemEnabled;
  ModelFit::Handle m_modelFit;
  bool m_modelEnabled;
  bool m_showFull;
  bool m_showPartial;
};
  
bool PlotOptions::inqTitle() {return m_impl->m_title;}
bool PlotOptions::inqXGrid() {return m_impl->m_xGrid;}
bool PlotOptions::inqYGrid() {return m_impl->m_yGrid;}
bool PlotOptions::inqXNums() {return m_impl->m_xNums;}
bool PlotOptions::inqYNums() {return m_impl->m_yNums;}
bool PlotOptions::inqXLabel()  {return m_impl->m_xLabel;}
bool PlotOptions::inqYLabel()  {return m_impl->m_yLabel;}
int  PlotOptions::inqXOffset() {return m_impl->m_xOffset;}
int  PlotOptions::inqYOffset() {return m_impl->m_yOffset;}
int  PlotOptions::inqZOffset() {return m_impl->m_zOffset;}
bool PlotOptions::inqFeedback() {return m_impl->m_feedback;}
bool PlotOptions::inqFeatMode() {return m_impl->m_modelFit && m_impl->m_modelEnabled;}
bool PlotOptions::inqAddRemEnabled(){return m_impl->m_addRemEnabled;}
  
ModelFit::Handle& PlotOptions::getModelFit(){return m_impl->m_modelFit;}

void PlotOptions::showFull(bool y) {  m_impl->m_showFull=y; /*notify();*/ }
bool PlotOptions::showFull(void) const { return m_impl->m_showFull; }

void PlotOptions::showPartial(bool y) {  m_impl->m_showPartial=y; /*notify();*/ }
bool PlotOptions::showPartial(void) const { return m_impl->m_showPartial; }

void PlotOptions::setTitle(bool state)    
{
  m_impl->m_title = state;
}

void PlotOptions::setGrids(bool x, bool y)
{
  m_impl->m_xGrid = x;
  m_impl->m_yGrid = y;
}
void PlotOptions::setNums(bool x, bool y) 
{
  m_impl->m_xNums = x;
  m_impl->m_yNums = y;
}
void PlotOptions::setLabels(bool x, bool y)
{
  m_impl->m_xLabel = x;
  m_impl->m_yLabel = y;
}
void PlotOptions::setOffsets(int x,int y,int z)
{
  m_impl->m_xOffset = x;
  m_impl->m_yOffset = y;
  m_impl->m_zOffset = z;
}
void PlotOptions::setFeedBack(bool state)
{
  m_impl->m_feedback = state;
}

void PlotOptions::setFeatMode(bool y)
{
  m_impl->m_modelEnabled = y;
}


void PlotOptions::setModelFit(ModelFit::Handle& model)
{
  m_impl->m_modelFit = model;
  m_impl->m_addRemEnabled = false;
}

PlotOptions::PlotOptions():
  m_impl(new Implementation)
{ 
 
}

PlotOptions::~PlotOptions(){}

PlotOptions::Handle PlotOptions::create()
{
  Handle dst(new PlotOptions());
  return dst;
}

