text: /*
   * Copyright (c) Contributors to the Open 3D Engine Project.
   * For complete copyright and license terms please see the LICENSE at the root of this distribution.
   *
   * SPDX-License-Identifier: Apache-2.0 OR MIT
   *
   */
  
  #pragma once
  
  #include <AzNetworking/Serialization/ISerializer.h>
  #include <AzNetworking/Utilities/NetworkCommon.h>
  #include <AzCore/std/containers/stack.h>
  #include <AzCore/std/containers/array.h>
  #include <AzCore/std/containers/fixed_unordered_map.h>
  #include <AzCore/std/containers/map.h>
  #include <AzCore/std/string/string.h>
  #include <AzCore/Math/Vector2.h>
  #include <AzCore/Math/Vector3.h>
  #include <AzCore/Math/Vector4.h>
  #include <AzCore/Math/Quaternion.h>
  #include <AzCore/Math/Transform.h>
  #include <AzCore/Math/Frustum.h>
  #include <AzCore/Math/Aabb.h>
  #include <limits>
  
  namespace AzNetworking
  {
      // Generic AZ Containers
      template <typename TYPE>
      struct SerializeAzContainer
      {
          static bool Serialize(ISerializer& serializer, TYPE& container)
          {
              using ValueType = typename TYPE::value_type;
              constexpr uint32_t max = std::numeric_limits<uint32_t>::max(); // Limit to uint32 max elements
              const bool write = (serializer.GetSerializerMode() == SerializerMode::WriteToObject);
  
              uint32_t size = static_cast<uint32_t>(container.size());
              bool success = serializer.Serialize(size, "Size");
  
              // Dynamic containers require different read/write serialization interfaces
              if (write)
              {
                  container.clear();
                  AzNetworking::AzContainerHelper::ReserveContainer<TYPE>(container, size);
                  ValueType element;
                  for (uint32_t i = 0; i < size; ++i)
                  {
                      success &= serializer.Serialize(element, GenerateIndexLabel<max>(i).c_str());
                      container.insert(container.end(), element);
                  }
              }
              else
              {
                  uint32_t i = 0;
                  for (auto it = container.begin(); it != container.end(); ++it, ++i)
                  {
                      success &= serializer.Serialize(*it, GenerateIndexLabel<max>(i).c_str());
                  }
              }
              return success;
          }
      };
  
      // fixed size array
      template <typename TYPE, AZStd::size_t Size>
      struct SerializeAzContainer<AZStd::array<TYPE, Size>>
      {
          static bool Serialize(ISerializer& serializer, AZStd::array<TYPE, Size>& container)
          {
              constexpr uint32_t max = static_cast<uint32_t>(Size);
              static_assert(Size <= max, "Array size must be less than max.\n");
              bool success = true;
  
              int i = 0;
              for (auto &elem : container)
              {
                  success &= serializer.Serialize(elem, GenerateIndexLabel<max>(i++).c_str());
              }
              return success;
          }
      };
  
      // fixed_unordered_map
      template <typename Key, typename MappedType, AZStd::size_t FixedNumBuckets, AZStd::size_t FixedNumElements, class Hasher, class EqualKey>
      struct SerializeAzContainer<AZStd::fixed_unordered_map<Key, MappedType, FixedNumBuckets, FixedNumElements, Hasher, EqualKey>>
      {
          using TYPE = AZStd::fixed_unordered_map<Key, MappedType, FixedNumBuckets, FixedNumElements, Hasher, EqualKey>;
          static bool Serialize(ISerializer& serializer, TYPE& container)
          {
              using SizeType = typename TYPE::size_type;
              using ValueType = typename TYPE::value_type;
              static_assert(FixedNumElements >= FixedNumBuckets, "fixed_unordered_map buckets is less than elements.");
              constexpr uint32_t max = static_cast<uint32_t>(FixedNumElements); // Elements is > Buckets
              const bool write = (serializer.GetSerializerMode() == SerializerMode::WriteToObject);
  
              SizeType size = container.size();
              bool success = serializer.Serialize(size, "Size");
  
              if (write)
              {
                  container.clear();
                  ValueType element;
                  for (uint32_t i = 0; i < size; ++i)
                  {
                      success &= serializer.Serialize(element, GenerateIndexLabel<max>(i).c_str());
                      container.insert(element);
  
                  }
              }
              else
              {
                  uint32_t i = 0;
                  for (auto it = container.begin(); it != container.end(); ++it, ++i)
                  {
                      success &= serializer.Serialize(*it, GenerateIndexLabel<max>(i).c_str());
                  }
              }
              return success;
          }
      };
  
      // fixed_unordered_multimap
      template <typename Key, typename MappedType, AZStd::size_t FixedNumBuckets, AZStd::size_t FixedNumElements, class Hasher, class EqualKey>
      struct SerializeAzContainer<AZStd::fixed_unordered_multimap<Key, MappedType, FixedNumBuckets, FixedNumElements, Hasher, EqualKey>>
      {
          using TYPE = AZStd::fixed_unordered_multimap<Key, MappedType, FixedNumBuckets, FixedNumElements, Hasher, EqualKey>;
          static bool Serialize(ISerializer& serializer, TYPE& container)
          {
              using SizeType = typename TYPE::size_type;
              using ValueType = typename TYPE::value_type;
              static_assert(FixedNumElements >= FixedNumBuckets, "fixed_unordered_multimap buckets is less than elements.");
              constexpr uint32_t max = static_cast<uint32_t>(FixedNumElements); // Elements is > Buckets
              const bool write = (serializer.GetSerializerMode() == SerializerMode::WriteToObject);
  
              SizeType size = container.size();
              bool success = serializer.Serialize(size, "Size");
  
              if (write)
              {
                  container.clear();
                  ValueType element;
                  for (uint32_t i = 0; i < size; ++i)
                  {
                      success &= serializer.Serialize(element, GenerateIndexLabel<max>(i).c_str());
                      container.insert(element);
  
                  }
              }
              else
              {
                  uint32_t i = 0;
                  for (auto it = container.begin(); it != container.end(); ++it, ++i)
                  {
                      success &= serializer.Serialize(*it, GenerateIndexLabel<max>(i).c_str());
                  }
              }
              return success;
          }
      };
  
      // multimap
      template <class Key, class MappedType, class Compare, class Allocator>
      struct SerializeAzContainer<AZStd::multimap<Key, MappedType, Compare, Allocator>>
      {
          using TYPE = AZStd::multimap<Key, MappedType, Compare, Allocator>;
          static bool Serialize(ISerializer& serializer, TYPE& container)
          {
              using SizeType = typename TYPE::size_type;
              using ValueType = typename TYPE::value_type;
              constexpr uint32_t max = std::numeric_limits<uint32_t>::max(); // Limit to uint32 max elements
              const bool write = (serializer.GetSerializerMode() == SerializerMode::WriteToObject);
  
              SizeType size = static_cast<uint32_t>(container.size());
              bool success = serializer.Serialize(size, "Size");
  
              if (write)
              {
                  container.clear();
                  ValueType element;
                  for (uint32_t i = 0; i < size; ++i)
                  {
                      success &= serializer.Serialize(element, GenerateIndexLabel<max>(i).c_str());
                      container.insert(element);
                  }
              }
              else
              {
                  uint32_t i = 0;
                  for (auto it = container.begin(); it != container.end(); ++it, ++i)
                  {
                      success &= serializer.Serialize(*it, GenerateIndexLabel<max>(i).c_str());
                  }
              }
              return success;
          }
      };
  
      // String
      template<>
      struct SerializeAzContainer<AZStd::string>
      {
          static bool Serialize(ISerializer& serializer, AZStd::string& value)
          {
              uint32_t size = aznumeric_cast<uint32_t>(value.length());
              uint32_t outBytes = size;
  
              bool success = serializer.Serialize(size, "Size");
              value.resize_no_construct(size);
              success &= serializer.SerializeBytes(reinterpret_cast<uint8_t*>(value.data()), size, true, outBytes, "String");
  
              return success && outBytes == size;
          }
      };
  
      // fixed_string
      template <AZStd::size_t MaxElementCount>
      struct SerializeAzContainer<AZStd::fixed_string<MaxElementCount>>
      {
          static bool Serialize(ISerializer& serializer, AZStd::fixed_string<MaxElementCount>& value)
          {
              using SizeType = typename AZ::SizeType<AZ::RequiredBytesForValue<MaxElementCount>(), false>::Type;
              SizeType size = aznumeric_cast<SizeType>(value.length());
              uint32_t outBytes = static_cast<uint32_t>(size);
  
              bool success = serializer.Serialize(size, "Size");
              value.resize_no_construct(size);
              success &= serializer.SerializeBytes(reinterpret_cast<uint8_t*>(value.data()), static_cast<uint32_t>(size), true, outBytes, "String");
  
              return success && outBytes == size;
          }
      };
  
      // Az Containers
      template <typename TYPE>
      struct SerializeObjectHelper<TYPE, AZStd::enable_if_t<AzContainerHelper::IsIterableContainer<TYPE>::Value>>
      {
          static bool SerializeObject(ISerializer& serializer, TYPE& container)
          {
              return SerializeAzContainer<TYPE>::Serialize(serializer, container);
          }
      };
  
      // Az Types
      template <>
      struct SerializeObjectHelper<AZ::Vector2>
      {
          static bool SerializeObject(ISerializer& serializer, AZ::Vector2& value)
          {
              float values[4];
              value.StoreToFloat2(values);
              serializer.Serialize(values[0], "xValue");
              serializer.Serialize(values[1], "yValue");
              value = AZ::Vector2::CreateFromFloat2(values);
              return serializer.IsValid();
          }
      };
  
      template <>
      struct SerializeObjectHelper<AZ::Vector3>
      {
          static bool SerializeObject(ISerializer& serializer, AZ::Vector3& value)
          {
              float values[4];
              value.StoreToFloat3(values);
              serializer.Serialize(values[0], "xValue");
              serializer.Serialize(values[1], "yValue");
              serializer.Serialize(values[2], "zValue");
              value = AZ::Vector3::CreateFromFloat3(values);
              return serializer.IsValid();
          }
      };
  
      template <>
      struct SerializeObjectHelper<AZ::Vector4>
      {
          static bool SerializeObject(ISerializer& serializer, AZ::Vector4& value)
          {
              float values[4];
              value.StoreToFloat4(values);
              serializer.Serialize(values[0], "xValue");
              serializer.Serialize(values[1], "yValue");
              serializer.Serialize(values[2], "zValue");
              serializer.Serialize(values[3], "wValue");
              value = AZ::Vector4::CreateFromFloat4(values);
              return serializer.IsValid();
          }
      };
  
      template <>
      struct SerializeObjectHelper<AZ::Quaternion>
      {
          static bool SerializeObject(ISerializer& serializer, AZ::Quaternion& value)
          {
              float values[4];
              value.StoreToFloat4(values);
              serializer.Serialize(values[0], "xValue");
              serializer.Serialize(values[1], "yValue");
              serializer.Serialize(values[2], "zValue");
              serializer.Serialize(values[3], "wValue");
              value = AZ::Quaternion::CreateFromFloat4(values);
              return serializer.IsValid();
          }
      };
  }
  