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

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

#include "QPulsePlot.h"
#include "DataRequestWidget.h"

#include "cdm/CommonDataModel.h"
#include "PulsePhysiologyEngine.h"
#include "cdm/engine/SEEngineTracker.h"
#include "cdm/engine/SEDataRequest.h"
#include "cdm/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(QTextEdit& log) : LogBox(log) {}
  QTextEdit&                         LogBox;
  QDataRequestWidget*                DataRequestWidget;
  size_t                             CurrentPlot = -1;
  std::vector<QPulsePlot*>           Plots;
  std::vector<SEDataRequest*>        DataRequests;

};

QPlotSetWidget::QPlotSetWidget(QPulse& qp, QWidget *parent, Qt::WindowFlags flags) : QDockWidget(parent, flags)
{
  m_Controls = new Controls(qp.GetLogBox());
  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::Reset()
{
  for (QPulsePlot* plot : m_Controls->Plots)
    delete plot;
  m_Controls->Plots.clear();
  m_Controls->DataRequests.clear();
  m_Controls->PlotComboBox->clear();
  m_Controls->PlotComboBox->hide();
  m_Controls->DataRequestWidget->show();
  m_Controls->PlotSetButtons->hide();
  m_Controls->DataRequestButtons->show();
}

void QPlotSetWidget::AddDataRequest()
{
  SEDataRequest& dr = m_Controls->DataRequestWidget->GetDataRequest();
  QString title = GetTitle(dr).c_str();
  int idx=-1;
  for(; idx<m_Controls->Plots.size(); idx++)
    if (m_Controls->Plots[idx]->GetChart().title() == title)
      break;
  if (idx == -1)
  {
    idx = (int)m_Controls->Plots.size();
    QPulsePlot* newPlot = new QPulsePlot(1000);
    m_Controls->Plots.push_back(newPlot);
    m_Controls->DataRequests.push_back(&dr);
    newPlot->GetChart().setTitle(dr.GetPropertyName().c_str());
    m_Controls->PlotComboBox->addItem(title);
    m_Controls->PlotContainer->layout()->addWidget(&newPlot->GetView());
  }
  // 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::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 (QPulsePlot* plot : m_Controls->Plots)
    plot->GetView().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->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 (QPulsePlot* plot : m_Controls->Plots)
    plot->GetView().hide();
  m_Controls->Plots[idx]->GetView().show();
}

void QPlotSetWidget::AtSteadyState(PhysiologyEngine& pulse)
{
  
}
void QPlotSetWidget::AtSteadyStateUpdateUI()
{
  Reset();
}

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;
  SEDataRequest* dr;
  double time = pulse.GetSimulationTime(TimeUnit::s);
  // The DataRequestView updated all DataRequests,
  // Now just push the values into the plots
  for (size_t i = 0; i < m_Controls->Plots.size(); i++)
  {
    dr = m_Controls->DataRequests[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();
  }
}