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

#include <QObject>
#include <QTextEdit>
#include <QDoubleSpinBox>
#include <QComboBox>
#include "cdm/CommonDataModel.h"
#include "cdm/engine/SEAdvanceHandler.h"
class PhysiologyEngine;
class SEEngineTracker;
class SEDataRequestManager;
class SEPatientConfiguration;
class PulseScenario;
class PulseConfiguration;

enum class InputMode { None = 0, Showcase, Scenario, State, Patient };

class PulseListener
{
public:
  // This is called at the end of stabilizing a patient via the editor
  virtual void AtSteadyState(PhysiologyEngine& pulse) = 0; // Don't modify UI in here!!
  // This is where we take data that we pulled from pulse at steady state and do anything to our UI based on it
  virtual  void AtSteadyStateUpdateUI() = 0; //This is called only if you are stabilizing a new patient
  // This is where we pull data from pulse, and push any actions to it
  virtual void ProcessPhysiology(PhysiologyEngine& pulse) = 0; // Don't modify UI in here!!
  // This is where we take data that we pulled from pulse and do anything to our UI based on it
  virtual  void PhysiologyUpdateUI() = 0; // This is called every time step (after stabilization || state load)
};

class QPulse : public QObject, public SEAdvanceHandler
{
  Q_OBJECT
  friend class Worker;
public:
  QPulse(QThread& thread, QTextEdit& log);
  virtual ~QPulse();

public:
  PhysiologyEngine& GetEngine();
  SEEngineTracker& GetEngineTracker();

  void ScrollLogBox();
  QTextEdit& GetLogBox();
  void IgnoreAction(const std::string& name);

  void Reset();
  void Start();
  void Stop();
  bool IsRunning();
  bool ToggleRealtime();//return true=yes
  bool PlayPause();//return true=paused
  void Save();//return true=success
  void RegisterListener(PulseListener* listener);
  void RemoveListener(PulseListener* listener);
  
  double GetTimeStep_s();
  void OnAdvance(double time_s, PhysiologyEngine const& engine);

  //////////////////////////////////////
  // Various ways to setup the engine //
  //////////////////////////////////////
  void EngineToPatientConfiguration();

  // Stabilize to the patient configuration, and optional engine, configuration
  void StabilizeEngine(bool active);

  PulseScenario& GetScenario();
  // If you want to stabilize to a new patient (required for stabilization)
  SEPatientConfiguration& GetPatientConfiguration();
  // If you want to modify one or more advanced Pulse internals (optional for stabilization)
  PulseConfiguration& GetPulseConfiguration();

protected:
  void AdvanceTime();

signals:
  void AtSteadyState();
  void Advanced();
protected slots :
  void AtSteadyStateUpdateUI();
  void PhysiologyUpdateUI();

private:
  class Controls;
  Controls* m_Controls;
};

class Worker : public QObject
{
  Q_OBJECT
public:
  Worker(QPulse& qpulse) : _qpulse(qpulse) {};
  virtual ~Worker() {}

public slots:
  void Work();

protected:
  QPulse& _qpulse;
};