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

#include "cdm/CommonDataModel.h"
#include "PulsePhysiologyEngine.h"
#include "cdm/engine/SEDataRequestManager.h"
#include "cdm/engine/SEActionManager.h"
#include "cdm/engine/SEPatientActionCollection.h"
#include "cdm/properties/SEScalarTime.h"
#include "cdm/properties/SEScalar0To1.h"
#include "cdm/properties/SEScalarVolume.h"
#include "cdm/properties/SEScalarMassPerVolume.h"
#include "cdm/properties/SEScalarVolumePerTime.h"
#include "cdm/substance/SESubstance.h"
#include "cdm/substance/SESubstanceManager.h"
#include "cdm/patient/actions/SESubstanceBolus.h"
#include "cdm/patient/actions/SESubstanceCompoundInfusion.h"
#include "cdm/patient/actions/SEHemorrhage.h"
#include "cdm/patient/actions/SETensionPneumothorax.h"
#include "cdm/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;
  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, SEDataRequestManager& drMgr)
{
  if (!pulse.SerializeFromFile("./states/Soldier@0s.json", SerializationFormat::JSON))
  {
    QMessageBox msgBox(this);
    msgBox.setWindowTitle("Error!");
    QString err = "Unable to load engine state : ./states/Soldier@0s.json";
    msgBox.setText(err);
    msgBox.exec();
    return false;
  }

  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
  drMgr.CreatePhysiologyDataRequest("BloodVolume", VolumeUnit::L);
  drMgr.CreatePhysiologyDataRequest("TidalVolume", VolumeUnit::mL);
  drMgr.CreatePhysiologyDataRequest("CardiacOutput", VolumePerTimeUnit::L_Per_min);
  drMgr.CreateGasCompartmentDataRequest(pulse::PulmonaryCompartment::LeftLung, "Volume", VolumeUnit::mL);
  drMgr.CreateGasCompartmentDataRequest(pulse::PulmonaryCompartment::RightLung, "Volume", VolumeUnit::mL);
  const SESubstance* Morphine = pulse.GetSubstanceManager().GetSubstance("Morphine");
  drMgr.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.SetActive(true);
    needle.SetSide(m_Controls->PneumothoraxSide);
    pulse.ProcessAction(needle);
  }

  if (m_Controls->ApplyHemorrhage)
  {
    m_Controls->ApplyHemorrhage = false;
    SEHemorrhage hemorrhage;
    hemorrhage.GetRate().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.GetRate().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.GetRate().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)
  {
     auto itr = pulse.GetActionManager().GetPatientActions().GetSubstanceCompoundInfusions().find(m_Controls->Saline);
     if (itr != pulse.GetActionManager().GetPatientActions().GetSubstanceCompoundInfusions().end())
     {
       const SESubstanceCompoundInfusion* si = itr->second;
       m_Controls->BagVolume_mL = si->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);
  }
}