text: /*
  * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  * its licensors.
  *
  * For complete copyright and license terms please see the LICENSE at the root of this
  * distribution (the "License"). All use of this software is governed by the License,
  * or, if provided, by the license below or the license accompanying this file. Do not
  * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *
  */
  #pragma once
  
  #include <AzCore/Math/Uuid.h>
  #include <AzCore/Component/Component.h>
  #include <AzCore/Component/ComponentApplicationBus.h>
  #include <AzCore/Asset/AssetCommon.h>
  #include <AzCore/Slice/SliceComponent.h>
  
  #include <AzFramework/Entity/EntityContext.h>
  #include <AzFramework/Asset/AssetCatalogBus.h>
  
  #include <AzToolsFramework/Entity/EditorEntityContextPickingBus.h>
  
  #include <Cry_Vector2.h>
  
  #include <LyShine/UiEntityContext.h>
  #include "UiEditorEntityContextBus.h"
  
  namespace AZ
  {
      class SerializeContext;
  }
  
  namespace AzFramework
  {
      class EntityContext;
  }
  
  class EditorWindow;
  
  ////////////////////////////////////////////////////////////////////////////////////////////////////
  //! The UIEditorEntityContext extends the UiEditorContext to add functionality only needed when
  //! a UI canvas is loaded in the UI Editor.
  class UiEditorEntityContext
      : public UiEntityContext
      , private UiEditorEntityContextRequestBus::Handler
      , private AzToolsFramework::EditorEntityContextPickingRequestBus::Handler
      , private AzFramework::AssetCatalogEventBus::Handler
      , private AzFramework::SliceInstantiationResultBus::MultiHandler
  {
  public: // member functions
  
      UiEditorEntityContext(EditorWindow* editorWindow);
      ~UiEditorEntityContext() override;
  
      // EntityContext
      bool HandleLoadedRootSliceEntity(AZ::Entity* rootEntity, bool remapIds, AZ::SliceComponent::EntityIdToEntityIdMap* idRemapTable = nullptr) override;
      // ~EntityContext
  
      // UiEntityContext
      void InitUiContext() override;
      void DestroyUiContext() override;
      bool SaveToStreamForGame(AZ::IO::GenericStream& stream, AZ::DataStream::StreamType streamType) override;
      bool SaveCanvasEntityToStreamForGame(AZ::Entity* canvasEntity, AZ::IO::GenericStream& stream, AZ::DataStream::StreamType streamType) override;
      // ~UiEntityContext
  
      // UiEntityContextRequestBus
      AZ::SliceComponent* GetUiRootSlice() override { return GetRootSlice(); }
      AZ::Entity* CreateUiEntity(const char* name) override;
      void AddUiEntity(AZ::Entity* entity) override;
      void AddUiEntities(const AzFramework::EntityContext::EntityList& entities) override;
      bool CloneUiEntities(const AZStd::vector<AZ::EntityId>& sourceEntities, AzFramework::EntityContext::EntityList& resultEntities) override;
      bool DestroyUiEntity(AZ::EntityId entityId) override;
      // ~UiEntityContextRequestBus
  
      // EditorEntityContextPickingRequestBus
      bool SupportsViewportEntityIdPicking() override;
      // ~EditorEntityContextPickingRequestBus
  
      // UiEditorEntityContextRequestBus
      AZ::SliceComponent::SliceInstanceAddress CloneEditorSliceInstance(AZ::SliceComponent::SliceInstanceAddress sourceInstance) override;
      AzFramework::SliceInstantiationTicket InstantiateEditorSlice(const AZ::Data::Asset<AZ::Data::AssetData>& sliceAsset, AZ::Vector2 viewportPosition) override;
      void RestoreSliceEntity(AZ::Entity* entity, const AZ::SliceComponent::EntityRestoreInfo& info) override;
      void QueueSliceReplacement(const char* targetPath, 
                                  const AZStd::unordered_map<AZ::EntityId, AZ::EntityId>& selectedToAssetMap,
                                  const AZStd::unordered_set<AZ::EntityId>& entitiesInSelection,
                                  AZ::Entity* commonParent, AZ::Entity* insertBefore) override;
      void DeleteElements(AzToolsFramework::EntityIdList elements) override;
      bool HasPendingRequests() override;
      bool IsInstantiatingSlices() override;
      void DetachSliceEntities(const AzToolsFramework::EntityIdList& entities) override;
      // ~UiEditorEntityContextRequestBus
  
      // AzFramework::SliceInstantiationResultBus
      void OnSlicePreInstantiate(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress) override;
      void OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress) override;
      void OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId) override;
      // ~AzFramework::SliceInstantiationResultBus
          
      // AssetCatalogEventBus::Handler
      void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
      // ~AssetCatalogEventBus::Handler
  
      // EntityContextRequestBus
      void ResetContext() override;
      // ~EntityContextRequestBus
  
      AZStd::string GetErrorMessage() const { return m_errorMessage; }
  
  protected: // member functions
  
      void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
      void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
      void OnContextEntitiesAdded(const AzFramework::EntityContext::EntityList& entities) override;
  
      // Used to validate that the entities in an instantiated slice are valid entities for this context
      bool ValidateEntitiesAreValidForContext(const EntityList& entities) override;
  
      void SetupUiEntity(AZ::Entity* entity);
      void InitializeEntities(const AzFramework::EntityContext::EntityList& entities);
  
      using InstantiatingSlicePair = AZStd::pair<AZ::Data::Asset<AZ::Data::AssetData>, AZ::Vector2>;
      AZStd::vector<InstantiatingSlicePair> m_instantiatingSlices;
  
  private: // types
  
      //! Tracks a queued slice replacement, which is a deferred operation.
      //! If the asset has not yet been processed (a new asset), we need
      //! to defer before attempting a load.
      struct QueuedSliceReplacement
      {
          ~QueuedSliceReplacement() = default;
          QueuedSliceReplacement() = default;
  
          QueuedSliceReplacement(const QueuedSliceReplacement&) = delete;
          QueuedSliceReplacement& operator=(const QueuedSliceReplacement&) = delete;
  
          void Setup(const char* path,
                      const AZStd::unordered_map<AZ::EntityId, AZ::EntityId>& selectedToAssetMap,
                      const AZStd::unordered_set<AZ::EntityId>& entitiesInSelection,
                      AZ::Entity* commonParent, AZ::Entity* insertBefore)
          {
              m_path = path;
              m_selectedToAssetMap = selectedToAssetMap;
              m_entitiesInSelection.clear();
              m_entitiesInSelection.insert(entitiesInSelection.begin(), entitiesInSelection.end());
              m_commonParent = commonParent;
              m_insertBefore = insertBefore;
          }
  
          bool IsValid() const;
          void Reset();
  
          void Finalize(const AZ::SliceComponent::SliceInstanceAddress& instanceAddress, EditorWindow* editorWindow);
  
          AZStd::string                                       m_path;
          AZStd::unordered_map<AZ::EntityId, AZ::EntityId>    m_selectedToAssetMap;
          AZStd::unordered_set<AZ::EntityId>                  m_entitiesInSelection;
          AZ::Entity*                                         m_commonParent;
          AZ::Entity*                                         m_insertBefore;
          AzFramework::SliceInstantiationTicket               m_ticket;
      };
  
  private: // member functions
  
      void GetTopLevelEntities(const AZStd::unordered_set<AZ::EntityId>& entities, AZStd::unordered_set<AZ::EntityId>& topLevelEntities);
  
  private: // data
  
      EditorWindow* m_editorWindow;
  
      //! List of selected entities prior to entering game.
      AZStd::vector<AZ::EntityId> m_selectedBeforeStartingGame;
  
      QueuedSliceReplacement m_queuedSliceReplacement;
  
      //! Slice entity restore requests, which can be deferred if asset wasn't loaded at request time.
      struct SliceEntityRestoreRequest
      {
          AZ::Entity* m_entity;
          AZ::SliceComponent::EntityRestoreInfo m_restoreInfo;
          AZ::Data::Asset<AZ::Data::AssetData> m_asset;
      };
  
      AZStd::vector<SliceEntityRestoreRequest> m_queuedSliceEntityRestores;
  
      AZ::ComponentTypeList m_requiredEditorComponentTypes;
  
      AZStd::string m_errorMessage;
  };
  