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

#include "CollapsableWidget.h"

#include "actions/ActionWidget.h"
#include "actions/AcuteRespiratoryDistressSyndromeExacerbationWidget.h"
#include "actions/AcuteStressWidget.h"
#include "actions/AirwayObstructionWidget.h"
#include "actions/ArrhythmiaWidget.h"
#include "actions/AsthmaAttackWidget.h"
#include "actions/BrainInjuryWidget.h"
#include "actions/BronchoconstrictionWidget.h"
#include "actions/ChestCompressionForceScaleWidget.h"
#include "actions/ChestOcclusiveDressingWidget.h"
#include "actions/ChronicObstructivePulmonaryDiseaseExacerbationWidget.h"
//#include "actions/ConsciousRespirationWidget.h"
#include "actions/DyspneaWidget.h"
#include "actions/ExerciseWidget.h"
#include "actions/HemorrhageWidget.h"
#include "actions/ImpairedAlveolarExchangeExacerbationWidget.h"
#include "actions/IntubationWidget.h"
#include "actions/LobarPneumoniaExacerbationWidget.h"
#include "actions/MechanicalVentilatorContinuousPositiveAirwayPressureWidget.h"
#include "actions/MechanicalVentilatorHoldWidget.h"
#include "actions/MechanicalVentilatorLeakWidget.h"
#include "actions/MechanicalVentilatorVolumeControlWidget.h"
#include "actions/MechanicalVentilatorPressureControlWidget.h"
#include "actions/NeedleDecompressionWidget.h"
#include "actions/PericardialEffusionWidget.h"
#include "actions/PulmonaryShuntExacerbationWidget.h"
#include "actions/RespiratoryFatigueWidget.h"
#include "actions/SerializeStateWidget.h"
#include "actions/SubstanceBolusWidget.h"
#include "actions/SubstanceCompoundInfusionWidget.h"
#include "actions/SubstanceInfusionWidget.h"
#include "actions/SupplementalOxygenWidget.h"
#include "actions/TensionPneumothoraxWidget.h"
#include "actions/UrinateWidget.h"

#include "pulse/cdm/CommonDefs.h"
#include "pulse/cdm/engine/SEPatientConfiguration.h"
#include "pulse/cdm/engine/SEActionManager.h"
#include "pulse/cdm/engine/SEAction.h"
// All the actions for checking against later.
#include "pulse/cdm/engine/SESerializeState.h"
#include "pulse/cdm/patient/actions/SEAcuteRespiratoryDistressSyndromeExacerbation.h"
#include "pulse/cdm/patient/actions/SEAcuteStress.h"
#include "pulse/cdm/patient/actions/SEAirwayObstruction.h"
#include "pulse/cdm/patient/actions/SEArrhythmia.h"
#include "pulse/cdm/patient/actions/SEAsthmaAttack.h"
#include "pulse/cdm/patient/actions/SEBrainInjury.h"
#include "pulse/cdm/patient/actions/SEBronchoconstriction.h"
#include "pulse/cdm/patient/actions/SEChestCompressionForceScale.h"
#include "pulse/cdm/patient/actions/SEChestOcclusiveDressing.h"
#include "pulse/cdm/patient/actions/SEChronicObstructivePulmonaryDiseaseExacerbation.h"
#include "pulse/cdm/patient/actions/SEDyspnea.h"
#include "pulse/cdm/patient/actions/SEExercise.h"
#include "pulse/cdm/patient/actions/SEHemorrhage.h"
#include "pulse/cdm/patient/actions/SEImpairedAlveolarExchangeExacerbation.h"
#include "pulse/cdm/patient/actions/SEIntubation.h"
#include "pulse/cdm/patient/actions/SELobarPneumoniaExacerbation.h"
#include "pulse/cdm/patient/actions/SENeedleDecompression.h"
#include "pulse/cdm/patient/actions/SEPericardialEffusion.h"
#include "pulse/cdm/patient/actions/SEPulmonaryShuntExacerbation.h"
#include "pulse/cdm/patient/actions/SERespiratoryFatigue.h"
#include "pulse/cdm/patient/actions/SESubstanceBolus.h"
#include "pulse/cdm/patient/actions/SESubstanceCompoundInfusion.h"
#include "pulse/cdm/patient/actions/SESubstanceInfusion.h"
#include "pulse/cdm/patient/actions/SESupplementalOxygen.h"
#include "pulse/cdm/patient/actions/SETensionPneumothorax.h"
#include "pulse/cdm/patient/actions/SEUrinate.h"
#include "pulse/cdm/system/equipment/mechanical_ventilator/actions/SEMechanicalVentilatorVolumeControl.h"
#include "pulse/cdm/system/equipment/mechanical_ventilator/actions/SEMechanicalVentilatorLeak.h"



