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

  Copyright (c) Kitware, Inc.
  All rights reserved.

     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 <vector>
#include <atomic>
#include "vtkNew.h"
#include "vtkSmartPointer.h"
#include "vtkVector.h"

#include <queue> // for ivar
#include <array>

class mvActor;
class mvCameraPose;
class mvDataObject;
class mvFlagpole;
class mvLobby;
class mvPhotoSphere;
class mvTexture;
class mvTourStop;

class vtkActor;
class vtkDataSet;
class vtkImageData;
class vtkLineSource;
class vtkMultiThreader;
class vtkOpenGLMovieSphere;
class vtkOpenVRCameraPose;
class vtkOpenVRPanelWidget;
class vtkOpenVRPanelRepresentation;
class vtkPlaneSource;
class vtkRenderer;
class vtkSkybox;
class vtkSphereSource;
class vtkTextureObject;
class vtkXMLDataElement;

class mvView
{
public:
  mvView();
  ~mvView();

  void Initialize(std::string fname, mvLobby *lobby);
  void SetPosition(double x, double y, double x2, double y2, double height);

  void LoadMainThread(); // gets called before launching the background thread
  void LoadMinimumData(void *threadinfo);
  void GoToTourStop(int poseNum, bool broadcast = false, bool fromCollab = false,
    double *trans = nullptr, double *dir = nullptr);
  void GoToNextTourStop();
  void GoToFirstTourStop();
  int GetCurrentTourStop() {
    return this->CurrentTourStop;
  }

  void ReturnFromView();

  void LobbyOff();
  void LobbyOn();

  vtkActor *GetPosterActor();

  double GetLongitude() {
    return this->Longitude; }

  double GetLatitude() {
    return this->Latitude; }

  bool EventCallback(
    unsigned long eventID,
    void* calldata);

  // write out level of detail files for large data files
  // these get loaded first to improve user interactivity
  void GenerateLODs();

  // used by the worker thread
  void LoadRemainingData(void *data);

  std::string GetRootDirectory() {
    return this->RootDirectory;
  }

  // get a name for this View
  std::string GetName() {
    return this->Name;
  }

  bool GetActive() {
    return this->Active;
  }
  void SetActive(bool act);

  void FinishLoadView();

protected:
  mvView(const mvView&) = delete;
  void operator=(const mvView&) = delete;

  std::atomic<int> LoadProgress;

  void LoadAudioClips();
  void LoadIndexData(vtkXMLDataElement *topel);
  void LoadIndexDataVersion2(vtkXMLDataElement *topel);
  void LoadExtraData(vtkXMLDataElement *topel);

  void ComputeLoadPriority();

  void GoToPhotoSphere(mvPhotoSphere &sphere);
  void ExitPhotoSphere();
  void ReturnFromPhotoSphere();
  bool InPhotoSphere;

  void ProcessRenderOperations();
  class RenderOperation
  {
  public:
    int SmallestLOD;
    vtkSmartPointer<vtkImageData> ImageData;
    std::vector<int> Textures;
    int Level;
    int CurrentRow = 0;
  };
  std::queue<RenderOperation> RenderOperations;

  void HandleAnimation();
  bool Animating;
  double AnimationStartTime;
  double AnimationDuration;
  double AnimationLastTick;
  double AnimationSum;

  double ButtonPressTime;
  vtkEventDataDevice ButtonPressDevice;

  void UpdatePropVisibilityAndOpacity(mvTourStop &ts, float blendAmount);

  const int MaxLODLevel = 10;

  std::string GetTextureName(const std::string &root, int level);

  double Latitude;
  double Longitude;
  int UseImageBasedLighting;
  int EnvironmentCubeMap;
  std::string ViewFile;
  std::string RootDirectory;
  std::string Name;
  bool Active;

  int CurrentTourStop;
  int InitialTourStop;
  bool InLocalPhotoSphere;

  // the main elements a View holds
  std::vector<mvCameraPose>  CameraPoses;
  std::vector<mvTourStop>    TourStops;
  std::vector<mvPhotoSphere> PhotoSpheres;
  std::vector<mvFlagpole>    Flagpoles;
  std::map<size_t, mvTexture>    Textures;
  std::map<size_t, mvActor>      Actors;
  std::map<size_t, mvDataObject> DataObjects;

  vtkNew<vtkActor> LineActor;
  vtkNew<vtkLineSource> Line;
  vtkNew<vtkActor> PosterActor;
  vtkNew<vtkPlaneSource> Plane;
  vtkNew<vtkActor> BackgroundActor;
  vtkNew<vtkPlaneSource> BackgroundPlane;

  vtkNew<vtkMultiThreader> Threader;
  int LODLoadThreadId;

  vtkXMLDataElement *PosesXML;

  vtkNew<vtkOpenVRPanelWidget> MessageWidget;
  vtkNew<vtkOpenVRPanelRepresentation> MessageRepresentation;

  vtkNew<vtkSkybox> Skybox;
  vtkNew<vtkOpenGLMovieSphere> MovieSphere;
  vtkNew<vtkSphereSource> SphereSource;


  mvPhotoSphere *CurrentPhotoSphere;
  vtkOpenVRCameraPose *SavedPose;
  vtkOpenVRCameraPose *NextPose;
  void SetPose(vtkOpenVRCameraPose *thePose,
    vtkOpenGLRenderer *ren,
    vtkOpenGLRenderWindow *renWin);
  void ApplyPose(vtkOpenVRCameraPose *thePose,
    vtkOpenGLRenderer *ren,
    vtkOpenGLRenderWindow *renWin);

  mvLobby *MyLobby;
};
