using System;
using System.Runtime.InteropServices;

namespace Wayne.Lib
{
    /// <summary>
    /// System time structure for Win32.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct SystemTime
    {
        [MarshalAs(UnmanagedType.U2)]
        public short Year;
        [MarshalAs(UnmanagedType.U2)]
        public short Month;
        [MarshalAs(UnmanagedType.U2)]
        public short DayOfWeek;
        [MarshalAs(UnmanagedType.U2)]
        public short Day;
        [MarshalAs(UnmanagedType.U2)]
        public short Hour;
        [MarshalAs(UnmanagedType.U2)]
        public short Minute;
        [MarshalAs(UnmanagedType.U2)]
        public short Second;
        [MarshalAs(UnmanagedType.U2)]
        public short Milliseconds;
    }

    /// <summary>
    /// System time proxy, should be used instead of DateTime.Now, and injected to the class,
    /// so the current time can be mocked in unit tests.
    /// 
    /// Added routine to set system time, this seemed as good a place as any to put it. (JHC)
    /// </summary>
    public class WayneSystemTime : ISystemTime
    {
        /// <summary>
        /// The current Date and time.
        /// </summary>
        public DateTime Now
        {
            get { return DateTime.Now; }
            set { SetSystemTime(value); }
        }

        [DllImport("kernel32.dll")]
        static extern bool SetSystemTime(
       ref SystemTime systemTime);

        [DllImport("kernel32.dll")]
        static extern bool SetLocalTime(
        ref SystemTime systemTime);

        ///<summary>
        /// Sets the system time
        ///</summary>
        ///<param name="newTime"></param>
        ///<exception cref="Exception"></exception>
        public void SetSystemTime(DateTime newTime)
        {
            SystemTime st = ConvertToSystemTime(newTime.ToUniversalTime());
            bool rc = SetSystemTime(ref st);
            if (!rc)
            {
                int wrc = Marshal.GetLastWin32Error();
                throw new Exception("Error " + wrc + " setting system time");
            }
        }

        /// <summary>
        /// Set local time
        /// </summary>
        /// <param name="newTime"></param>
        public void SetLocalTime(DateTime newTime)
        {
            //            Microsoft.VisualBasic.DateAndTime.TimeOfDay = DateTime.SpecifyKind(newTime, DateTimeKind.Local);
            SystemTime st = ConvertToSystemTime(newTime);
            bool rc = SetLocalTime(ref st);
            if (!rc)
            {
                int wrc = Marshal.GetLastWin32Error();
                throw new Exception("Error " + wrc + " setting local time");
            }
        }

        internal static SystemTime ConvertToSystemTime(DateTime t)
        {
            SystemTime st = new SystemTime();
            st.Year = (short)t.Year;
            st.Month = (short)t.Month;
            st.Day = (short)t.Day;
            st.Hour = (short)t.Hour;
            st.Minute = (short)t.Minute;
            st.Second = (short)t.Second;
            return st;
        }
    }
}