/* 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 "CommonDataModel.h"
#include "engine/SEAction.h"
#include "engine/SEAdvanceHandler.h"
class PhysiologyEngine;
class SEEngineTracker;
class SEDataRequestManager;
class SEPatientConfiguration;
class PulseScenario;
class PulseConfiguration;

enum class ExplorerMode { None = 0, Showcase, Editor };

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)
  // Handle a Pulse Error that should stop execution
  virtual void EngineErrorUI() = 0;
};

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

  PhysiologyEngine& GetEngine();

  ExplorerMode GetMode();
  void SetMode(ExplorerMode m);

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

  void Clear();
  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) override;

  std::string GetEngineStateFilename();
  void SetEngineStateFilename(std::string const& statefileName);

  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
  PulseConfiguration& GetPulseConfiguration();

  void AddScenarioAction(ushort time_s, SEAction const&);

protected:
  void AdvanceTime();

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

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;
};