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

#include "cdm/CommonDataModel.h"
#include "PulsePhysiologyEngine.h"
#include "cdm/engine/SEDataRequestManager.h"
#include "cdm/properties/SEScalarTime.h"
#include "cdm/properties/SEScalar0To1.h"
#include "cdm/properties/SEScalarVolume.h"
#include "cdm/properties/SEScalarVolumePerTime.h"
#include "cdm/properties/SEScalarMassPerVolume.h"
#include "cdm/substance/SESubstance.h"
#include "cdm/substance/SESubstanceManager.h"
#include "cdm/substance/SESubstancePharmacodynamics.h"
#include "cdm/patient/actions/SESubstanceBolus.h"
#include "cdm/patient/actions/SEAirwayObstruction.h"
#include "cdm/system/physiology/SEBloodChemistrySystem.h"

class QAnaphylaxisShowcaseWidget::Controls : public Ui::AnaphylaxisShowcaseWidget
{
public:
  Controls(QPulse& qp) : Pulse(qp) {}
  QPulse&              Pulse;
  const SESubstance*   Epinephrine;
  bool                 ApplyAirwayObstruction=false;
  bool                 InjectEpinephrine=false;
  bool                 ReduceAirwayObstruction=false;
  bool                 CheckEC50=false;
  bool                 Finished=false;
  double               SpO2;
  double               Severity=0;
  double               ReduceRatio = 0.1; // Amount to reduce obstruction size per time step.
};

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

  connect(m_Controls->ObsButton, SIGNAL(clicked()), this, SLOT(ApplyAirwayObstruction()));
  connect(m_Controls->EpiButton, SIGNAL(clicked()), this, SLOT(InjectEpinephrine()));
}

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

bool QAnaphylaxisShowcaseWidget::ConfigurePulse(PhysiologyEngine& pulse, SEDataRequestManager& drMgr)
{
  if (!pulse.SerializeFromFile("./states/StandardMale@0s.json", SerializationFormat::JSON))
  {
    QMessageBox msgBox(this);
    msgBox.setWindowTitle("Error!");
    QString err = "Unable to load engine state : ./states/StandardMale@0s.json";
    msgBox.setText(err);
    msgBox.exec();
    return false;
  }

  m_Controls->SeveritySpinBox->setEnabled(true);
  m_Controls->SeveritySpinBox->setUpdatesEnabled(true);
  m_Controls->ObsButton->setEnabled(true);
  m_Controls->EpiButton->setEnabled(false);
  m_Controls->ApplyAirwayObstruction = false;
  m_Controls->InjectEpinephrine = false;
  m_Controls->ReduceAirwayObstruction = false;
  m_Controls->CheckEC50 = false;
  m_Controls->Finished = false;

   m_Controls->Pulse.GetLogBox().append("Anaphylaxis is a serious, potentially life threatening allergic reaction with facial and airway swelling.");
  m_Controls->Pulse.GetLogBox().append("It is an immune response that can occur quickly in response to exposure to an allergen.");
  m_Controls->Pulse.GetLogBox().append("The immune system releases chemicals into the body that cause the blood pressure to drop and the airways to narrow, blocking breathing.");
  m_Controls->Pulse.GetLogBox().append("Anaphylaxis is treated with an injection of epinephrine.");
  m_Controls->Pulse.GetLogBox().append("The anaphylaxis is rapidly reversed by the drug, allowing patient vital signs to return to normal.");
  m_Controls->Pulse.GetLogBox().append("");
  m_Controls->Pulse.GetLogBox().append("To introduce the anaphylaxis state, select a severity and click the 'Apply' Button.");
  m_Controls->Pulse.ScrollLogBox();

  m_Controls->Epinephrine = pulse.GetSubstanceManager().GetSubstance("Epinephrine");

  // Fill out any data requsts that we want to have plotted
  drMgr.CreatePhysiologyDataRequest("TidalVolume", VolumeUnit::mL);
  drMgr.CreatePhysiologyDataRequest("CardiacOutput", VolumePerTimeUnit::L_Per_min);
  drMgr.CreateSubstanceDataRequest(*m_Controls->Epinephrine,"PlasmaConcentration",MassPerVolumeUnit::mg_Per_mL);
  return true;
}

