/* Distributed under the Apache License, Version 2.0.
See accompanying NOTICE file for details.*/

#include "PlotSetWidget.h"
#include "ui_PlotSet.h"

#include "QwtPulsePlot.h"
#include "DataRequestWidget.h"

#include "PulsePhysiologyEngine.h"
#include "PulseScenario.h"
#include "engine/SEEngineTracker.h"
#include "engine/SEDataRequestManager.h"
#include "properties/SEScalarTime.h"

// TODO 
// I took this from SEEngineTrack, this method should go in there
// (I did change the delimeter from "-" to " ")
std::string GetTitle(SEDataRequest& dr)
{
  std::stringstream ss;
  switch (dr.GetCategory())
  {
  case eDataRequest_Category::Patient:
    ss << "Patient";
  case eDataRequest_Category::Physiology:
  case eDataRequest_Category::Environment:
  case eDataRequest_Category::AnesthesiaMachine:
  case eDataRequest_Category::ECG:
  case eDataRequest_Category::Inhaler:
  {
    if (!dr.GetUnit())
      ss << dr.GetPropertyName();
    else
      ss << dr.GetPropertyName() << "(" << dr.GetUnit() << ")";
    break;
  }
  case eDataRequest_Category::GasCompartment:
  case eDataRequest_Category::LiquidCompartment:
  case eDataRequest_Category::ThermalCompartment:
  case eDataRequest_Category::TissueCompartment:
  {
    if (dr.HasSubstanceName())
    {
      if (!dr.GetUnit())
        ss << dr.GetCompartmentName() << " " << dr.GetSubstanceName() << " " << dr.GetPropertyName();
      else
        ss << dr.GetCompartmentName() << " " << dr.GetSubstanceName() << " " << dr.GetPropertyName() << "(" << dr.GetUnit() << ")";
    }
    else
    {
      if (!dr.GetUnit())
        ss << dr.GetCompartmentName() << " " << dr.GetPropertyName();
      else
        ss << dr.GetCompartmentName() << " " << dr.GetPropertyName() << "(" << dr.GetUnit() << ")";
    }
    break;
  }
  case eDataRequest_Category::Substance:
  {
    if (dr.HasCompartmentName())
    {
      if (!dr.GetUnit())
        ss << dr.GetSubstanceName() << " " << dr.GetCompartmentName() << " " << dr.GetPropertyName();
      else
        ss << dr.GetSubstanceName() << " " << dr.GetCompartmentName() << " " << dr.GetPropertyName() << "(" << dr.GetUnit() << ")";
    }
    else
    {
      if (!dr.GetUnit())
        ss << dr.GetSubstanceName() << " " << dr.GetPropertyName();
      else
        ss << dr.GetSubstanceName() << " " << dr.GetPropertyName() << "(" << dr.GetUnit() << ")";
    }
    break;
  }
  default:
    ss << "Unhandled data request category: " << eDataRequest_Category_Name(dr.GetCategory()) << std::endl;
  }
  return ss.str();
}

class QPlotSetWidget::Controls : public Ui::PlotSetWidget
{
public:
  Controls(SEDataRequestManager& drMgr) : DataRequestManager(drMgr) {}
  SEDataRequestManager&              DataRequestManager;
  QDataRequestWidget*                DataRequestWidget;
  size_t                             CurrentPlot = -1;
  std::vector<QwtPulsePlot*>         Plots;
  std::vector<const SEDataRequest*>  DataRequests;
  std::vector<const SEDataRequest*>  EngineDataRequests;

};

QPlotSetWidget::QPlotSetWidget(QPulse& qp, SEDataRequestManager& drMgr, QWidget *parent, Qt::WindowFlags flags) : QDockWidget(parent, flags)
{
  m_Controls = new Controls(drMgr);
  m_Controls->setupUi(this);

  m_Controls->DataRequestWidget = new QDataRequestWidget(qp, this);
  m_Controls->PlotContainer->layout()->addWidget(m_Controls->DataRequestWidget);
  m_Controls->DataRequestWidget->setTitleBarWidget(new QWidget());
  m_Controls->PlotSetButtons->hide();
  m_Controls->DataRequestButtons->show();
  m_Controls->DataRequestWidget->show();
  m_Controls->PlotComboBox->hide();

  connect(m_Controls->PlotComboBox, SIGNAL(currentIndexChanged(int)),SLOT(SwitchPlot()));
  connect(m_Controls->AddPlotButton, SIGNAL(clicked()),SLOT(AddPlot()));
  connect(m_Controls->RemovePlotButton, SIGNAL(clicked()), SLOT(RemovePlot()));
  connect(m_Controls->AddDRButton, SIGNAL(clicked()), SLOT(AddDataRequest()));
  connect(m_Controls->CancelDRButton, SIGNAL(clicked()), SLOT(CancelDataRequest()));

  this->setWindowTitle(QString(""));
}

QPlotSetWidget::~QPlotSetWidget()
{
  delete m_Controls;
}

