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

  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 <string>
#include <vector>
#include <map>
#include <vtkNew.h>
#include <vtkSmartPointer.h>
#include "vtkAtomicTypes.h" // for ivars

class mvAudioHandler;
class mvLobbyClient;
class mvView;

class vtkActor;
class vtkCallbackCommand;
class vtkEventDataDevice3D;
enum class vtkEventDataDevice;
class vtkMultiThreader;
class vtkMutexLock;
class vtkObject;
class vtkRenderer;

class vtkOpenVRCamera;
class vtkOpenVROverlay;
class vtkOpenVRInteractorStyle;
class vtkOpenVRRenderer;
class vtkOpenVRRenderWindow;
class vtkOpenVRRenderWindowInteractor;

class mvLobby
{
public:
  mvLobby(int argc, char *argv[]);
  ~mvLobby();

  // start the interactor
  void Start();

  // Loads the heavyweight data for a view
  // and makes it active
  void LoadView(size_t viewNum, bool fromCollab = false);

  // Views call to indicate a new pose
  void NewPose(int poseNum);

  // views call to return
  void ReturnFromView(bool fromCollab = false);

  // lock/unlock the rendering thread mutex
  void RenderLock();
  void RenderUnlock();

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

  vtkRenderer *GetRenderer();

  vtkOpenVROverlay *GetDashboardOverlay();

  bool GetDisplayMap() { return this->DisplayMap;  }

  // Return the interactor style
  vtkOpenVRInteractorStyle *GetStyle() { return this->Style; }

  // generate level of detail files for quicker loading.
  // Optionally for a specific view
  void GenerateLODs(std::string const &viewName);

  // get the audio handler instance
  mvAudioHandler *GetAudioHandler() {
    return this->AudioHandler;
  }

  // get the audio handler instance
  mvLobbyClient *GetCollaborationClient() {
    return this->Client;
  }

  // Valid states for the application along with their transitions
  //
  // Unknown -> Lobby via Initialize()
  // Unknown -> GeneratingLOD via constructor
  // Lobby -> Unknown (exiting)
  // Lobby -> LoadingView via LoadView()
  // LoadingView -> Lobby via CancelLoadView()
  // LoadingView -> AsynchronousLoadComplete
  // CompletedAsynchronousLoad -> InView via FinishLoadView()
  // InView -> Lobby via ReturnFromView()
  enum States {
    Unknown,
    InLobby,
    LoadingView,
    CompletedAsynchronousLoad,
    InView,
    GeneratingLOD
  };

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

  // called from mvLobbyClient
  friend mvLobbyClient;
  void ServerViewChanged(std::string const &viewName);
  void ServerTourStopChanged(int viewIndex,
    double translation[3], double direction[3]);

  // called once the View->Load has finished
  void FinishLoadView();

  // called to cancel a view that is in the process
  // of being loaded
  void CancelLoadView();

  int CurrentView;

  // see the enum of state up above
  vtkAtomicInt32 State;

  // returns false on failure, true on success
  bool Initialize(int argc, char *argv[]);

  void LoadMap();

  // loads the lightweight data for the views
  void InitializeViews();

  int GetViewNumberFromEvent(vtkEventDataDevice3D *edd);

  mvAudioHandler *AudioHandler;

  mvLobbyClient *Client;

  bool FindViewsDirectory(char *argv0);
  std::string ViewsDirectory;
  std::vector<mvView *> Views;
  vtkNew<vtkActor> MapActor;

  double PosterCenter[2];
  double PosterRadius;
  double PosterHeight;
  double ArcStart; // in degrees
  double ArcContentSize; // in degrees
  double ArcGap; // in degreees

  vtkCallbackCommand* EventCommand;
  static void EventCallback(
    vtkObject* object, unsigned long event, void* clientdata, void* calldata);

  vtkNew<vtkMutexLock> RenderMutex;
  vtkNew<vtkMultiThreader> Threader;
  int AsynchronousLoadViewThreadId;

  bool DisplayMap;
  std::vector<int> BackgroundColor;
  std::string LobbyAudio;

  vtkNew<vtkOpenVRCamera> Camera;
  vtkNew<vtkOpenVRRenderer> Renderer;
  vtkNew<vtkOpenVRRenderWindow> RenderWindow;
  vtkNew<vtkOpenVRRenderWindowInteractor> Interactor;
  vtkNew<vtkOpenVRInteractorStyle> Style;

  // volume level for audio output, defaults to -1.0 indicating
  // to not change the current volume
  double Volume;
};
