/*=========================================================================

  Program: ParaView
  Module:  SimpleQtAppMainWindow.cxx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "SimpleQtAppMainWindow.h"
#include "ui_SimpleQtAppMainWindow.h"

#include "pqFileDialog.h"
#include "pqFiltersMenuReaction.h"
#include "pqInterfaceTracker.h"
#include "pqProgressManager.h"
#include "pqProgressWidget.h"
#include "pqProxy.h"
#include "pqProxyGroupMenuManager.h"
#include "pqServer.h"
#include "pqServerManagerModel.h"
#include "pqSettings.h"
#include "pqSourcesMenuReaction.h"
#include "pqStandardPropertyWidgetInterface.h"
#include "vtkCamera.h"
#include "vtkClientSession.h"
#include "vtkLogger.h"
#include "vtkNew.h"
#include "vtkPVDataInformation.h"
#include "vtkPVGUIApplication.h"
#include "vtkSMApplyController.h"
#include "vtkSMInputProperty.h"
#include "vtkSMOutputPort.h"
#include "vtkSMParaViewPipelineControllerWithRendering.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMProxyProperty.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMViewProxy.h"
#include "vtkSmartPointer.h"

#include <qlayoutitem.h>
#include <vtk_rxqt.h>
// clang-format off
#include VTK_REMOTING_RXQT(rxqt.hpp)
// clang-format on

#include <QVBoxLayout>

class SimpleQtAppMainWindow::QInternals
{
public:
  Ui::SimpleQtAppMainWindow Ui;
  QTimer SpinTimer;
  bool StateRestored{ false };
  vtkTypeUInt32 SessionId{ 0 };
  vtkSMViewProxy* setupSession(vtkTypeUInt32 sessionId);
  void openData(const QString& fileName);
  void saveState(const QString& fileName);
};

void SimpleQtAppMainWindow::QInternals::openData(const QString& fileName)
{
  auto* pvapp = vtkPVGUIApplication::GetInstance();
  auto* session = pvapp->GetSession(this->SessionId);
  auto* pxm = session->GetProxyManager();

  vtkNew<vtkSMParaViewPipelineControllerWithRendering> controller;

  vtkLogF(INFO, "openData (%s)", qPrintable(fileName));

  auto reader =
    vtk::TakeSmartPointer(vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("sources", "IOSSReader")));
  vtkSMApplyController::MarkShowOnApply(reader);

  controller->PreInitializeProxy(reader);
  vtkSMPropertyHelper(reader, "FileName").Set(fileName.toStdString().c_str());
  reader->UpdateVTKObjects();
  controller->PostInitializeProxy(reader);
  controller->RegisterPipelineProxy(reader);
}
void SimpleQtAppMainWindow::QInternals::saveState(const QString& fileName)
{
  vtkLogF(INFO, "saving state to %s", qPrintable(fileName));
  auto* pvapp = vtkPVGUIApplication::GetInstance();
  auto* session = pvapp->GetSession(this->SessionId);
  auto* pxm = session->GetProxyManager();
  if (!pxm->SaveXMLState(fileName.toStdString().c_str()))
  {
    vtkLogF(ERROR, "Error saving state");
  }
}

vtkSMViewProxy* SimpleQtAppMainWindow::QInternals::setupSession(vtkTypeUInt32 sessionId)
{
  this->SessionId = sessionId;

  vtkNew<vtkSMParaViewPipelineControllerWithRendering> controller;
  auto* pvapp = vtkPVGUIApplication::GetInstance();
  auto* tracker = pvapp->GetInterfaceTracker();
  tracker->addInterface(new pqStandardPropertyWidgetInterface(tracker));
  auto* session = pvapp->GetSession(this->SessionId);
  auto* pxm = session->GetProxyManager();
  auto* smmodel = pvapp->GetServerManagerModel();
  pqServer* pqserver = smmodel->findServer(this->SessionId);

  // initialize the session.
  controller->InitializeSession(session);

  auto wavelet = vtk::TakeSmartPointer(
    vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("sources", "RTAnalyticSource")));
  controller->PreInitializeProxy(wavelet);
  wavelet->UpdateVTKObjects();
  controller->PostInitializeProxy(wavelet);
  controller->RegisterPipelineProxy(wavelet);

  auto contour =
    vtk::TakeSmartPointer(vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("filters", "Contour")));
  double values[6] = { 37.35310363769531, 90.56993103027344, 143.78675842285156, 197.0035858154297,
    250.2204132080078, 276.8288269042969 };
  controller->PreInitializeProxy(contour);
  vtkSMPropertyHelper(contour, "SelectInputScalars")
    .SetInputArrayToProcess(vtkDataObject::FIELD_ASSOCIATION_POINTS, "RTData");
  vtkSMPropertyHelper(contour, "ContourValues").Set(values, 6);
  vtkSMPropertyHelper(contour, "Input").Set(wavelet, 0);
  contour->UpdateVTKObjects();
  controller->PostInitializeProxy(contour);
  controller->RegisterPipelineProxy(contour);

  auto view =
    vtk::TakeSmartPointer(vtkSMViewProxy::SafeDownCast(pxm->NewProxy("views", "RenderView")));
  controller->InitializeProxy(view);
  controller->RegisterViewProxy(view);
  controller->AssignViewToLayout(view);
  auto waveletRepr = controller->Show(wavelet, 0, view, "GeometryRepresentation");
  vtkSMPropertyHelper(waveletRepr, "Representation").Set(0, "Outline");
  waveletRepr->UpdateVTKObjects();
  controller->Show(contour, 0, view, "GeometryRepresentation");

  // Update the view.
  view->Update().subscribe([view](bool) { view->ResetCameraUsingVisiblePropBounds(); });
  return view;
}

//-----------------------------------------------------------------------------
SimpleQtAppMainWindow::SimpleQtAppMainWindow()
  : Internals(new SimpleQtAppMainWindow::QInternals())
{
  auto& internals = (*this->Internals);
  internals.Ui.setupUi(this);
  internals.SpinTimer.setSingleShot(false);

  // Close view tab crashes, hide tab bar decorations
  internals.Ui.centralwidget->hideDecorations();

#if 0
  // Disable creation of new sources, filters because some might crash.
  auto* sourceMenuMgr = new pqProxyGroupMenuManager(internals.Ui.menu_Sources, "ParaViewSources");
  sourceMenuMgr->loadConfiguration(":/pqApplicationComponents/Menus/Sources.xml");
  sourceMenuMgr->addProxyDefinitionUpdateListener("sources");
  sourceMenuMgr->setRecentlyUsedMenuSize(10);
  new pqSourcesMenuReaction(sourceMenuMgr);

  auto* filtersMenuMgr = new pqProxyGroupMenuManager(internals.Ui.menu_Filters, "ParaViewFilters");
  filtersMenuMgr->loadConfiguration(":/pqApplicationComponents/Menus/Filters.xml");
  filtersMenuMgr->addProxyDefinitionUpdateListener("filters");
  filtersMenuMgr->setRecentlyUsedMenuSize(10);
  new pqFiltersMenuReaction(filtersMenuMgr);
#endif

  auto* progressWidget = new pqProgressWidget();
  progressWidget->setMaximumWidth(250);
  internals.Ui.statusbar->addPermanentWidget(progressWidget, /*stretch*/ 0);

  auto* pvapp = vtkPVGUIApplication::GetInstance();
  auto* pgm = pvapp->GetProgressManager();
  QObject::connect(pgm, SIGNAL(enableProgress(bool)), progressWidget, SLOT(enableProgress(bool)));
  QObject::connect(pgm, SIGNAL(progress(const QString&, int)), progressWidget,
    SLOT(setProgress(const QString&, int)));

  QObject::connect(internals.Ui.actionSpin, &QAction::toggled, [&internals](bool checked) {
    if (checked)
    {
      internals.SpinTimer.start(10);
    }
    else
    {
      internals.SpinTimer.stop();
    }
  });

  // Hookup file-open dialog
  QObject::connect(
    internals.Ui.actionOpenFile, &QAction::triggered, [pvapp, this, &internals](bool /*unused*/) {
      auto* smmodel = pvapp->GetServerManagerModel();
      if (auto* pqserver = smmodel->findServer(internals.SessionId))
      {
        pqFileDialog dialog(pqserver, this);
        if (pqFileDialog::Accepted != dialog.exec())
        {
          vtkLogF(ERROR, "Selected file was not accepted");
        }
        else
        {
          QStringList fileList = dialog.getSelectedFiles();
          if (!fileList.empty())
          {
            internals.openData(fileList.front());
          }
        }
      }
    });

  QObject::connect(
    internals.Ui.actionSaveState, &QAction::triggered, [pvapp, this, &internals](bool /*unused*/) {
      auto* smmodel = pvapp->GetServerManagerModel();
      if (auto* pqserver = smmodel->findServer(internals.SessionId))
      {
        QString file = pqFileDialog::getSaveFileName(
          pqserver, this->Internals->Ui.dockWidget, "Save State as...");
        if (!file.isEmpty())
        {
          internals.saveState(file);
        }
      }
    });
}