void QPlotSetWidget::Clear()
{
  for (QwtPulsePlot* plot : m_Controls->Plots)
     plot->Clear();
  m_Controls->EngineDataRequests.clear();
}

void QPlotSetWidget::AddDataRequest(SEDataRequest& dr)
{
  QString title = GetTitle(dr).c_str();
  int idx=-1;
  for(; idx<m_Controls->Plots.size(); idx++)
    if (m_Controls->Plots[idx]->GetPlot().title() == title)
      break;
  if (idx == -1)
  {
    idx = (int)m_Controls->Plots.size();
    QwtPulsePlot* newPlot = new QwtPulsePlot(this,1000);
    QPalette pal = newPlot->GetPlot().palette();
    pal.setColor(QPalette::Window, Qt::white);
    newPlot->GetPlot().setPalette(pal);
    newPlot->GetPlot().setAutoFillBackground(true);
    newPlot->GetCurve().setPen(Qt::blue, 2);
    newPlot->GetGrid().setPen(Qt::gray);
    newPlot->GetGrid().enableX(true);
    newPlot->GetGrid().enableY(true);
    m_Controls->Plots.push_back(newPlot);
    m_Controls->DataRequests.push_back(&dr);
    newPlot->GetPlot().setTitle(dr.GetPropertyName().c_str());
    m_Controls->PlotComboBox->addItem(title);
    m_Controls->PlotContainer->layout()->addWidget(&newPlot->GetPlot());
  }
  // Update Controls
  m_Controls->PlotComboBox->show();
  m_Controls->PlotComboBox->setCurrentIndex(idx);
  m_Controls->DataRequestWidget->hide();
  m_Controls->PlotSetButtons->show();
  m_Controls->DataRequestButtons->hide();
}

void QPlotSetWidget::AddDataRequest()
{
  SEDataRequest& dr = m_Controls->DataRequestWidget->GetDataRequest(m_Controls->DataRequestManager);
  this->AddDataRequest(dr);
}

void QPlotSetWidget::CancelDataRequest()
{
  m_Controls->DataRequestWidget->hide();
  m_Controls->PlotSetButtons->show();
  m_Controls->DataRequestButtons->hide();
}

void QPlotSetWidget::AddPlot()
{
  m_Controls->PlotComboBox->hide();
  m_Controls->DataRequestWidget->show();
  m_Controls->PlotSetButtons->hide();
  m_Controls->DataRequestButtons->show();
  for (QwtPulsePlot* plot : m_Controls->Plots)
    plot->GetPlot().hide();
}
void QPlotSetWidget::RemovePlot()
{
  int idx = m_Controls->PlotComboBox->currentIndex();
  delete m_Controls->Plots[idx];
  m_Controls->Plots.erase(m_Controls->Plots.begin() + idx);
  m_Controls->DataRequests.erase(m_Controls->DataRequests.begin() + idx);
  m_Controls->EngineDataRequests.erase(m_Controls->EngineDataRequests.begin() + idx);
  m_Controls->PlotComboBox->removeItem(idx);
  if (m_Controls->Plots.size() == 0)
    AddPlot();
  else
    m_Controls->PlotComboBox->setCurrentIndex(idx);
}

void QPlotSetWidget::SwitchPlot()
{
  int idx = m_Controls->PlotComboBox->currentIndex();
  if (m_Controls->Plots.size() == 0)
    return;
  for (QwtPulsePlot* plot : m_Controls->Plots)
    plot->GetPlot().hide();
  m_Controls->Plots[idx]->GetPlot().show();
}

void QPlotSetWidget::AtSteadyState(PhysiologyEngine& pulse)
{

}
void QPlotSetWidget::AtSteadyStateUpdateUI()
{

}

void QPlotSetWidget::ProcessPhysiology(PhysiologyEngine& pulse)
{// This is called from a thread, you should NOT update UI here
  // This is where we pull data from pulse, and push any actions to it
  double v;
  const SEDataRequest* dr;
  double time = pulse.GetSimulationTime(TimeUnit::s);
  if (m_Controls->DataRequests.size() != m_Controls->EngineDataRequests.size())
  {
    // New Data Request!
    for (size_t i = 0; i < m_Controls->DataRequests.size(); i++)
    {
      if (i+1 > m_Controls->EngineDataRequests.size())
      {
        SEDataRequest const* dr = m_Controls->DataRequests[i];
        m_Controls->EngineDataRequests.push_back(&pulse.GetEngineTracker()->GetDataRequestManager().CopyDataRequest(*dr));
      }
    }
  }
  // Push the values into the plots
  for (size_t i = 0; i < m_Controls->Plots.size(); i++)
  {
    dr = m_Controls->EngineDataRequests[i];
    v = pulse.GetEngineTracker()->GetValue(*dr);
    m_Controls->Plots[i]->Append(time, v);
  }
}

void QPlotSetWidget::PhysiologyUpdateUI()
{
  if (!m_Controls->PlotComboBox->isHidden())
  {
    int idx = m_Controls->PlotComboBox->currentIndex();
    m_Controls->Plots[idx]->UpdateUI();
  }
}