/* Distributed under the Apache License, Version 2.0.
See accompanying NOTICE file for details.*/
#include <QApplication>
#include <QDockWidget>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLayout>
#include <QMainWindow>
#include <QWidget>
#include <QPlainTextEdit>
#include <QFile>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include <QScrollBar>
#include <QTimer>
#include <QThread>
#include <QPointer>
#include <QtCharts/QChart>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>
#include <QCloseEvent>
#include <QMessageBox>

#include "cdm/CommonDataModel.h"
#include "cdm/utils/FileUtils.h"

#ifdef PARAVIEW
  #include <pqActiveObjects.h>
  #include <pqAlwaysConnectedBehavior.h>
  #include <pqApplicationCore.h>
  #include <pqObjectBuilder.h>
  #include <pqPersistentMainWindowStateBehavior.h>
  #include <pqContextView.h>
  #include <pqXYChartView.h>
  #include <pqRenderView.h>

  #include <vtkSMProxy.h>
  #include <vtkSMPropertyHelper.h>
  #include <vtkSMReaderFactory.h>

  #include "GeometryView.h"
#endif

#include "MainExplorerWindow.h"
#include "ExplorerIntroWidget.h"
#include "ui_MainExplorerWindow.h"
#include "DataRequestsWidget.h"
#include "VitalsMonitorWidget.h"
#include "DynamicControlsWidget.h"

#include "cdm/CommonDataModel.h"
#include "PulsePhysiologyEngine.h"
#include "cdm/engine/SEDataRequestManager.h"
#include "cdm/engine/SEEngineTracker.h"
#include "cdm/properties/SEScalarTime.h"

class MainExplorerWindow::Controls : public Ui::MainExplorerWindow
{
public:

  virtual ~Controls()
  {
    delete Pulse;
#ifdef PARAVIEW
    delete GeometryView;
    delete ParaViewRenderView;
#endif
    delete ExplorerIntroWidget;
    delete VitalsMonitorWidget;
    delete DataRequestsWidget;
    delete DynamicControls;
  }

#ifdef PARAVIEW  
  QPointer<GeometryView>            GeometryView;
  pqRenderView*                     ParaViewRenderView;
#endif
  QPulse*                           Pulse=nullptr;
  QPointer<QThread>                 Thread;
  QExplorerIntroWidget*             ExplorerIntroWidget=nullptr;
  QVitalsMonitorWidget*             VitalsMonitorWidget=nullptr;
  QDataRequestsWidget*              DataRequestsWidget=nullptr;
  QDynamicControlsWidget*           DynamicControls=nullptr;
  std::stringstream                 Status;  
  double                            CurrentSimTime_s; 
  InputMode                         Mode=InputMode::None;
};

