using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;

namespace TwitchInGames {
    public class SingleThread : IDisposable {
        private readonly Thread thread;
        private readonly ManualResetEvent signal = new ManualResetEvent(false);
        private readonly Queue<Action> queue = new Queue<Action>();
        private volatile State state = State.Running;

        public SingleThread() {
            thread = new Thread(Loop) { IsBackground = true };
            thread.Start();
        }

        public void EnqueueTask(Action task) {
            if(task == null) {
                throw new ArgumentNullException(nameof(task));
            }
            lock(queue) {
                queue.Enqueue(task);
                signal.Set();
            }
        }

        private void Loop() {
            while(state != State.Stopped) {
                signal.WaitOne();
                Action task;
                while(DequeueTask(out task)) {
                    try {
                        task();
                    } catch(Exception ex) {
                        Debug.LogException(ex);
                    }
                }
            }
        }

        private bool DequeueTask(out Action task) {
            lock(queue) {
                if(queue.Any()) {
                    task = queue.Dequeue();
                    return true;
                } else {
                    signal.Reset();
                    task = null;
                    return false;
                }
            }
        }

        protected virtual void Dispose(bool disposing) {
            if(state == State.Running) {
                state = State.Stopping;
                if(disposing) {
                    lock(queue) {
                        queue.Clear();
                        queue.Enqueue(() => state = State.Stopped);
                        signal.Set();
                    }
                    thread.Join();
                    ((IDisposable)signal).Dispose();
                }
            }
        }

        public void Dispose() {
            Dispose(true);
        }

        private enum State { Running, Stopping, Stopped };
    }
}
