using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace WinTrace.App
{
    internal static class Extensions
    {
        public static V GetOrNull<K, V>(this Dictionary<K, V> dict, K k) where V : class
        {
            V v;
            if (dict.TryGetValue(k, out v))
            {
                return v;
            }
            else
            {
                return null;
            }
        }

        public static bool TryRemove<K, V>(this Dictionary<K, V> dict, K k)
        {
            if (dict.ContainsKey(k))
            {
                dict.Remove(k);
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    internal class DebuggerContext
    {
        private Dictionary<uint, Thread> threads = new Dictionary<uint, Thread>();
        private uint singleThreadMode = 0;

        public IntPtr Process { get; set; }

        public uint MainThread { get; set; }

        public Thread Thread(uint id)
        {
            return threads.GetOrNull(id);
        }

        public void AddThread(IntPtr handle, uint id, IntPtr startAddress)
        {
            var thread = new Thread(handle, id, startAddress);

            // TODO: According to MSDN supplied handle could be NULL, so we
            //       need to OpenThread() in this case.
            Debug.Assert(thread.Handle != IntPtr.Zero);

            threads.Add(thread.Id, thread);

            if (singleThreadMode != 0)
            {
                // Suspend the new thread.
                thread.Suspend();
            }
        }

        public bool RemoveThread(uint id)
        {
            return threads.TryRemove(id);
        }

        public void EnterSingleThreadMode(uint id)
        {
            if (Thread(id) == null)
            {
                throw new ArgumentException();
            }

            singleThreadMode = id;

            foreach (var thread in threads.Values.Where(t => t.Id != id))
            {
                thread.Suspend();
            }
        }

        public void LeaveSingleThreadMode()
        {
            var id = singleThreadMode;

            if (id == 0)
            {
                throw new InvalidOperationException();
            }

            singleThreadMode = 0;

            foreach (var thread in threads.Values.Where(t => t.Id != id))
            {
                thread.Resume();
            }
        }
    }
}
