/******************************************************************************
 * Copyright (C) Ultraleap, Inc. 2011-2024.                                   *
 *                                                                            *
 * Use subject to the terms of the Apache License 2.0 available at            *
 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement           *
 * between Ultraleap and you, your company or other organization.             *
 ******************************************************************************/

using System;
using System.Runtime.InteropServices;

namespace LeapInternal
{

    /**
     * A helper class to marshal between unmanaged memory and structs without creating garbage.
     */
    public static class StructMarshal<T> where T : struct
    {
#if !ENABLE_IL2CPP
        [StructLayout(LayoutKind.Sequential)]
        private class StructContainer
        {
            public T value;
        }

        [ThreadStatic]
        private static StructContainer _container;
#endif

        private static int _sizeofT;

        static StructMarshal()
        {
            _sizeofT = Marshal.SizeOf(typeof(T));
        }

        /**
         * Returns the size in bytes of the struct of type T.  This call is equivalent to
         * Marshal.Sizeof(typeof(T)) but caches the result for ease of access.
         */
        public static int Size
        {
            get
            {
                return _sizeofT;
            }
        }

        /**
         * Converts an IntPtr to a struct of type T.
         */
        public static void PtrToStruct(IntPtr ptr, out T t)
        {
#if ENABLE_IL2CPP
      unsafe {
        Unity.Collections.LowLevel.Unsafe.UnsafeUtility.CopyPtrToStructure((void*)ptr, out t);
      }
#else
            if (_container == null)
            {
                _container = new StructContainer();
            }

            try
            {
                Marshal.PtrToStructure(ptr, _container);
                t = _container.value;
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogException(e);
                t = default(T);
            }
#endif
        }

        /**
         * Converts a single element in an array pointed to by ptr to a struct
         * of type T.  This method does not and cannot do any bounds checking!
         * This method does not create any garbage.
         */
        public static void ArrayElementToStruct(IntPtr ptr, int arrayIndex, out T t)
        {
            PtrToStruct(new IntPtr(ptr.ToInt64() + _sizeofT * arrayIndex), out t);
        }
    }
}