//-----------------------------------------------------------------------------
SimpleQtAppMainWindow::~SimpleQtAppMainWindow() = default;

//-----------------------------------------------------------------------------
void SimpleQtAppMainWindow::showEvent(QShowEvent* evt)
{
  auto* pvapp = vtkPVGUIApplication::GetInstance();
  auto& internals = (*this->Internals);
  if (!internals.StateRestored)
  {
    internals.StateRestored = true;
    pvapp->GetSettings()->restoreState("mainWindow", *this);
  }
}

//-----------------------------------------------------------------------------
void SimpleQtAppMainWindow::closeEvent(QCloseEvent* evt)
{
  auto* pvapp = vtkPVGUIApplication::GetInstance();
  pvapp->GetSettings()->saveState(*this, "mainWindow");
  this->QMainWindow::closeEvent(evt);
}

//-----------------------------------------------------------------------------
void SimpleQtAppMainWindow::connect(const QString& url)
{
  auto& internals = (*this->Internals);
  auto* pvapp = vtkPVGUIApplication::GetInstance();
  auto observable =
    url.isEmpty() ? pvapp->CreateBuiltinSession() : pvapp->CreateRemoteSession(qUtf8Printable(url));
  observable.subscribe([&internals](vtkTypeUInt32 id) {
    auto* view = internals.setupSession(id);
    QObject::connect(&internals.SpinTimer, &QTimer::timeout, [view]() {
      view->GetCamera()->Azimuth(1);
      view->StillRender();
    });
  });
}