class QActionsEditorWidget::Controls : public Ui::ActionsEditorWidget
{
public:
  Controls(QPulse& qp) : Pulse(qp) {
  }
  QPulse&                Pulse;

  QVector<QActionWidget*> Listeners;
  QVector<QCollapsableWidget*> Collapsables;

  // Work around larger inheritance refactoring
  QVector<std::function<bool(const SEAction*, const SEScalarTime&)>> Openers;

  template <class Widget>
  std::pair<Widget*, QCollapsableWidget*> AddWidget(QString label, int size, QWidget* parent, QPulse& qp)
  {
    std::pair<Widget*, QCollapsableWidget*> result;
    result.first = new Widget(qp, parent);
    result.second = new QCollapsableWidget(label, size, parent);
    result.second->setVisible(true);
    result.second->layout()->addWidget(result.first);
    result.second->setContentLayout(*result.first->layout());
    ActionWidgets->layout()->addWidget(result.second);
    connect(result.first, SIGNAL(UpdateAction(SEAction const&,SEScalarTime const&)), parent, SIGNAL(UpdateAction(SEAction const&,SEScalarTime const&)));

    Listeners.push_back(result.first);
    Collapsables.push_back(result.second);

    // Create a function object that checks the type of the incoming action and then executes
    // the opening action if the action is what the widget expected
    // Requires Widget::ActionType to be defined otherwise we will get a compilation error
    // captures the action widget and the container by value but they'll go out of scope
    // at the same time as the lambda
    Openers.push_back([widget = result.first, collapseable = result.second](const SEAction* action, const SEScalarTime& t)
    {
      const auto* typedAction = dynamic_cast<const typename Widget::ActionType*>(action);
      if (typedAction != nullptr)
      {
        widget->SetEnabled(true);
        widget->ActionToControls(*typedAction);
        widget->SetProcessTime(t);
        collapseable->expand(true);
        return true;
      }
      return false;
    });

    return result;
  }
};

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

  // Add in the order that should be used in the action editor (alphabetical atm)

  m_Controls->AddWidget<QAcuteRespiratoryDistressSyndromeExacerbationWidget>("ARDS Exacerbation", 120, this, qp);
  m_Controls->AddWidget<QAcuteStressWidget>("Acute Stress", 120, this, qp);
  m_Controls->AddWidget<QAirwayObstructionWidget>("Airway Obstruction", 120, this, qp);
  m_Controls->AddWidget<QArrhythmiaWidget>("Arrhythmia", 120, this, qp);
  m_Controls->AddWidget<QAsthmaAttackWidget>("Asthma Attack", 120, this, qp);
  m_Controls->AddWidget<QBrainInjuryWidget>("Brain Injury", 120, this, qp);
  m_Controls->AddWidget<QBronchoconstrictionWidget>("Bronchoconstriction", 120, this, qp);
  m_Controls->AddWidget<QChestCompressionForceScaleWidget>("Chest Compression Force Scale", 120, this, qp);
  m_Controls->AddWidget<QChestOcclusiveDressingWidget>("Chest Occlusive Dressing", 120, this, qp);
  m_Controls->AddWidget<QChronicObstructivePulmonaryDiseaseExacerbationWidget>("COPD Exacerbation", 120, this, qp);
  m_Controls->AddWidget<QDyspneaWidget>("Dyspnea", 120, this, qp);
  m_Controls->AddWidget<QExerciseWidget>("Exercise", 120, this, qp);
  m_Controls->AddWidget<QHemorrhageWidget>("Hemorrhage", 120, this, qp);
  m_Controls->AddWidget<QImpairedAlveolarExchangeExacerbationWidget>("Impaired Alveolar Exchange Exacerbation", 120, this, qp);
  m_Controls->AddWidget<QIntubationWidget>("Intubation", 120, this, qp);
  m_Controls->AddWidget<QLobarPneumoniaExacerbationWidget>("Lobar Pneumonia Exacerbation", 120, this, qp);
  m_Controls->AddWidget<QNeedleDecompressionWidget>("Needle Decompression", 120, this, qp);
  m_Controls->AddWidget<QPericardialEffusionWidget>("Pericardial Effusion", 120, this, qp);
  m_Controls->AddWidget<QPulmonaryShuntExacerbationWidget>("Pulmonary Shunt Exacerbation", 120, this, qp);
  m_Controls->AddWidget<QRespiratoryFatigueWidget>("Respiratory Fatigue", 120, this, qp);
  m_Controls->AddWidget<QSerializeStateWidget>("Serialization", 120, this, qp);
  m_Controls->AddWidget<QSubstanceBolusWidget>("Substance Bolus", 120, this, qp);
  m_Controls->AddWidget<QSubstanceCompoundInfusionWidget>("Substance Compound Infusion", 120, this, qp);
  m_Controls->AddWidget<QSubstanceInfusionWidget>("Substance Infusion", 120, this, qp);
  m_Controls->AddWidget<QSupplementalOxygenWidget>("Supplemental Oxygen", 120, this, qp);
  m_Controls->AddWidget<QTensionPneumothoraxWidget>("Tension Pneumothorax", 120, this, qp);
  m_Controls->AddWidget<QUrinateWidget>("Urinate", 120, this, qp);
  m_Controls->AddWidget<QMechanicalVentilatorContinuousPositiveAirwayPressureWidget>("Ventilator CPAP", 120, this, qp);
  m_Controls->AddWidget<QMechanicalVentilatorHoldWidget>("Ventilator Hold", 120, this, qp);
  m_Controls->AddWidget<QMechanicalVentilatorPressureControlWidget>("Ventilator Pressure Control", 120, this, qp);
  m_Controls->AddWidget<QMechanicalVentilatorVolumeControlWidget>("Ventilator Volume Control", 120, this, qp);
  m_Controls->AddWidget<QMechanicalVentilatorLeakWidget>("Ventilator Leak", 120, this, qp);

  // Not supported at the moment
  // m_Controls->AddWidget<QConsciousRespirationWidget>("Conscious Respiration, 120, this);

  QSpacerItem *spacer = new QSpacerItem(40, 1000, QSizePolicy::Expanding, QSizePolicy::Expanding);
  m_Controls->ActionWidgets->layout()->addItem(spacer);
}

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

