context: /*==============================================================================
  Copyright (c) 2010-2013 QUALCOMM Austria Research Center GmbH.
  All Rights Reserved.
  Qualcomm Confidential and Proprietary
  ==============================================================================*/
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  using UnityEditor;
  using UnityEngine;
  
  // The scene manager is responsible for synchronizing scene and config.xml file
  public class SceneManager
  {
      #region PROPERTIES
  
      // Returns an instance of a SceneManager (thread safe)
      public static SceneManager Instance
      {
          get
          {
              // Make sure only one instance of SceneManager is created.
              if (mInstance == null)
              {
                  lock (typeof(SceneManager))
                  {
                      if (mInstance == null)
                      {
                          mInstance = new SceneManager();
                      }
                  }
              }
              return mInstance;
          }
      }
  
      // Returns if the scene has already been initialized. This initialization
      // happens only once in a SceneManager lifetime.
      public bool SceneInitialized
      {
          get
          {
              return mSceneInitialized;
          }
      }    
  
      #endregion // PROPERTIES
  
  
  
      #region PRIVATE_MEMBER_VARIABLES
  
      // Delegate that is called by Unity in editor.
      private EditorApplication.CallbackFunction mUpdateCallback = null;
      // These Booleans are used to tell the SceneManager that it is time to write
      // or read values to or from the config.xml file.
      private bool mDoSerialization = false;
      private bool mDoDeserialization = false;
      // This Boolean is used to apply properties (e.g. size) from loaded data
      // sets to the current scene.
      private bool mApplyProperties = false;
      // This Boolean is used to change the appearance (e.g. aspect ratio and
      // texture)of Trackables in the scene with data from the data sets.
      private bool mApplyAppearance = false;
      // This variable is used to check if the scene has been initialized.
      private bool mSceneInitialized = false;
      // Needs scene to be checked for issues (e.g. duplicates)?
      private bool mValidateScene = false;
      // Since it is not possible to open up the QCAR web page from a button press
      // in the Unity editor directly this is handled in the EditorUpdate
      // callback.
      private bool mGoToARPage = false;
      // The path used for exporting the data set.
      private string mDataSetExportPath = "";
  
      // Singleton: Still uses lazy initialization:
      // Private static variables initialized on first reference to class.
      private static SceneManager mInstance;
      private static TimeSpan UPDATE_INTERVAL = TimeSpan.FromSeconds(60);
      private DateTime mLastUpdate = DateTime.Now;
  
      #endregion // PRIVATE_MEMBER_VARIABLES
  
  
  
      #region CONSTRUCTION
  
      private SceneManager()
      {
          mUpdateCallback = new EditorApplication.CallbackFunction(EditorUpdate);
          if (EditorApplication.update == null)
          {
              EditorApplication.update += mUpdateCallback;
          }
          else if (!EditorApplication.update.Equals(mUpdateCallback))
          {
              EditorApplication.update += mUpdateCallback;
          }
  
          // We force a config.xml read operation before the SceneManager is used
          // to avoid inconsistencies on Unity startup.
          mDoDeserialization = true;
  
          // Make sure that the scene is initialized whenever a new instance of
          // the SceneManager is created.
          mSceneInitialized = false;
      }
  
      #endregion // CONSTRUCTION
  
  
  
      #region PUBLIC_METHODS
  
      // Initializes scene with content of the config.xml file (ensures that 
      // changes to the file that happened while Unity was not running are
      // applied).
      public void InitScene()
      {
          DataSetTrackableBehaviour[] trackables =
              (DataSetTrackableBehaviour[])UnityEngine.Object.FindObjectsOfType(
                  typeof(DataSetTrackableBehaviour));
  
          // Read all avaiable data set files.
          ConfigDataManager.Instance.DoRead();
  
          // We apply the newly read values to the scene.
          UpdateTrackableAppearance(trackables);
  
          mValidateScene = true;
  
          mSceneInitialized = true;
  
          SetUnityVersion();
      }
  
      
      // Update method triggered by Unity.
      // Used to check scene content changes.
      // Be aware that this update callback only is called once a SceneManager
      // instance has been created.
      // Be aware that the instance is destroyed if the SceneManager file is
      // recompiled.
      public void EditorUpdate()
      {
          TrackableBehaviour[] trackables =
              (TrackableBehaviour[])UnityEngine.Object.FindObjectsOfType(
                  typeof(TrackableBehaviour));
          VirtualButtonBehaviour[] virtualButtons =
              (VirtualButtonBehaviour[])UnityEngine.Object.FindObjectsOfType(
                  typeof(VirtualButtonBehaviour));
  
          // Correct scales of Trackables.
          CorrectTrackableScales(trackables);
  
          // Correct Virtual Button poses.
          VirtualButtonEditor.CorrectPoses(virtualButtons);
  
          // We do deserialization and serialization in this order to avoid
          // overwriting the config.xml file.
          if (mDoDeserialization)
          {
              ConfigDataManager.Instance.DoRead();
  
              DataSetLoadEditor.OnConfigDataChanged();
  
              ApplyDataSetAppearance();
  
              mDoDeserialization = false;
          }
  
          if (mApplyAppearance)
          {
              // We apply the newly read values to the scene.
              UpdateTrackableAppearance(trackables);
  
              mApplyAppearance = false;
          }
  
          if (mApplyProperties)
          {
              // We apply the newly read values to the scene.
              UpdateTrackableProperties(trackables);
  
              mApplyProperties = false;
          }
  
          if (mDoSerialization)
          {
              ConfigData sceneData = CreateDataSetFromTrackables(trackables);
              if (!ConfigParser.Instance.structToFile(mDataSetExportPath, sceneData))
              {
                  Debug.LogError("Export of scene file failed.");
              }
  
              mDoSerialization = false;
          }
  
          if (mValidateScene)
          {
              // Check if there are duplicate trackables in the scene.
              CheckForDuplicates(trackables);
  
              // Validate all Virtual Buttons in the scene.
              VirtualButtonEditor.Validate();
  
              mValidateScene = false;
          }
  
          if (mGoToARPage)
          {
              mGoToARPage = false;
              System.Diagnostics.Process.Start(
                  "https://developer.vuforia.com/target-manager");
          }
  
          if (mLastUpdate.Add(UPDATE_INTERVAL) < DateTime.Now)
          {
              SetUnityVersion();
              mLastUpdate = DateTime.Now;
          }
      }
  
      private static void SetUnityVersion()
      {
          // Set the Unity version for internal use
          string path = Path.Combine(Application.dataPath, "StreamingAssets/QCAR");
          QCARUnityImpl.SetUnityVersion(path);
      }
      
      public string[] GetImageTargetNames(string dataSetName)
      {
          ConfigData dataSetData = ConfigDataManager.Instance.GetConfigData(dataSetName);
          string[] itNames = new string[dataSetData.NumImageTargets + 1];
          itNames[0] = QCARUtilities.GlobalVars.DEFAULT_TRACKABLE_NAME;
          dataSetData.CopyImageTargetNames(itNames, 1);
          return itNames;
      }
  
  
      public string[] GetMultiTargetNames(string dataSetName)
      {
          ConfigData dataSetData = ConfigDataManager.Instance.GetConfigData(dataSetName);
          string[] itNames = new string[dataSetData.NumMultiTargets + 1];
          itNames[0] = QCARUtilities.GlobalVars.DEFAULT_TRACKABLE_NAME;
          dataSetData.CopyMultiTargetNames(itNames, 1);
          return itNames;
      }
  
  
      public int GetNextFrameMarkerID()
      {
          IEditorMarkerBehaviour[] markers =
              (MarkerBehaviour[])UnityEngine.Object.FindObjectsOfType(
                  typeof(MarkerBehaviour));
  
          if (markers.Length <= 0)
          {
              return 0;
          }
          if (markers.Length >= QCARUtilities.GlobalVars.MAX_NUM_FRAME_MARKERS)
          {
              Debug.LogWarning("Too many frame markers in scene.");
              return (QCARUtilities.GlobalVars.MAX_NUM_FRAME_MARKERS - 1);
          }
  
          int freeMarkerID = 0;
          bool idIsFree = false;
          while (!idIsFree)
          {
              idIsFree = true;
              for (int i = 0; i < markers.Length; ++i)
              {
                  // If marker is in scene it is not free.
                  if (markers[i].MarkerID == freeMarkerID)
                  {
                      idIsFree = false;
                      ++freeMarkerID;
                      break;
                  }
              }
          }
          return freeMarkerID;
      }
  
  
      // Export scene to dataSet.xml file.
      public void ExportScene(string path)
      {
          mDataSetExportPath = path;
  
          mDoSerialization = true;
      }
  
  
      // Tell scene manager that scene has been updated.
      public void SceneUpdated()
      {
          mValidateScene = true;
      }
  
  
      // Method to be called by data postprocessor on import of new QCAR
      // related files.
      public void FilesUpdated()
      {
          mDoDeserialization = true;
      }
  
  
      // Change the properties (e.g. size)of Trackables in the scene with
      // data from the data sets.
      public void ApplyDataSetProperties()
      {
          mApplyProperties = true;
      }
  
  
      // Change the appearance (e.g. aspect ratio and texture)of Trackables in 
      // the scene with data from the data sets.
      public void ApplyDataSetAppearance()
      {
          mApplyAppearance = true;
      }
  
  
      // This is function enables an asynchronous call to open the QCAR help page.
      public void GoToARPage()
      {
          mGoToARPage = true;
      }
  
      #endregion // PUBLIC_METHODS
  
  
  
      #region PRIVATE_METHODS
  
      // Updates trackables in scene from config data.
      private void UpdateTrackableAppearance(TrackableBehaviour[] trackables)
      {
          // do not set appearance in play mode
          if (!Application.isPlaying)
          {
              foreach (TrackableBehaviour tb in trackables)
              {
                  // Ignore non-data set trackables.
                  if (!(tb is DataSetTrackableBehaviour))
                  {
                      continue;
                  }
  
                  DataSetTrackableBehaviour trackable = (DataSetTrackableBehaviour) tb;
                  TrackableAccessor configApplier = AccessorFactory.Create(trackable);
                  if (configApplier != null)
                      configApplier.ApplyDataSetAppearance();
              }
          }
      }
  
  
      // Updates trackables in scene from config data.
      private void UpdateTrackableProperties(TrackableBehaviour[] trackables)
      {
          foreach (TrackableBehaviour tb in trackables)
          {
              // Ignore non-data set trackables.
  
gt:             if (!(tb is DataSetTrackableBehaviour))