/// <summary>
        /// Queues a method for execution, and specifies an object containing data to be used by the method. The method executes when a thread pool thread becomes available.
        /// </summary>
        /// <param name="callBack">A <see cref="WaitCallback"/> representing the method to execute.</param>
        /// <param name="state">An object containing data to be used by the method.</param>
        /// <returns><see langword="true"/> if the method is successfully queued; <see cref="NotSupportedException"/> is thrown if the work item could not be queued.</returns>
        public static bool QueueUserWorkItem(WaitCallback callBack, object state)
        {
            ThreadWorker worker = GetOrCreateFreeWorker();

            if (worker != null)
            {
                worker.Post(callBack, state);
                return(true);
            }
            //queue a work item that is not bound to a specific thread context
            return(pendingWorks.Enqueue(new WorkItem(callBack, state)));
        }
        /// <summary>
        ///  Queues a method specified by an System.Action`1 delegate for execution, and provides data to be used by the method. The method executes when a thread pool thread becomes available.
        /// </summary>
        /// <typeparam name="TState">The type of elements of state.</typeparam>
        /// <param name="callBack">An Action representing the method to execute.</param>
        /// <param name="state">An object containing data to be used by the method.</param>
        /// <param name="preferLocal"><see langword="true"/> to prefer queueing the work item in a queue close to the current thread; <see langword="false"/> to prefer queueing the work item to the thread pool's shared queue.</param>
        /// <returns><see langword="true"/> if the method is successfully queued; <see cref="NotSupportedException"/> is thrown if the work item could not be queued. </returns>
        public static bool QueueUserWorkItem <TState>(Action <TState> callBack, TState state, bool preferLocal)
        {
            if (preferLocal)
            {
                throw new Exception("PreferLocal:true not supported");
            }

            ThreadWorker worker = GetOrCreateFreeWorker();

            if (worker != null)
            {
                worker.Post((_) => callBack(state), null);

                return(true);
            }

            return(pendingWorks.Enqueue(new WorkItem((_) => callBack(state), state)));
        }
 static internal void RunPendingWorkItems(ThreadWorker callingWorker)
 {
     lock (mlock)
     {
         //first find the first workitem that was requested to be run on this calling worker and start it
         for (int i = 0; i < pendingWorks.Count; i++)
         {
             //TODO: remove this workitem from queue, figured it will be removed below
             WorkItem work = pendingWorks[i];
             //if the work must be run on this thread
             if (work.workerId == callingWorker.Id)
             {
                 //post the job back to the callingWorker
                 callingWorker.Post(work.callBack, work.state);
             }
         }
         //now run all other pending works on any available pool
         while (pendingWorks.Count > 0)
         {
             //get an available pool
             ThreadWorker pool = GetOrCreateFreeWorker();
             if (pool != null)
             {
                 do
                 {
                     WorkItem work = default;
                     if (pendingWorks.Dequeue(ref work))
                     {
                         //if the work can be run on any thread
                         if (work.workerId < 0)
                         {
                             pool.Post(work.callBack, work.state);
                         }
                         else
                         {
                             continue;
                         }
                     }
                 } while (false);
             }
         }
     }
 }
        internal static bool QueueUserWorkItemOnSpecificWorker(int threadId, WaitCallback callBack, object state)
        {
            ThreadWorker worker = GetWorkerById(threadId);

            if (worker == null)
            {
                throw new Exception($"No such worker with id {threadId}");
            }

            if (worker.IsFree)
            {
                worker.Post(callBack, state);
                return(true);
            }
            else
            {
                //queue a work item that is bound to a specific thread context(threadId)
                return(pendingWorks.Enqueue(new WorkItem((_) => callBack(state), state, threadId)));
            }
        }
        public async Task <ushort> SendData(byte[] data)
        {
            try
            {
                await locker.WaitAsync();

                await Task.Delay(1000);


                ushort _messageId   = (ushort)storage.GetShort(device.Id, "message-id", 0);
                var    ___messageId = _messageId + 1;
                storage.PutShort(device.Id, "message-id", (short)___messageId);

                i++;

                if (i % 8 == 0)
                {
                    throw new Exception("Dummy send error");
                }


                var m = Message.Unpack(data, new InMemoryBuffer());

                if (m is BalanceMO balance)
                {
                    thread.PostDelayed(() =>
                    {
                        var balanceMT = BalanceMT.Create(ProtocolVersion.v3__WeatherExtension, DateTime.UtcNow, DateTime.UtcNow.AddDays(-12), DateTime.UtcNow.AddDays(30), 672, 1000, 328).Pack();

                        PacketReceived(this, new PacketReceivedEventArgs()
                        {
                            Payload   = balanceMT[0].Payload,
                            MessageId = (short)(10006 + ___messageId),
                        });
                    }, TimeSpan.FromSeconds(15));
                }
                if (m is MessageSentMO sent)
                {
                    thread.PostDelayed(() =>
                    {
                        var resendIndexes = new byte[resendParts];
                        for (int k = 0; k < resendParts; k++)
                        {
                            resendIndexes[k] = (byte)k;
                        }

                        resendParts -= 2;

                        if (resendParts < 0)
                        {
                            resendParts = 2;
                        }

                        var ack = MessageAckMT.Create(ProtocolVersion.v3__WeatherExtension, (byte)sent.SentGroup, resendIndexes).Pack();

                        PacketReceived(this, new PacketReceivedEventArgs()
                        {
                            Payload   = ack[0].Payload,
                            MessageId = (short)(10005 + ___messageId),
                        });
                    }, TimeSpan.FromSeconds(10));
                }


                thread.Post(async() =>
                {
                    await Task.Delay(TimeSpan.FromSeconds(4));


                    PacketStatusUpdated(this, new PacketStatusUpdatedEventArgs()
                    {
                        MessageId = (short)_messageId,
                        Status    = MessageStatus.Transmitted,
                    });


                    //await Task.Delay(TimeSpan.FromSeconds(6));


                    //var m = Message.Unpack(data) as ChatMessageMO;

                    //if (m != null && m.TotalParts == 1)
                    //{
                    //var p = ChatMessageMT.Create(m.Version, m.Subscriber, m.Id, m.Conversation, m.Text, m.Lat, m.Lon, m.Alt, m.ByskyToken, m.File, m.FileExtension, m.ImageQuality, m.Subject).Pack();

                    //PacketReceived(this, new PacketReceivedEventArgs()
                    //{
                    //    Payload = p[0].Payload,
                    //    MessageId = (short)(10000 + ___messageId),
                    //});
                    //}
                });

                return(_messageId);
            }
            finally
            {
                locker.Release();
            }
        }