using ChocolArm64;
using System;

namespace Ryujinx.Core.OsHle.Handles
{
    class KThread : KSynchronizationObject
    {
        public AThread Thread { get; private set; }

        public KThread MutexOwner { get; set; }

        public KThread NextMutexThread   { get; set; }
        public KThread NextCondVarThread { get; set; }

        public long MutexAddress   { get; set; }
        public long CondVarAddress { get; set; }

        public int ActualPriority { get; private set; }
        public int WantedPriority { get; private set; }

        public int ProcessorId  { get; private set; }

        public int WaitHandle { get; set; }

        public int ThreadId => Thread.ThreadId;

        public KThread(AThread Thread, int ProcessorId, int Priority)
        {
            this.Thread      = Thread;
            this.ProcessorId = ProcessorId;

            ActualPriority = WantedPriority = Priority;
        }

        public void SetPriority(int Priority)
        {
            WantedPriority = Priority;

            UpdatePriority();
        }

        public void UpdatePriority()
        {
            int OldPriority = ActualPriority;

            int CurrPriority = WantedPriority;

            if (NextMutexThread != null && CurrPriority > NextMutexThread.WantedPriority)
            {
                CurrPriority = NextMutexThread.WantedPriority;
            }

            if (CurrPriority != OldPriority)
            {
                ActualPriority = CurrPriority;

                UpdateWaitList();

                MutexOwner?.UpdatePriority();
            }
        }

        private void UpdateWaitList()
        {
            KThread OwnerThread = MutexOwner;

            if (OwnerThread != null)
            {
                //The MutexOwner field should only be non null when the thread is
                //waiting for the lock, and the lock belongs to another thread.
                if (OwnerThread == this)
                {
                    throw new InvalidOperationException();
                }

                lock (OwnerThread)
                {
                    //Remove itself from the list.
                    KThread CurrThread = OwnerThread;

                    while (CurrThread.NextMutexThread != null)
                    {
                        if (CurrThread.NextMutexThread == this)
                        {
                            CurrThread.NextMutexThread = NextMutexThread;

                            break;
                        }

                        CurrThread = CurrThread.NextMutexThread;
                    }

                    //Re-add taking new priority into account.
                    CurrThread = OwnerThread;

                    while (CurrThread.NextMutexThread != null)
                    {
                        if (CurrThread.NextMutexThread.ActualPriority < ActualPriority)
                        {
                            break;
                        }

                        CurrThread = CurrThread.NextMutexThread;
                    }

                    NextMutexThread = CurrThread.NextMutexThread;

                    CurrThread.NextMutexThread = this;
                }
            }
        }
    }
}