/* Distributed under the Apache License, Version 2.0.
See accompanying NOTICE file for details.*/
#include "MultiTraumaShowcaseWidget.h"
#include "ui_MultiTraumaShowcase.h"
#include <QMessageBox>

#include "pulse/PulsePhysiologyEngine.h"
#include "pulse/engine/SEDataRequestManager.h"
#include "pulse/engine/SEActionManager.h"
#include "pulse/engine/SEPatientActionCollection.h"
#include "pulse/engine/SEEngineTracker.h"
#include "pulse/properties/SEScalarTime.h"
#include "pulse/properties/SEScalar0To1.h"
#include "pulse/properties/SEScalarVolume.h"
#include "pulse/properties/SEScalarMassPerVolume.h"
#include "pulse/properties/SEScalarVolumePerTime.h"
#include "pulse/substance/SESubstance.h"
#include "pulse/substance/SESubstanceManager.h"
#include "pulse/patient/actions/SESubstanceBolus.h"
#include "pulse/patient/actions/SESubstanceCompoundInfusion.h"
#include "pulse/patient/actions/SEHemorrhage.h"
#include "pulse/patient/actions/SETensionPneumothorax.h"
#include "pulse/patient/actions/SENeedleDecompression.h"

class QMultiTraumaShowcaseWidget::Controls : public Ui::MultiTraumaShowcaseWidget
{
public:
  Controls(QPulse& qp) : Pulse(qp) {}
  QPulse&                    Pulse;
  double                     HemorrhageRate_mL_Per_min = 0;
  double                     PneumothoraxSeverity = 0;
  eSide                      PneumothoraxSide;
  bool                       ApplyHemorrhage = false;
  bool                       ApplyPneumothorax = false;
  bool                       ApplyPressure = false;
  bool                       ApplyNeedleDecompression = false;
  bool                       ApplyTourniquet = false;
  bool                       InfuseSaline = false;
  bool                       UpdateBagVolume = false;
  bool                       ZeroBagVolume = false;
  double                     BagVolume_mL = 0;
  double                     InfusionRate_mL_Per_min = 0;
  bool                       InjectMorphine = false;
  const SESubstanceCompound* Saline = nullptr;
};

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

  connect(m_Controls->ApplyHemorrhageButton, SIGNAL(clicked()), this, SLOT(ApplyHemorrhage()));
  connect(m_Controls->ApplyPneumoButton, SIGNAL(clicked()), this, SLOT(ApplyPneumothorax()));
  connect(m_Controls->ApplyPressureButton, SIGNAL(clicked()), this, SLOT(ApplyPressure()));
  connect(m_Controls->NeedleDecompressButton, SIGNAL(clicked()), this, SLOT(ApplyNeedleDecompression()));
  connect(m_Controls->ApplyTournyButton, SIGNAL(clicked()), this, SLOT(ApplyTourniquet()));
  connect(m_Controls->InfuseSalineButton, SIGNAL(clicked()), this, SLOT(InfuseSaline()));
  connect(m_Controls->InjectMorphineButton, SIGNAL(clicked()), this, SLOT(InjectMorphine()));
}

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

bool QMultiTraumaShowcaseWidget::ConfigurePulse(PhysiologyEngine& pulse, QDataRequestViewWidget& drWidget)
{
  if (!pulse.SerializeFromFile("./states/Soldier@0s.pbb"))
  {
    QMessageBox msgBox(this);
    msgBox.setWindowTitle("Error!");
    QString err = "Unable to load engine state : ./states/Soldier@0s.pbb";
    msgBox.setText(err);
    msgBox.exec();
    return false;
  }
  m_Controls->Pulse.SetEngineStateFilename("./states/Soldier@0s.pbb");

  m_Controls->HemorrhageRate_mL_Per_min = 0;
  m_Controls->PneumothoraxSeverity = 0;
  m_Controls->ApplyHemorrhage = false;
  m_Controls->ApplyPneumothorax = false;
  m_Controls->ApplyPressure = false;
  m_Controls->ApplyNeedleDecompression = false;
  m_Controls->ApplyTourniquet = false;
  m_Controls->InfuseSaline = false;
  m_Controls->UpdateBagVolume = false;
  m_Controls->ZeroBagVolume = false;
  m_Controls->InjectMorphine = false;

  m_Controls->ApplyHemorrhageButton->setEnabled(true);
  m_Controls->FlowRateEdit->setEnabled(true);
  m_Controls->ApplyPneumoButton->setEnabled(true);
  m_Controls->SeveritySpinBox->setEnabled(true);
  m_Controls->PneumothoraxTypeCombo->setEnabled(true);
  m_Controls->ApplyPressureButton->setEnabled(false);
  m_Controls->NeedleDecompressButton->setEnabled(false);
  m_Controls->ApplyTournyButton->setEnabled(false);
  m_Controls->InfuseSalineButton->setEnabled(false);
  m_Controls->BagVolumeEdit->setEnabled(false);
  m_Controls->InfusionRateEdit->setEnabled(false);
  m_Controls->InjectMorphineButton->setEnabled(false);

  m_Controls->Pulse.GetLogBox().append("Combining the tension pneumothorax with the blood loss from the hemorrhage pushes and eventually exceeds the limits of the homeostatic control mechanisms.");
  m_Controls->Pulse.ScrollLogBox();
  // Fill out any data requsts that we want to have plotted
  drWidget.CreatePlot(pulse.GetEngineTracker()->GetDataRequestManager().CreatePhysiologyDataRequest("BloodVolume", VolumeUnit::L));
  drWidget.CreatePlot(pulse.GetEngineTracker()->GetDataRequestManager().CreatePhysiologyDataRequest("TidalVolume", VolumeUnit::mL));
  drWidget.CreatePlot(pulse.GetEngineTracker()->GetDataRequestManager().CreatePhysiologyDataRequest("CardiacOutput", VolumePerTimeUnit::L_Per_min));
  drWidget.CreatePlot(pulse.GetEngineTracker()->GetDataRequestManager().CreateGasCompartmentDataRequest(pulse::PulmonaryCompartment::LeftLung, "Volume", VolumeUnit::mL));
  drWidget.CreatePlot(pulse.GetEngineTracker()->GetDataRequestManager().CreateGasCompartmentDataRequest(pulse::PulmonaryCompartment::RightLung, "Volume", VolumeUnit::mL));
  drWidget.CreatePlot(pulse.GetEngineTracker()->GetDataRequestManager().CreateSubstanceDataRequest("Morphine", "PlasmaConcentration", MassPerVolumeUnit::ug_Per_mL));

  m_Controls->Saline = pulse.GetSubstanceManager().GetCompound("Saline");
  return true;
}