void QAnaphylaxisShowcaseWidget::ProcessPhysiology(PhysiologyEngine& pulse)
{
  // This is where we pull data from pulse, and push any actions to it
  if (m_Controls->ApplyAirwayObstruction)
  {
    m_Controls->ApplyAirwayObstruction = false;
    SEAirwayObstruction AirwayObstuction;
    AirwayObstuction.GetSeverity().SetValue(m_Controls->Severity);
    pulse.ProcessAction(AirwayObstuction);
  }
  if (m_Controls->InjectEpinephrine)
  {
    m_Controls->InjectEpinephrine = false;
    m_Controls->ReduceAirwayObstruction = true;
    SESubstanceBolus   EpinephrineBolus(*m_Controls->Epinephrine);
    EpinephrineBolus.GetConcentration().SetValue(1, MassPerVolumeUnit::g_Per_L);
    EpinephrineBolus.GetDose().SetValue(0.3, VolumeUnit::mL);
    EpinephrineBolus.SetAdminRoute(eSubstanceAdministration_Route::Intravenous);
    pulse.ProcessAction(EpinephrineBolus);
    m_Controls->Pulse.IgnoreAction("Airway Obstruction");
  }
  if (m_Controls->CheckEC50)
  {
    if (m_Controls->Epinephrine->GetPlasmaConcentration(MassPerVolumeUnit::mg_Per_mL) >= m_Controls->Epinephrine->GetPD()->GetEC50(MassPerVolumeUnit::mg_Per_mL))
    {
      m_Controls->CheckEC50 = false;
      m_Controls->ReduceAirwayObstruction = true;
    }
  }
  if (m_Controls->ReduceAirwayObstruction)
  {
    SEAirwayObstruction AirwayObstuction;
    m_Controls->Severity -= (m_Controls->ReduceRatio * m_Controls->Pulse.GetTimeStep_s());
    if (m_Controls->Severity <= 0)
    {
      m_Controls->Severity = 0;
      m_Controls->ReduceAirwayObstruction = false;
      m_Controls->Finished = true;
    }
    AirwayObstuction.GetSeverity().SetValue(m_Controls->Severity);
    pulse.ProcessAction(AirwayObstuction);
  }
}

// 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 QAnaphylaxisShowcaseWidget::ApplyAirwayObstruction()
{
  m_Controls->ApplyAirwayObstruction = true;
  m_Controls->SeveritySpinBox->setEnabled(false);
  m_Controls->Severity = m_Controls->SeveritySpinBox->value();
  m_Controls->ObsButton->setEnabled(false);
  m_Controls->EpiButton->setEnabled(true);
  m_Controls->Pulse.GetLogBox().append("Applying anaphylaxis");
  m_Controls->Pulse.ScrollLogBox();
}

void QAnaphylaxisShowcaseWidget::InjectEpinephrine()
{
  m_Controls->InjectEpinephrine = true;
  m_Controls->EpiButton->setEnabled(false);
  m_Controls->Pulse.GetLogBox().append("Injecting a bolus of epinephrine");
  m_Controls->Pulse.ScrollLogBox();
}

void QAnaphylaxisShowcaseWidget::PhysiologyUpdateUI()
{
  if(m_Controls->ReduceAirwayObstruction)
    m_Controls->SeveritySpinBox->setValue(m_Controls->Severity);
  if (m_Controls->Finished && m_Controls->SeveritySpinBox->value() != 0.0)
  {
    m_Controls->Finished = false;
    m_Controls->SeveritySpinBox->setValue(0.0);
  }
}