MainExplorerWindow::MainExplorerWindow()
{
  m_Controls = new Controls();
  m_Controls->setupUi(this);
  setWindowIcon(QIcon("resource/pulse.ico"));
#ifdef PARAVIEW 
  new pqAlwaysConnectedBehavior(this);
  new pqPersistentMainWindowStateBehavior(this);
  vtkSMReaderFactory::AddReaderToWhitelist("sources", "XMLPolyDataReader");
  vtkSMReaderFactory::AddReaderToWhitelist("sources", "XMLUnstructuredGridReader");
  pqApplicationCore::instance()->loadConfigurationXML("<xml/>");
#endif
  // This is the widget all input widgets will use
  m_Controls->InputWidget->show();
  m_Controls->InputWidget->raise();
  m_Controls->InputWidget->setVisible(true);
  m_Controls->InputLayout->setAlignment(Qt::AlignHCenter);

  // This is the logger widget
  m_Controls->OutputWidget->show();
  m_Controls->OutputWidget->raise();
  m_Controls->OutputWidget->setVisible(true);
  m_Controls->LogBox->setFontPointSize(10);
  
  m_Controls->Thread = new QThread(parent());
  m_Controls->Pulse = new QPulse(*m_Controls->Thread, *m_Controls->LogBox);
  m_Controls->Pulse->RegisterListener(this);
  m_Controls->Status << "Current Simulation Time : 0s";

  m_Controls->ExplorerIntroWidget = new QExplorerIntroWidget(this);
  m_Controls->ExplorerIntroWidget->setTitleBarWidget(new QWidget());
  m_Controls->ExplorerIntroWidget->setVisible(true);
  m_Controls->InputLayout->addWidget(m_Controls->ExplorerIntroWidget);

  // Add the IDynamic Controls to the main control area
  m_Controls->DynamicControls = new QDynamicControlsWidget(*m_Controls->Pulse, this);
  m_Controls->DynamicControls->setTitleBarWidget(new QWidget());
  m_Controls->DynamicControls->setVisible(false);
  m_Controls->InputLayout->addWidget(m_Controls->DynamicControls);

  int tabIdx = 0; // Order is ParaView, Vitals, DataRequests
  //this->setCentralWidget(m_Controls->TabWidget);
#ifdef PARAVIEW
  // Add ParaView view to the tabWidget
  m_Controls->ParaViewRenderView =
      qobject_cast<pqRenderView*>(pqApplicationCore::instance()->getObjectBuilder()->createView(
                                  pqRenderView::renderViewType(),pqActiveObjects::instance().activeServer()));
  m_Controls->GeometryView = new GeometryView(m_Controls->ParaViewRenderView, this);
  m_Controls->GeometryView->LoadGeometry();
  m_Controls->TabWidget->widget(tabIdx++)->layout()->addWidget(m_Controls->ParaViewRenderView->widget());
#else
  m_Controls->TabWidget->removeTab(0);
#endif  
  m_Controls->VitalsMonitorWidget = new QVitalsMonitorWidget(*m_Controls->LogBox, this);
  m_Controls->TabWidget->widget(tabIdx++)->layout()->addWidget(m_Controls->VitalsMonitorWidget);
  m_Controls->TabWidget->setCurrentIndex(tabIdx - 1);
  m_Controls->DataRequestsWidget = new QDataRequestsWidget(*m_Controls->LogBox, this);
  m_Controls->DataRequestsWidget->setTitleBarWidget(new QWidget());
  m_Controls->TabWidget->widget(tabIdx++)->layout()->addWidget(m_Controls->DataRequestsWidget);

  m_Controls->StartEngine->setVisible(false);
  m_Controls->RunInRealtime->setVisible(false);
  m_Controls->PlayPause->setVisible(false);
  m_Controls->ResetExplorer->setVisible(false);
  m_Controls->ResetEngine->setVisible(false);

  connect(m_Controls->RunInRealtime, SIGNAL(clicked()), this, SLOT(RunInRealtime()));
  connect(m_Controls->PlayPause,     SIGNAL(clicked()), this, SLOT(PlayPause()));
  connect(m_Controls->ResetExplorer, SIGNAL(clicked()), this, SLOT(ResetExplorer()));

  connect(m_Controls->StartEngine,   SIGNAL(clicked()), this, SLOT(StartEngine()));
  connect(m_Controls->ResetEngine,   SIGNAL(clicked()), this, SLOT(ResetEngine()));
}

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


void MainExplorerWindow::closeEvent(QCloseEvent *event)
{
  // Are these questions too annoying?
  /*QMessageBox::StandardButton resBtn = QMessageBox::question(this, "Explorer",
    tr("Are you sure you want to exit?\n"),
    QMessageBox::No | QMessageBox::Yes,
    QMessageBox::Yes);
  if (resBtn != QMessageBox::Yes) {
    event->ignore();
  }
  else {
    event->accept();
  }*/
  m_Controls->Pulse->Stop();
  QMainWindow::closeEvent(event);
}

void MainExplorerWindow::PlayPause()
{
  if (m_Controls->Pulse->PlayPause())
    m_Controls->PlayPause->setText("Play");
  else
    m_Controls->PlayPause->setText("Pause");
}

void MainExplorerWindow::RunInRealtime()
{
  if (m_Controls->Pulse->ToggleRealtime())
    m_Controls->RunInRealtime->setChecked(true);
  else
    m_Controls->RunInRealtime->setChecked(false);
}

