using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace ClockRack
{
public partial class TimeZoneManager
{
    [StructLayout(LayoutKind.Sequential)]
    struct SYSTEMTIME
    {
        public ushort wYear;
        public ushort wMonth;
        public ushort wDayOfWeek;
        public ushort wDay;
        public ushort wHour;
        public ushort wMinute;
        public ushort wSecond;
        public ushort wMilliseconds;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct TIME_ZONE_INFORMATION
    {
        public int Bias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string StandardName;
        public SYSTEMTIME StandardDate;
        public int StandardBias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DaylightName;
        public SYSTEMTIME DaylightDate;
        public int DaylightBias;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct DYNAMIC_TIME_ZONE_INFORMATION
    {
        public int Bias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string StandardName;
        public SYSTEMTIME StandardDate;
        public int StandardBias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DaylightName;
        public SYSTEMTIME DaylightDate;
        public int DaylightBias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string TimeZoneKeyName;
        public byte DynamicDaylightTimeDisabled;
    }

    [DllImport("Advapi32.dll")]
    static extern uint EnumDynamicTimeZoneInformation(uint index, 
                                        ref DYNAMIC_TIME_ZONE_INFORMATION dynamicTzi);

    [DllImport("kernel32.dll")]
    static extern byte GetTimeZoneInformationForYear(ushort year, 
                                        ref DYNAMIC_TIME_ZONE_INFORMATION dtzi, 
                                        out TIME_ZONE_INFORMATION tzi);

    [DllImport("kernel32.dll")]
    static extern byte SystemTimeToTzSpecificLocalTime(ref TIME_ZONE_INFORMATION tzi, 
                                        ref SYSTEMTIME utc, out SYSTEMTIME local);

    // Wewnętrzny słownik do wyszukiwania wartości DYNAMIC_TIME_ZONE_INFORMATION na podstawie kluczy
    Dictionary<string, DYNAMIC_TIME_ZONE_INFORMATION> dynamicTzis = 
                                    new Dictionary<string, DYNAMIC_TIME_ZONE_INFORMATION>();

    public TimeZoneManager()
    {
        uint index = 0;
        DYNAMIC_TIME_ZONE_INFORMATION tzi = new DYNAMIC_TIME_ZONE_INFORMATION();
        List<TimeZoneDisplayInfo> displayInformation = new List<TimeZoneDisplayInfo>();

        // Pętla po strefach czasowych
        while (0 == EnumDynamicTimeZoneInformation(index, ref tzi))
        {
            dynamicTzis.Add(tzi.TimeZoneKeyName, tzi);

            // Tworzenie TimeZoneDisplayInfo na potrzeby właściwości publicznej
            TimeZoneDisplayInfo displayInfo = new TimeZoneDisplayInfo 
            { 
                Bias = tzi.Bias, 
                TimeZoneKey = tzi.TimeZoneKeyName 
            };

            // Wyszukiwanie ciągu do wyświetlenia
            if (displayStrings.ContainsKey(tzi.TimeZoneKeyName))
            {
                displayInfo.Display = displayStrings[tzi.TimeZoneKeyName];
            }
            else if (displayStrings.ContainsKey(tzi.StandardName))
            {
                displayInfo.Display = displayStrings[tzi.StandardName];
            }
            // Lub wyliczenie 
            else
            {
                if (tzi.Bias == 0)
                    displayInfo.Display = "(UTC) ";
                else
                    displayInfo.Display = String.Format("(UTC{0}{1:D2}:{2:D2}) ", 
                                                        tzi.Bias > 0 ? '-' : '+', 
                                                        Math.Abs(tzi.Bias) / 60, 
                                                        Math.Abs(tzi.Bias) % 60);
                displayInfo.Display += tzi.TimeZoneKeyName;
            }

            // Dodanie do kolekcji
            displayInformation.Add(displayInfo);

            // Przygotowanie do następnej iteracji
            index += 1;
            tzi = new DYNAMIC_TIME_ZONE_INFORMATION();
        }

        // Posortowanie wyśweitlanych elementów
        displayInformation.Sort((TimeZoneDisplayInfo info1, TimeZoneDisplayInfo info2) =>
            {
                return info2.Bias.CompareTo(info1.Bias);
            });

        // Przypisanie do właściwości publicznej
        this.DisplayInformation = displayInformation;
    }

    // Interfejs publiczny
    public IList<TimeZoneDisplayInfo> DisplayInformation { protected set; get; }

    public DateTime GetLocalTime(string timeZoneKey, DateTime utc)
    {
        // Konwersja na Win32 SYSTEMTIME
        SYSTEMTIME utcSysTime = new SYSTEMTIME
        {
            wYear = (ushort)utc.Year,
            wMonth = (ushort)utc.Month,
            wDay = (ushort)utc.Day,
            wHour = (ushort)utc.Hour,
            wMinute = (ushort)utc.Minute,
            wSecond = (ushort)utc.Second,
            wMilliseconds = (ushort)utc.Millisecond
        };

        // Konwersja na czas lokalny
        DYNAMIC_TIME_ZONE_INFORMATION dtzi = dynamicTzis[timeZoneKey];
        TIME_ZONE_INFORMATION tzi = new TIME_ZONE_INFORMATION();
        GetTimeZoneInformationForYear((ushort)utc.Year, ref dtzi, out tzi);

        SYSTEMTIME localSysTime = new SYSTEMTIME();
        SystemTimeToTzSpecificLocalTime(ref tzi, ref utcSysTime, out localSysTime);

        // Konwersja SYSTEMTIME na DateTime
        return new DateTime(localSysTime.wYear, localSysTime.wMonth, localSysTime.wDay, 
                            localSysTime.wHour, localSysTime.wMinute, 
                            localSysTime.wSecond, localSysTime.wMilliseconds);
    }
}
}
