// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Net;
using System.Threading;

namespace FASTER.core
{
    /// <summary>
    /// Fast implementation of instance-thread-local variables
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class FastThreadLocal<T>
    {
        // Max instances supported
        private const int kMaxInstances = 128;

        [ThreadStatic]
        private static T[] tl_values;
        [ThreadStatic]
        private static int[] tl_iid;

        private readonly int offset;
        private readonly int iid;

        private static readonly int[] instances = new int[kMaxInstances];
        private static int instanceId = 0;

        public FastThreadLocal()
        {
            iid = Interlocked.Increment(ref instanceId);

            for (int i = 0; i < kMaxInstances; i++)
            {
                if (0 == Interlocked.CompareExchange(ref instances[i], iid, 0))
                {
                    offset = i;
                    return;
                }
            }
            throw new Exception("Unsupported number of simultaneous instances");
        }

        public void InitializeThread()
        {
            if (tl_values == null)
            {
                tl_values = new T[kMaxInstances];
                tl_iid = new int[kMaxInstances];
            }
            if (tl_iid[offset] != iid)
            {
                tl_iid[offset] = iid;
                tl_values[offset] = default(T);
            }
        }

        public void DisposeThread()
        {
            tl_values[offset] = default(T);
            tl_iid[offset] = 0;
        }

        /// <summary>
        /// Dispose instance for all threads
        /// </summary>
        public void Dispose()
        {
            instances[offset] = 0;
        }

        public T Value
        {
            get => tl_values[offset];
            set => tl_values[offset] = value;
        }

        public bool IsInitializedForThread => (tl_values != null) && (iid == tl_iid[offset]);
    }
}