void MainExplorerWindow::ResetExplorer()
{
  m_Controls->Pulse->Reset();
  m_Controls->Mode = InputMode::None;
  m_Controls->DataRequestsWidget->Reset();
  m_Controls->VitalsMonitorWidget->Reset();
  m_Controls->RunInRealtime->setChecked(true);
  m_Controls->PlayPause->setText("Pause");
  m_Controls->LogBox->clear();
  m_Controls->Status.str("");
  m_Controls->Status << "Current Simulation Time : 0s";
  m_Controls->StatusBar->showMessage(QString(m_Controls->Status.str().c_str()));
  m_Controls->StartEngine->setVisible(false);
  m_Controls->RunInRealtime->setVisible(false);
  m_Controls->PlayPause->setVisible(false);
  m_Controls->ResetExplorer->setVisible(false);
  m_Controls->ResetEngine->setVisible(false);
  m_Controls->ExplorerIntroWidget->setVisible(true);
  m_Controls->DynamicControls->setVisible(false);
  m_Controls->DynamicControls->Reset();
}

void MainExplorerWindow::ResetEngine()
{
  m_Controls->Pulse->Reset();
#ifdef PARAVIEW
  m_Controls->GeometryView->Reset();
#endif
  m_Controls->DynamicControls->Reset();
  m_Controls->DataRequestsWidget->Reset();
  m_Controls->VitalsMonitorWidget->Reset();
  m_Controls->RunInRealtime->setChecked(true);
  m_Controls->PlayPause->setText("Pause");
  m_Controls->LogBox->clear();

  switch (m_Controls->Mode)
  {
  case InputMode::Showcase:
    StartShowcase();
    break;
  case InputMode::Scenario:
    SetupScenarioEditor();
    break;
  case InputMode::State:
    SetupEngineState();
    break;
  case InputMode::Patient:
    SetupPatientEditor();
    break;
  }
}


void MainExplorerWindow::StartShowcase()
{
  m_Controls->ExplorerIntroWidget->setVisible(false);
  m_Controls->DynamicControls->setVisible(true);
  m_Controls->StartEngine->setVisible(false);
  m_Controls->PlayPause->setVisible(true);
  m_Controls->RunInRealtime->setVisible(true);
  m_Controls->ResetExplorer->setVisible(true);
  m_Controls->ResetEngine->setVisible(true);

  if (!m_Controls->DynamicControls->SetupShowcase(m_Controls->ExplorerIntroWidget->GetSelectedShowcase()))
  {
    QMessageBox msgBox(this);
    msgBox.setWindowTitle("Error!");
    QString err = "Unable to setup the : " + m_Controls->ExplorerIntroWidget->GetSelectedShowcase() + " showcase scenario";
    msgBox.setText(err);
    msgBox.exec();
    return;
  }
  m_Controls->Mode = InputMode::Showcase;
  // Start up the engine right away!
  m_Controls->Pulse->Start();
}

void MainExplorerWindow::SetupScenarioEditor()
{
  m_Controls->Pulse->GetEngineTracker().Clear();
  if (m_Controls->DynamicControls->SetupScenarioEditor())
  {
    m_Controls->ExplorerIntroWidget->setVisible(false);
    m_Controls->DynamicControls->setVisible(true);
    m_Controls->StartEngine->setVisible(true);
    m_Controls->PlayPause->setVisible(false);
    m_Controls->RunInRealtime->setVisible(false);
    m_Controls->ResetExplorer->setVisible(true);
    m_Controls->ResetEngine->setVisible(false);

    m_Controls->Mode = InputMode::Scenario;
  }
  else
  {
    ResetExplorer();
  }
}

void MainExplorerWindow::SetupEngineState()
{
  if (m_Controls->DynamicControls->SetupEngineState())
  {
    m_Controls->ExplorerIntroWidget->setVisible(false);
    m_Controls->DynamicControls->setVisible(true);
    m_Controls->StartEngine->setVisible(true);
    m_Controls->PlayPause->setVisible(false);
    m_Controls->RunInRealtime->setVisible(false);
    m_Controls->ResetExplorer->setVisible(true);
    m_Controls->ResetEngine->setVisible(false);

    m_Controls->Mode = InputMode::State;
  }
  else
  {
    ResetExplorer();
  }
}