void QMultiTraumaShowcaseWidget::ProcessPhysiology(PhysiologyEngine& pulse)
{
  // This is where we pull data from pulse, and push any actions to it
  if (m_Controls->ApplyPneumothorax)
  {
    m_Controls->ApplyPneumothorax = false;
    SETensionPneumothorax pneumothorax;
    pneumothorax.SetSide(m_Controls->PneumothoraxSide);
    pneumothorax.SetType(eGate::Closed);
    pneumothorax.GetSeverity().SetValue(m_Controls->PneumothoraxSeverity);
    pulse.ProcessAction(pneumothorax);
  }
  if (m_Controls->ApplyNeedleDecompression)
  {
    m_Controls->ApplyNeedleDecompression = false;
    SENeedleDecompression needle;
    needle.SetState(eSwitch::On);
    needle.SetSide(m_Controls->PneumothoraxSide);
    pulse.ProcessAction(needle);
  }

  if (m_Controls->ApplyHemorrhage)
  {
    m_Controls->ApplyHemorrhage = false;
    SEHemorrhage hemorrhage;
    hemorrhage.GetFlowRate().SetValue(m_Controls->HemorrhageRate_mL_Per_min, VolumePerTimeUnit::mL_Per_min);
    hemorrhage.SetCompartment(pulse::VascularCompartment::RightLeg);
    pulse.ProcessAction(hemorrhage);
  }
  if (m_Controls->ApplyPressure)
  {
    m_Controls->ApplyPressure = false;
    SEHemorrhage hemorrhage;
    hemorrhage.GetFlowRate().SetValue(m_Controls->HemorrhageRate_mL_Per_min*0.15, VolumePerTimeUnit::mL_Per_min);
    hemorrhage.SetCompartment(pulse::VascularCompartment::RightLeg);
    pulse.ProcessAction(hemorrhage);
  }
  if (m_Controls->ApplyTourniquet)
  {
    m_Controls->ApplyTourniquet = false;
    SEHemorrhage hemorrhage;
    hemorrhage.GetFlowRate().SetValue(0,VolumePerTimeUnit::mL_Per_min);
    hemorrhage.SetCompartment(pulse::VascularCompartment::RightLeg);
    pulse.ProcessAction(hemorrhage);
  }

  if (m_Controls->InfuseSaline)
  {
    m_Controls->InfuseSaline = false;
    m_Controls->UpdateBagVolume = true;
    SESubstanceCompoundInfusion SalineInfusion(*m_Controls->Saline);
    SalineInfusion.GetBagVolume().SetValue(m_Controls->BagVolume_mL, VolumeUnit::mL);
    SalineInfusion.GetRate().SetValue(m_Controls->InfusionRate_mL_Per_min, VolumePerTimeUnit::mL_Per_min);
    pulse.ProcessAction(SalineInfusion);
  }
  if (m_Controls->UpdateBagVolume)
  {
     const SESubstanceCompoundInfusion* sci = pulse.GetActionManager().GetPatientActions().GetSubstanceCompoundInfusion(*m_Controls->Saline);
     if (sci != nullptr && sci->HasBagVolume())
     {
       m_Controls->BagVolume_mL = sci->GetBagVolume(VolumeUnit::mL);
     }
     else
     {
       m_Controls->UpdateBagVolume = false;
       m_Controls->ZeroBagVolume = true;
     }
  }
  if (m_Controls->InjectMorphine)
  {
    m_Controls->InjectMorphine = false;
    const SESubstance* Morphine = pulse.GetSubstanceManager().GetSubstance("Morphine");
    SESubstanceBolus   MorphineBolus(*Morphine);
    MorphineBolus.GetConcentration().SetValue(1000, MassPerVolumeUnit::ug_Per_mL);
    MorphineBolus.GetDose().SetValue(5.0, VolumeUnit::mL);
    MorphineBolus.SetAdminRoute(eSubstanceAdministration_Route::Intravenous);
    pulse.ProcessAction(MorphineBolus);
  }
}