void QActionsEditorWidget::Clear()
{
  for (auto widget : m_Controls->Listeners)
  {
    widget->Reset();
  }

  for (auto widget : m_Controls->Collapsables)
  {
    widget->expand(false);
  }

  EnableInput(true);
  EnableEditControls(true);
}

void QActionsEditorWidget::EnableInput(bool b)
{
  for (auto widget : m_Controls->Listeners)
  {
    widget->SetEnabled(b);
  }

}

void QActionsEditorWidget::EnableEditControls(bool b)
{
  for (auto widget : m_Controls->Listeners)
  {
    widget->ShowProcessTimeCtrl(b);
  }
}

void QActionsEditorWidget::OpenAction(SEAction const& action, SEScalarTime const& pTime)
{
  Clear();
  for (const auto& openAction : m_Controls->Openers)
  {
    if (openAction(&action, pTime)) break;
  }
}

void QActionsEditorWidget::AtSteadyState(PhysiologyEngine& pulse)
{
  for (auto widget : m_Controls->Listeners)
  {
    widget->AtSteadyState(pulse);
  }

}
void QActionsEditorWidget::AtSteadyStateUpdateUI()
{
  EnableInput(m_Controls->Pulse.GetMode() == ExplorerMode::Editor);

  for (auto widget : m_Controls->Listeners)
  {
    widget->AtSteadyStateUpdateUI();
  }
}

void QActionsEditorWidget::ProcessPhysiology(PhysiologyEngine& pulse)
{
  for (auto widget : m_Controls->Listeners)
  {
    widget->ProcessPhysiology(pulse);
  }
}
void QActionsEditorWidget::PhysiologyUpdateUI(const std::vector<SEAction const*>& actions)
{
  for (auto widget : m_Controls->Listeners)
  {
    widget->PhysiologyUpdateUI(actions);
  }
}