void MainExplorerWindow::SetupPatientEditor()
{
  m_Controls->Pulse->GetEngineTracker().Clear();
  m_Controls->ExplorerIntroWidget->setVisible(false);
  m_Controls->DynamicControls->setVisible(true);
  m_Controls->StartEngine->setVisible(true);
  m_Controls->PlayPause->setVisible(false);
  m_Controls->RunInRealtime->setVisible(false);
  m_Controls->ResetExplorer->setVisible(true);
  m_Controls->ResetEngine->setVisible(false);

  m_Controls->DynamicControls->SetupPatientEditor();
  m_Controls->Mode = InputMode::Patient;
}
void MainExplorerWindow::StartEngine()
{
  // Make Sure We have a patient...
  if (!m_Controls->DynamicControls->ValidPatient())
  {
    QMessageBox msgBox(this);
    msgBox.setWindowTitle("Error!");
    QString err = "Please ensure patient has at least a name";
    msgBox.setText(err);
    msgBox.exec();
    return;
  }

  m_Controls->StartEngine->setVisible(false);
  m_Controls->PlayPause->setVisible(true);
  m_Controls->RunInRealtime->setVisible(true);
  m_Controls->RunInRealtime->setChecked(true);

  m_Controls->ResetExplorer->setVisible(true);
  m_Controls->ResetEngine->setVisible(true);

  m_Controls->PlayPause->setEnabled(false);
  m_Controls->ResetEngine->setEnabled(false);
  m_Controls->ResetExplorer->setEnabled(false);

  m_Controls->DynamicControls->PrepStabilization(m_Controls->Mode);
  m_Controls->Pulse->Start();
}


void MainExplorerWindow::AtSteadyState(PhysiologyEngine& pulse)
{// This is called from a thread, you should NOT update UI here
 // This is where we pull data from pulse, and push any actions to it
  m_Controls->DynamicControls->AtSteadyState(pulse);
  m_Controls->VitalsMonitorWidget->AtSteadyState(pulse);
  m_Controls->DataRequestsWidget->AtSteadyState(pulse);
}
void MainExplorerWindow::AtSteadyStateUpdateUI()
{// This is called from a slot, you can update UI here
  m_Controls->DynamicControls->AtSteadyStateUpdateUI();
  m_Controls->VitalsMonitorWidget->AtSteadyStateUpdateUI();
  m_Controls->DataRequestsWidget->AtSteadyStateUpdateUI();

  m_Controls->PlayPause->setEnabled(true);
  m_Controls->ResetEngine->setEnabled(true);
  m_Controls->ResetExplorer->setEnabled(true);
}

void MainExplorerWindow::ProcessPhysiology(PhysiologyEngine& pulse)
{// This is called from a thread, you should NOT update UI here
 // This is where we pull data from pulse, and push any actions to it
  m_Controls->DynamicControls->ProcessPhysiology(pulse);
  m_Controls->VitalsMonitorWidget->ProcessPhysiology(pulse);
  m_Controls->DataRequestsWidget->ProcessPhysiology(pulse);

  m_Controls->CurrentSimTime_s = pulse.GetSimulationTime(TimeUnit::s);
}
void MainExplorerWindow::PhysiologyUpdateUI()
{// This is called from a slot, you can update UI here
  m_Controls->DynamicControls->PhysiologyUpdateUI();
  m_Controls->VitalsMonitorWidget->PhysiologyUpdateUI();
  m_Controls->DataRequestsWidget->PhysiologyUpdateUI();

  m_Controls->Status.str("");
  m_Controls->Status << "Current Simulation Time : " << m_Controls->CurrentSimTime_s << "s";
  m_Controls->StatusBar->showMessage(QString(m_Controls->Status.str().c_str()));
#ifdef PARAVIEW
  m_Controls->ParaViewRenderView->render();
#endif
}