// NOTE 
// Since we Pulse is running in a seperate thread from Qt signals for button clicks
// DO NOT add actions to Pulse in slots
// flip a flag and add them in your thread!!

void QMultiTraumaShowcaseWidget::ApplyHemorrhage()
{
  m_Controls->HemorrhageRate_mL_Per_min = m_Controls->FlowRateEdit->value();
  m_Controls->ApplyHemorrhage = true;
  m_Controls->ApplyHemorrhageButton->setDisabled(true);
  m_Controls->FlowRateEdit->setDisabled(true);
  m_Controls->ApplyPressureButton->setEnabled(true);
  m_Controls->Pulse.GetLogBox().append("Applying hemorrhage");
  m_Controls->Pulse.ScrollLogBox();
}

void QMultiTraumaShowcaseWidget::ApplyPneumothorax()
{
  m_Controls->PneumothoraxSide = m_Controls->PneumothoraxTypeCombo->currentIndex()==0?eSide::Left:eSide::Right;
  m_Controls->PneumothoraxSeverity = m_Controls->SeveritySpinBox->value();
  m_Controls->ApplyPneumothorax = true;
  m_Controls->ApplyPneumoButton->setDisabled(true);
  m_Controls->SeveritySpinBox->setDisabled(true);
  m_Controls->PneumothoraxTypeCombo->setDisabled(true);
  m_Controls->NeedleDecompressButton->setEnabled(true);
  m_Controls->Pulse.GetLogBox().append("Applying Pneumothorax");
  m_Controls->Pulse.ScrollLogBox();
}

void QMultiTraumaShowcaseWidget::ApplyPressure()
{
  m_Controls->ApplyPressure = true;
  m_Controls->ApplyPressureButton->setDisabled(true);
  m_Controls->ApplyTournyButton->setEnabled(true);
  m_Controls->Pulse.GetLogBox().append("Applying pressure to the wound");
  m_Controls->Pulse.ScrollLogBox();
}

void QMultiTraumaShowcaseWidget::ApplyNeedleDecompression()
{
  m_Controls->ApplyNeedleDecompression = true;
  m_Controls->NeedleDecompressButton->setEnabled(false);
  m_Controls->Pulse.GetLogBox().append("Applying Needle Decompression");
  m_Controls->Pulse.ScrollLogBox();
}

void QMultiTraumaShowcaseWidget::ApplyTourniquet()
{
  m_Controls->ApplyTourniquet = true;
  m_Controls->ApplyTournyButton->setEnabled(false);
  m_Controls->InfuseSalineButton->setEnabled(true);
  m_Controls->BagVolumeEdit->setEnabled(true);
  m_Controls->InfusionRateEdit->setEnabled(true);
  m_Controls->InjectMorphineButton->setEnabled(true);
  m_Controls->Pulse.GetLogBox().append("Applying Tourniquet");
  m_Controls->Pulse.ScrollLogBox();
}

void QMultiTraumaShowcaseWidget::InfuseSaline()
{
  m_Controls->InfuseSaline = true;
  m_Controls->BagVolume_mL = m_Controls->BagVolumeEdit->value();
  m_Controls->InfusionRate_mL_Per_min = m_Controls->InfusionRateEdit->value();
  m_Controls->InfuseSalineButton->setEnabled(false);
  m_Controls->BagVolumeEdit->setEnabled(false);
  m_Controls->InfusionRateEdit->setEnabled(false);
  m_Controls->Pulse.GetLogBox().append("Infusing saline");
  m_Controls->Pulse.ScrollLogBox();
}

void QMultiTraumaShowcaseWidget::InjectMorphine()
{
  m_Controls->InjectMorphine = true;
  m_Controls->InjectMorphineButton->setEnabled(false);
  m_Controls->Pulse.GetLogBox().append("Injecting a bolus of morphine");
  m_Controls->Pulse.ScrollLogBox();
}

void QMultiTraumaShowcaseWidget::PhysiologyUpdateUI()
{
  if (m_Controls->UpdateBagVolume)
    m_Controls->BagVolumeEdit->setValue(m_Controls->BagVolume_mL);
  if (m_Controls->ZeroBagVolume)
  {
    m_Controls->ZeroBagVolume = false;
    m_Controls->BagVolumeEdit->setValue(0);
  }
}