using System;
using System.Collections;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using Beefy.utils;

namespace IDE
{
	class ThreadWorkerContext
	{
		public Thread mThread;
		public bool mThreadRunning;
		public Action mOnThreadDone ~ delete _;
		[ThreadStatic]
		public static bool mBpSetThreadName;
		public bool mAllowFastFinish;
		WaitEvent mWaitEvent = new WaitEvent() ~ delete _;

		public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0, bool allowFastFinish = true)
		{
		    Debug.Assert(Thread.CurrentThread == IDEApp.sApp.mMainThread);

		    Debug.Assert(mOnThreadDone == null);
		    //mBfSystem.PerfZoneStart("BfCompiler.ThreadStart");

			BeefPerf.Event("DoBackground:starting", "");

			mAllowFastFinish = allowFastFinish;
		    mOnThreadDone = onThreadDone;
		    mThreadRunning = true;
			mWaitEvent.Reset();

		    ThreadPool.QueueUserWorkItem(new () =>
		    {
				if (!mBpSetThreadName)
				{
					String name = scope .();
					Thread.CurrentThread.GetName(name);
					BeefPerf.SetThreadName(name);
					mBpSetThreadName = true;
				}

				BeefPerf.Event("DoBackground:threadStart", "");
		        mThreadRunning = true;
		        mThread = Thread.CurrentThread;
		        threadStart();
				delete threadStart;
		        mThread = null;
		        mThreadRunning = false;
				mAllowFastFinish = true;
				BeefPerf.Event("DoBackground:threadEnd", "");

				mWaitEvent.Set();
		    });

			if (maxWait != 0)
			{
				if (mWaitEvent.WaitFor(maxWait))
					CheckThreadDone();
			}
		    //mBfSystem.PerfZoneEnd();
		}

		public void WaitForBackground()
		{
			if (mThreadRunning)
			{
				scope AutoBeefPerf("CancelBackground");

			    //mBfSystem.PerfZoneStart("BfCompiler.CancelBackground");
			    while (mThreadRunning)
			    {
			        // Lame
			        Thread.Sleep(20);
			    }
			    //mBfSystem.PerfZoneEnd();                                
			}

			if ((mOnThreadDone != null) && (Thread.CurrentThread == IDEApp.sApp.mMainThread))
			{
			    mOnThreadDone();
			    delete mOnThreadDone;
				mOnThreadDone = null;
			}
		}

		public void CheckThreadDone()
		{
			if ((!mThreadRunning) && (mOnThreadDone != null))
			{
			    mOnThreadDone();
				delete mOnThreadDone;
			    mOnThreadDone = null;
			}
		}
	}

    public abstract class CommandQueueManager
    {        
        protected class Command
        {

        }

		public ThreadWorkerContext mThreadWorker = new .() ~ delete _;
		public ThreadWorkerContext mThreadWorkerHi = new .() ~ delete _;

        protected List<Command> mCommandQueue = new List<Command>() ~ DeleteContainerAndItems!(_);
        protected bool mShuttingDown;
        public bool mAllowThreadStart = true;
		public Monitor mMonitor = new Monitor() ~ delete _;

		public bool ThreadRunning
		{
			get
			{
				return mThreadWorker.mThreadRunning || mThreadWorkerHi.mThreadRunning;
			}
		}

        public ~this()
        {
            using (mMonitor.Enter())
            {
                mShuttingDown = true;
				ClearAndDeleteItems(mCommandQueue);
            }
            CancelBackground();
        }

        public virtual void RequestCancelBackground()
        {

        }

		public virtual void RequestFastFinish(bool force = false)
		{

		}

        public void CancelBackground()
        {
            RequestCancelBackground();
            WaitForBackground();
        }

		public void WaitForBackground()
		{
			mThreadWorker.WaitForBackground();
			mThreadWorkerHi.WaitForBackground();
		}

        protected void QueueCommand(Command command)
        {
            using (mMonitor.Enter())
            {
                mCommandQueue.Add(command);
            }
        }

        public bool HasQueuedCommands()
        {
            using (mMonitor.Enter())
            {
                return (mThreadWorker.mThreadRunning) || (mThreadWorkerHi.mThreadRunning) || (mCommandQueue.Count > 0);
            }
        }

        public int32 GetCommandQueueSize()
        {
            using (mMonitor.Enter())
            {
                return (int32)mCommandQueue.Count;
            }
        }

        protected abstract void DoProcessQueue();

        public virtual void ProcessQueue()
        {
            DoProcessQueue();
        }

        public virtual void StartQueueProcessThread()
        {
            DoBackground(new => DoProcessQueue);
        }

        public bool IsPerformingBackgroundOperation()
        {
            return (mThreadWorker.mThreadRunning || mThreadWorkerHi.mThreadRunning);
        }

		public bool IsPerformingBackgroundOperationHi()
		{
		    return (mThreadWorkerHi.mThreadRunning);
		}

        public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0)
        {
			CancelBackground();
            mThreadWorker.DoBackground(threadStart, onThreadDone, maxWait);
        }

		public void DoBackgroundHi(ThreadStart threadStart, Action onThreadDone = null, bool allowFastFinish = true)
		{
		 	mThreadWorkerHi.DoBackground(threadStart, onThreadDone, 0, allowFastFinish);
		}

		public void CheckThreadDone()
		{
			mThreadWorker.CheckThreadDone();
			mThreadWorkerHi.CheckThreadDone();
		}

        public virtual void Update()
        {
            if (!mThreadWorker.mThreadRunning && !mThreadWorkerHi.mThreadRunning)
            {
             	CheckThreadDone();

                if (mAllowThreadStart)
                {                    
                    if (mCommandQueue.Count > 0)
                    {
                        StartQueueProcessThread();
                    }
                }
            }
        }
    }
}
