using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.vcard;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace ISIM
{
    public static class Global
    {
        /// <summary>
        /// Enum dostupnosti s přiřazenými prioritami (přetypováním na integer se získá přiřazené číslo)
        /// </summary>
        public enum Availability { Online = 1, Away = 2, DND = 3, Invisible = 4, Blocked = 5, Offline = 6, Conference = 7 };

        // globální proměnné
        public static bool disconnected = true;
        // jen pro výpis na odchozích zprávách
        public static string myNick;
        // pro hlášky aplikace v chatu
        public static string ISIM = "ISIM";
        // app data, registry a jiné vystupování aplikace
        public static string appName = "ISIM messenger";
        // mail na který elis pošle bug report
        public static string developerEmail = "david.jancik@sczdavos.eu";
        // cesta odkud byla spuštěná aplikace, zjistil sem, že je to potřeba hlavně když se aplikace spustí po spuštění os, pro přístup k externím souborům je třeba absolutní cesta
        public static string appRunPath = System.Windows.Forms.Application.StartupPath + Path.DirectorySeparatorChar;
        // cesta do app data
        public static string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + Path.DirectorySeparatorChar + Global.appName + Path.DirectorySeparatorChar;
        
        public static Font chatFont = new Font("Arial", 8.25f, FontStyle.Regular, GraphicsUnit.Point, ((byte)0));

        public static bool elisActivated = false;

        // stavy dostupnosti
        public static bool awayAuto, away, dnd;

        // klíč -> o co se jedná (např. settingsDictionary["ISIMsettings"]) hodnota id + hodnota
        public static Dictionary<string, Dictionary<string, string>> settingsDictionary = new Dictionary<string, Dictionary<string, string>>();

        // klíč -> o co se jedná (např. favourite) hodnota id uživatele
        public static Dictionary<string, List<string>> userSpecialList = new Dictionary<string, List<string>>();

        static Global()
        {
            if (!Directory.Exists(appDataFolder))
                Directory.CreateDirectory(appDataFolder);

            // tohle je úplně megaultrahustodémonskymegapřísně drsný
            // vytvořím si tu slovníky jaký chcu a ty se pak automaticky uloží a načtou při každém ukončení/startu aplikace
            settingsDictionary.Add("ISIMsettings", new Dictionary<string, string>());
            settingsDictionary.Add("bannedList", new Dictionary<string, string>());
            settingsDictionary.Add("contactNames", new Dictionary<string, string>());

            // podobný jako předchozí s tím, že zde jen seznam kontaktů dle jejich id, sloužicí například pro info, že se má připomenout dostupnost kontaktu
            userSpecialList.Add("remindWhenAvailable", new List<string>());
            userSpecialList.Add("favouriteList", new List<string>());
        }

        /// <summary>
        /// Pokud klíč existuje, přepíše jeho hodnotu. Jinak přidá. Slouží pro nastavení ISIMu.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void ChangeISIMsettings(string key, string value)
        {
            if (settingsDictionary["ISIMsettings"].ContainsKey(key))
                settingsDictionary["ISIMsettings"][key] = value;
            else
                settingsDictionary["ISIMsettings"].Add(key, value);
        }


        /// <summary>
        /// Instance okýnek
        /// </summary>
        public static class OpenForms
        {
            public static MainForm mainForm;
            public static ChatForm chatForm;
            public static PreferencesForm preferencesForm;

            public static Dictionary<string, ShowDialogs.MessageDialog> messageDialogList = new Dictionary<string, ShowDialogs.MessageDialog>();
        }



        /// <summary>
        /// Udržuje spuštěnou pouze jednu instanci aplikace
        /// </summary>
        public static class SingleInstance
        {
            public const int HWND_BROADCAST = 0xffff;
            public static int WM_SHOWME;
            [DllImport("user32")]
            public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
            [DllImport("user32")]
            public static extern int RegisterWindowMessage(string message);

            static SingleInstance()
            {
                // tato zpráva by měla být unikátní, aby ji neměla nastavenou jiná aplikace
                WM_SHOWME = RegisterWindowMessage("ISIM_SHOW_FORM");
            }

            public static void ShowForm()
            {
                SingleInstance.PostMessage(
                    (IntPtr)SingleInstance.HWND_BROADCAST,
                    SingleInstance.WM_SHOWME,
                    IntPtr.Zero,
                    IntPtr.Zero);
            }
        }



        /// <summary>
        /// FUnkce messengeru
        /// </summary>
        public static class ISIMFunctions
        {
            [DllImport("user32.dll")]
            private extern static IntPtr GetForegroundWindow();

            private static object locker = new object();
            /// <summary>
            /// Přidání tabu případné otevření chat okýnka
            /// </summary>
            /// <param name="contact"></param>
            /// <param name="selectTab"></param>
            public static void OpenChatForm(ISIMContact contact, bool selectTab = false)
            {
                // thread safe - zamču si to a kdyby se ta metoda zavolala znovu, než se stihne vytvořit instance chatFormu tak se musí počkat než locker přestane být používán tím pádem se neotevře víc vokýnek
                lock (locker)
                {
                    if (Global.OpenForms.chatForm == null)
                    {
                        // Abych zajistil otevření na hlavním threadu
                        Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                        {
                            if (Application.OpenForms.OfType<ChatForm>().Any())
                            {
                                Global.OpenForms.chatForm = Application.OpenForms.OfType<ChatForm>().First();
                                OpenChatForm(contact, selectTab);
                                return;
                            }

                            bool dontActivate = false;
                            if (Global.settingsDictionary["ISIMsettings"].ContainsKey("dontActivateChatWindow"))
                                dontActivate = bool.Parse(Global.settingsDictionary["ISIMsettings"]["dontActivateChatWindow"]);

                            Global.OpenForms.chatForm = new ChatForm(contact);

                            if (((Global.dnd) || (dontActivate)) && (!selectTab)) Global.OpenForms.chatForm.WindowState = FormWindowState.Minimized;
                            Global.OpenForms.chatForm.Show();  
                            if (((!Global.dnd) && (!dontActivate)) || (selectTab)) Global.OpenForms.chatForm.Activate();

                            if (GetForegroundWindow() != Global.OpenForms.chatForm.Handle)
                                FlashWindow.Flash(Global.OpenForms.chatForm);
                        }));
                    }
                    else
                    {
                        Global.OpenForms.chatForm.BeginInvoke(new MethodInvoker(() =>
                        {
                            Global.OpenForms.chatForm.chatTabs.AddTab(contact, selectTab);

                            if (selectTab)
                            {
                                Global.OpenForms.chatForm.WindowState = FormWindowState.Normal;
                                Global.OpenForms.chatForm.Activate();
                            }

                            if (GetForegroundWindow() != Global.OpenForms.chatForm.Handle)
                                FlashWindow.Flash(Global.OpenForms.chatForm);
                        }));
                    }
                }
            }

            /// <summary>
            /// Přidání zprávy do chat okýnka dané konverzace a případné otevření okýnka
            /// </summary>
            /// <param name="contact"></param>
            /// <param name="message"></param>
            /// <param name="time"></param>
            public static void AddChatMessage(ISIMContact contact, string message, DateTime time, string from)
            {
                // kontrola, zda není blokovaný (v tom případě se mu to náležitě sdělí a nic dalšího se neprovede - okýnko chatu se neotevře)
                if (Global.settingsDictionary["bannedList"].ContainsKey(contact.id))
                { 
                    string bannedMessage = "Elis: " + Global.LangManager.Translate("iAmSorryBut") + " " + Global.LangManager.Translate("youAreInBannedList") + ".";
                    if (Global.settingsDictionary["bannedList"][contact.id].Replace(" ", "").Length > 0)
                        bannedMessage += " " + Global.LangManager.Translate("reason") + ": " + Global.settingsDictionary["bannedList"][contact.id];
                    SendChatMessage(contact, bannedMessage, false);

                    string tempMsg = "\n" + "[color:3]" + Converter.GetRenamedContactName(contact) + " (" + time.ToString("H:mm:ss  dd/MM/yyyy") + ")" + "[/color:3]" + "\n" + TextEdit.CloseTags(message) + "\n" + "[color:2]" + Global.ISIM + " (" + time.ToString("H:mm:ss  dd/MM/yyyy") + ")" + "[/color:2]" + "\n" + bannedMessage;

                    SaveConversation(contact.id, tempMsg);
                    return;
                }

                OpenChatForm(contact);

                Global.OpenForms.chatForm.BeginInvoke(new MethodInvoker(() =>
                {
                    (Global.OpenForms.chatForm.chatTabs.TabPages[contact.id] as ChatTabPage).AddChatMessage(contact, message, time, from);

                    if ((Global.OpenForms.chatForm.chatTabs.TabPages[contact.id] != Global.OpenForms.chatForm.chatTabs.SelectedTab) || (Global.OpenForms.chatForm.WindowState == FormWindowState.Minimized))
                    {
                        contact.unreadedMessage = true;
                        contact.isComposingMessage = false;

                        Global.OpenForms.chatForm.Text = Global.Converter.GetRenamedContactName(contact);

                        Global.OpenForms.chatForm.Invoke(new MethodInvoker(() => { Global.OpenForms.chatForm.Refresh(); }));
                        Global.OpenForms.mainForm.Invoke(new MethodInvoker(() => { Global.OpenForms.mainForm.Refresh(); }));
                    }
                }));

                // nemám někoho rád, nech si s ním povídá Elis :))
                if ((from != myNick) && (from != ISIM))
                {
                    if (contact.elisActive)
                        ElisCore.IQPokydRespond(contact, message);
                }
                else
                    ElisCore.AutoRespond(contact, (from == myNick) || (from == ISIM));
            }

            /// <summary>
            /// Odeslání zprávy kontaktu
            /// </summary>
            /// <param name="contact"></param>
            /// <param name="message"></param>
            public static void SendChatMessage(ISIMContact contact, string message, bool addToChatForm = true)
            {
                // nejprve se zkontroluje dle nějakého identifikátoru které knihovně daný objekt náleží
                if (contact.contactObject is XmppContact.ContactObject)
                {
                    Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                    {
                        // nastala chyba - odpojeno od serveru
                        if (((contact.contactObject as XmppContact.ContactObject).xmppClient == null) || ((contact.contactObject as XmppContact.ContactObject).xmppClient.XmppConnectionState != agsXMPP.XmppConnectionState.SessionStarted))
                            Global.ISIMFunctions.AddChatMessage(contact, Global.LangManager.Translate("messageSendFailed"), DateTime.Now, Global.ISIM);

                        // lze odeslat jen asi 1000 znaků dlouho zprávu, pokud je delší splitne se na části
                        foreach (string msg in message.SplitString(900))
                            (contact.contactObject as XmppContact.ContactObject).xmppClient.Send(new agsXMPP.protocol.client.Message(new agsXMPP.Jid(contact.id), MessageType.chat, msg));
                    }));
                }

                if (contact.contactObject is IcqContact.ContactObject)
                {
                    // blbě kódování v ICQ
                    message = message.RemoveDiacritics();
                    (contact.contactObject as IcqContact.ContactObject).session.Messaging.Send(new IcqSharp.Base.Message(new IcqSharp.Base.Contact(contact.id), IcqSharp.Base.MessageType.Outgoing, message));
                }
                
                if (addToChatForm)
                    Global.ISIMFunctions.AddChatMessage(contact, message, DateTime.Now, myNick);
            }

            /// <summary>
            /// Přidání kontaktu do kontakt listu v main formu - přiřazení do dané skupiny a sesortění
            /// </summary>
            /// <param name="contact"></param>
            public static void AddContact(ISIMContact contact)
            {
                Global.OpenForms.mainForm.BeginInvoke(new MethodInvoker(() =>
                {
                    if (!Global.OpenForms.mainForm.contactListView.Items.ContainsKey(contact.id))
                    {
                        ContactListViewItem contactItem = new ContactListViewItem(contact);
                        contactItem.Text = Converter.GetRenamedContactName(contact);
                        Global.OpenForms.mainForm.contactListView.Items.Add(contactItem);

                        if (Global.userSpecialList["favouriteList"].Contains(contact.id))
                            Global.OpenForms.mainForm.contactListView.Items[contact.id].Group = Global.OpenForms.mainForm.contactListView.Groups["favouriteListViewGroup"];
                        else if (contact.availability == Availability.Offline)
                            Global.OpenForms.mainForm.contactListView.Items[contact.id].Group = Global.OpenForms.mainForm.contactListView.Groups["offlineListViewGroup"];
                        else
                            Global.OpenForms.mainForm.contactListView.Items[contact.id].Group = Global.OpenForms.mainForm.contactListView.Groups["onlineListViewGroup"];

                        Global.OpenForms.mainForm.contactListView.Sort();
                    }
                }));
            }

            /// <summary>
            /// Změna dostupnosti kontaktu - resortění, změna skupiny
            /// </summary>
            /// <param name="id"></param>
            /// <param name="availability"></param>
            public static void EditContactAvailability(string id, Availability availability)
            {
                Global.OpenForms.mainForm.BeginInvoke(new MethodInvoker(() =>
                {
                    if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(id))
                    {
                        ISIMContact contact = (Global.OpenForms.mainForm.contactListView.Items[id] as ContactListViewItem).contact;

                        if (Global.userSpecialList["remindWhenAvailable"].Contains(id) && (contact.availability == Availability.Offline) && (availability != Availability.Offline))
                        {
                            Global.userSpecialList["remindWhenAvailable"].Remove(id);
                            AddChatMessage(contact, Global.LangManager.Translate("contactIsAvailable"), DateTime.Now, Global.ISIM);
                        }

                        contact.availability = availability;
                        if (!Global.userSpecialList["favouriteList"].Contains(contact.id))
                        {
                            if (availability == Availability.Offline)
                                Global.OpenForms.mainForm.contactListView.Items[id].Group = Global.OpenForms.mainForm.contactListView.Groups["offlineListViewGroup"];
                            else
                                Global.OpenForms.mainForm.contactListView.Items[id].Group = Global.OpenForms.mainForm.contactListView.Groups["onlineListViewGroup"];
                        }

                        Global.OpenForms.mainForm.contactListView.Sort();

                        if (Global.OpenForms.chatForm != null)
                        {
                            Global.OpenForms.chatForm.chatTabs.BeginInvoke(new MethodInvoker(() =>
                            {
                                if (Global.OpenForms.chatForm.chatTabs.TabPages.ContainsKey(contact.id))
                                    Global.OpenForms.chatForm.chatTabs.Refresh();
                            }));
                        }
                    }
                }));
            }

            /// <summary>
            /// No smaže kontakt
            /// </summary>
            /// <param name="id"></param>
            public static void RemoveContact(ISIMContact contact)
            {
                Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                {
                    Global.OpenForms.mainForm.contactListView.Items.RemoveByKey(contact.id);
                }));
            }

            /// <summary>
            /// Tož zablokuje kontakt - u XMPP a ICQ nepříjímá zprávy a odpovídá, že je kontakt blokován
            /// </summary>
            /// <param name="contact"></param>
            public static void BlockContact(ISIMContact contact, string message = null)
            {
                Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                {
                    bool block = !Global.settingsDictionary["bannedList"].ContainsKey(contact.id);

                    if (block)
                        Global.settingsDictionary["bannedList"].Add(contact.id, message);
                    else
                        Global.settingsDictionary["bannedList"].Remove(contact.id);

                    // neproblikává potom
                    int index = Global.OpenForms.mainForm.contactListView.Items.IndexOfKey(contact.id);
                    if (index != -1)
                        Global.OpenForms.mainForm.contactListView.RedrawItems(index, index, false);// Refresh();
                }));
            }

            /// <summary>
            /// Přidá kontakt do oblíbených, pokud tam již je - tak jej odebere
            /// </summary>
            /// <param name="contact"></param>
            public static void FavouriteContact(ISIMContact contact)
            {
                Global.OpenForms.mainForm.contactListView.BeginInvoke(new MethodInvoker(() =>
                {
                    ContactListViewItem contactItem = Global.OpenForms.mainForm.contactListView.Items[contact.id] as ContactListViewItem;
                    ListView contactListView = Global.OpenForms.mainForm.contactListView;

                    if (contactItem.Group != contactListView.Groups["favouriteListViewGroup"])
                    {
                        contactItem.Group = contactListView.Groups["favouriteListViewGroup"];
                        if (!Global.userSpecialList["favouriteList"].Contains(contact.id))
                            Global.userSpecialList["favouriteList"].Add(contact.id);
                    }
                    else
                    {
                        if (contact.availability == Availability.Offline)
                            contactItem.Group = contactListView.Groups["offlineListViewGroup"];
                        else
                            contactItem.Group = contactListView.Groups["onlineListViewGroup"];
                        if (Global.userSpecialList["favouriteList"].Contains(contact.id))
                            Global.userSpecialList["favouriteList"].Remove(contact.id);
                    }
                    contactListView.Sort();
                }));
            }


            /// <summary>
            /// Gray filter. Překreslí obrázek na šedý.
            /// </summary>
            /// <param name="original"></param>
            /// <returns></returns>
            public static Bitmap MakeImageGrayscale(Bitmap original)
            {
                //create a blank bitmap the same size as original
                Bitmap newBitmap = new Bitmap(original.Width, original.Height);

                //get a graphics object from the new image
                Graphics g = Graphics.FromImage(newBitmap);

                //create the grayscale ColorMatrix
                ColorMatrix colorMatrix = new ColorMatrix(
                   new float[][] 
              {
                 new float[] {.3f, .3f, .3f, 0, 0},
                 new float[] {.59f, .59f, .59f, 0, 0},
                 new float[] {.11f, .11f, .11f, 0, 0},
                 new float[] {0, 0, 0, 1, 0},
                 new float[] {0, 0, 0, 0, 1}
              });

                //create some image attributes
                ImageAttributes attributes = new ImageAttributes();

                //set the color matrix attribute
                attributes.SetColorMatrix(colorMatrix);

                //draw the original image on the new image
                //using the grayscale color matrix
                g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
                   0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

                //dispose the Graphics object
                g.Dispose();
                return newBitmap;
            }


            /// <summary>
            /// Získá to veškeré info o kontaktu. Získá se foto a jméno uživatele (někdy z rosteru dojde jen nick nebo nedojde tady ho získáme)
            /// </summary>
            /// <param name="connection"></param>
            /// <param name="identity"></param>
            public static void GetXmppContactAvatar(XmppClientConnection connection, Jid identity)
            {
                VcardIq viq = new VcardIq(IqType.get);
                viq.Id = identity;
                viq.To = identity;
                viq.Vcard.JabberId = identity;
                connection.IqGrabber.SendIq(viq, new IqCB(VcardResult), null);
            }

            private static void VcardResult(object sender, IQ iq, object data)
            {
                if (iq.Type == IqType.result)
                {
                    Vcard vcard = iq.Vcard;
                    string id = iq.From.Bare;

                    if (vcard != null)
                    {
                        Photo photo = vcard.Photo;
                        if (photo != null)
                        {
                            SaveAvatar(vcard.Photo.Image, id);
                            Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                            {
                                if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(id))
                                {
                                    ISIMContact contact = (Global.OpenForms.mainForm.contactListView.Items[id] as ContactListViewItem).contact;
                                    if ((!Global.settingsDictionary["contactNames"].ContainsKey(id)) && (vcard.Fullname.Replace(" ", "").Length > 0))
                                        Global.ISIMFunctions.RenameContact((Global.OpenForms.mainForm.contactListView.Items[id] as ContactListViewItem).contact, vcard.Fullname);
                                    (Global.OpenForms.mainForm.contactListView.Items[id] as ContactListViewItem).contact.name = vcard.Fullname;
                                }
                                Global.OpenForms.mainForm.Refresh();
                            }));
                        }
                    }
                }
            }

            /// <summary>
            /// Uloží daný obrázek jako avatar. Resize ne 50x50px
            /// </summary>
            /// <param name="avatar"></param>
            /// <param name="chatId"></param>
            public static void SaveAvatar(Image avatar, string chatId)
            {
                if (!Directory.Exists(Global.appDataFolder + "cache"))
                    Directory.CreateDirectory(Global.appDataFolder + "cache");

                if (avatar == null) return;

                try
                {
                    chatId = TextEdit.RemoveInvalidCharactersFromFileName(chatId);
                    avatar.GetThumbnailImage(50, 50, null, IntPtr.Zero).Save(Global.appDataFolder + "cache" + Path.DirectorySeparatorChar + chatId + "_avatar.png", System.Drawing.Imaging.ImageFormat.Png);
                }
                catch (System.Runtime.InteropServices.ExternalException)
                {
                    Console.WriteLine("cannot save avatar, file in use");
                }
            }

            /// <summary>
            /// Připíše do souboru daný string.
            /// </summary>
            /// <param name="chatId"></param>
            /// <param name="message"></param>
            public static void SaveConversation(string chatId, string message)
            {
                List<string> conversation = new List<string>();
                conversation.Add(message);
                SaveConversation(chatId, conversation);
            }

            /// <summary>
            /// Uloží danou konverzaci
            /// </summary>
            /// <param name="chatId"></param>
            /// <param name="conversation"></param>
            public static void SaveConversation(string chatId, List<string> conversation)
            {
                if (conversation.Count == 0) return;

                if (!Directory.Exists(Global.appDataFolder + "conversations"))
                    Directory.CreateDirectory(Global.appDataFolder + "conversations");

                chatId = TextEdit.RemoveInvalidCharactersFromFileName(chatId);

                using (StreamWriter sw = new StreamWriter(Global.appDataFolder + "conversations" + Path.DirectorySeparatorChar + chatId + ".txt", true, Encoding.UTF8))
                {
                    foreach (string c in conversation)
                        sw.Write(c + Environment.NewLine);
                }
            }

            /// <summary>
            /// Načte posledních x řádků z dané konverzace a vrátí ji jako nezformátovaný plain text
            /// </summary>
            /// <param name="chatId"></param>
            /// <param name="rowsCount"></param>
            /// <returns></returns>
            public static string LoadConversation(string chatId, int rowsCount = 10)
            {
                chatId = TextEdit.RemoveInvalidCharactersFromFileName(chatId);
                string conv = "";
                try
                {
                    using (StreamReader sr = new StreamReader(Global.appDataFolder + "conversations" + Path.DirectorySeparatorChar + chatId + ".txt", Encoding.UTF8))
                    {
                        sr.BaseStream.Seek(0, SeekOrigin.End);

                        int c;
                        int count = 0;
                        long pos = -1;

                        while (count < rowsCount)
                        {
                            try
                            {
                                sr.BaseStream.Seek(pos, SeekOrigin.End);
                            }
                            catch
                            {
                                ++pos;
                                break;
                            }
                            c = sr.Read();
                            sr.DiscardBufferedData();

                            if (c == Convert.ToInt32('\n'))
                                ++count;
                            --pos;
                        }

                        sr.BaseStream.Seek(pos, SeekOrigin.End);
                        conv = sr.ReadToEnd();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("load conv ex" + ex.Message);
                }

                return conv;
            }

            /// <summary>
            /// Kontakt píše zprávu
            /// </summary>
            /// <param name="id"></param>
            /// <param name="stop"></param>
            public static void ContactIsComposing(string id, bool stop = false)
            {
                Global.OpenForms.mainForm.contactListView.Invoke(new MethodInvoker(() =>
                {
                    if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(id))
                    {
                        ISIMContact contact = (Global.OpenForms.mainForm.contactListView.Items[id] as ContactListViewItem).contact;

                        if (contact.isComposingMessage != !stop)
                        {
                            contact.isComposingMessage = !stop;

                            // překreslení dané položky bez blikání
                            int index = Global.OpenForms.mainForm.contactListView.Items.IndexOfKey(contact.id);
                            if (index != -1)
                                Global.OpenForms.mainForm.contactListView.RedrawItems(index, index, false);

                            if (Global.OpenForms.chatForm != null)
                            {
                                Global.OpenForms.chatForm.Invoke(new MethodInvoker(() =>
                                {
                                    if (Global.OpenForms.chatForm.chatTabs.TabPages.ContainsKey(id))
                                        Global.OpenForms.chatForm.chatTabs.Refresh();
                                }));
                            }

                            // za 10 vteřin se nastaví že už nepíše
                            System.ComponentModel.BackgroundWorker barInvoker = new System.ComponentModel.BackgroundWorker();
                            barInvoker.DoWork += delegate
                            {
                                Thread.Sleep(TimeSpan.FromSeconds(10));
                                ContactIsComposing(id, true);
                            };
                            barInvoker.RunWorkerAsync();
                        }
                    }
                }));
            }

            /// <summary>
            /// Odešle informaci, že píšu zprávu
            /// </summary>
            /// <param name="contact"></param>
            public static void SendComposingNotification(ISIMContact contact)
            {
                if (contact.contactObject is XmppContact.ContactObject)
                {
                    agsXMPP.protocol.client.Message msg = new agsXMPP.protocol.client.Message(new Jid(contact.id), MessageType.chat, "");
                    msg.AddTag("composing xmlns='http://jabber.org/protocol/chatstates'");
                    (contact.contactObject as XmppContact.ContactObject).xmppClient.Send(msg);
                }

                if (contact.contactObject is IcqContact.ContactObject)
                {
                    (contact.contactObject as IcqContact.ContactObject).session.Messaging.SendTypingNotification(new IcqSharp.Base.Contact(contact.id), IcqSharp.Base.TypingNotification.Begun);
                }
            }


            public static void ChangeMyAvailability(int availability)
            {
                Presence xmppAvailability = new Presence();
                IcqSharp.Base.Status icqAvailability = new IcqSharp.Base.Status();

                switch (availability)
                {
                    case 0:
                        xmppAvailability.Show = ShowType.chat;
                        xmppAvailability.Status = "Online";
                        xmppAvailability.Type = PresenceType.available;
                        icqAvailability = IcqSharp.Base.Status.Online;

                        away = awayAuto = dnd = false;
                        break;

                    case 1:
                        xmppAvailability.Show = ShowType.away;
                        xmppAvailability.Status = "Away";
                        xmppAvailability.Type = PresenceType.available;
                        icqAvailability = IcqSharp.Base.Status.Away;

                        away = true;
                        dnd = false;
                        break;

                    case 2:
                        xmppAvailability.Show = ShowType.dnd;
                        xmppAvailability.Status = "DND";
                        xmppAvailability.Type = PresenceType.available;
                        icqAvailability = IcqSharp.Base.Status.DoNotDisturb;

                        away = awayAuto = false;
                        dnd = true;
                        break;

                    case 3:
                        xmppAvailability.Show = ShowType.NONE;
                        xmppAvailability.Status = "Invisible";
                        xmppAvailability.Type = PresenceType.invisible;
                        icqAvailability = IcqSharp.Base.Status.Invisible;

                        away = awayAuto = dnd = false;
                        break;
                }

                Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                {
                    if ((Global.OpenForms.mainForm.fb != null) && (Global.OpenForms.mainForm.fb.xmppClient != null))
                        Global.OpenForms.mainForm.fb.xmppClient.Send(xmppAvailability);
                    if ((Global.OpenForms.mainForm.gt != null) && (Global.OpenForms.mainForm.gt.xmppClient != null))
                        Global.OpenForms.mainForm.gt.xmppClient.Send(xmppAvailability);
                    if ((Global.OpenForms.mainForm.icq != null) && (Global.OpenForms.mainForm.icq.session != null))
                        Global.OpenForms.mainForm.icq.session.Status = icqAvailability;
                }));
            }



            /// <summary>
            /// Uložení a šifrace loginu
            /// </summary>
            public static void SaveLogin()
            {
                using (StreamWriter sw = new StreamWriter(Global.appDataFolder + "login.dat", false, Encoding.UTF8))
                {
                    if ((Login.Jabber.userJid.Length > 0) && (Login.Jabber.password.Length > 0))
                    {
                        sw.WriteLine(Crypto.QuickEncrypt("jabberUsername:=" + Login.Jabber.userJid));
                        sw.WriteLine(Crypto.QuickEncrypt("jabberPassword:=" + Login.Jabber.password));
                    }

                    if ((Login.Facebook.userJid.Length > 0) && (Login.Facebook.password.Length > 0))
                    {
                        sw.WriteLine(Crypto.QuickEncrypt("facebookUsername:=" + Login.Facebook.userJid));
                        sw.WriteLine(Crypto.QuickEncrypt("facebookPassword:=" + Login.Facebook.password));
                    }

                    if ((Login.GoogleTalk.userJid.Length > 0) && (Login.GoogleTalk.password.Length > 0))
                    {
                        sw.WriteLine(Crypto.QuickEncrypt("googleUsername:=" + Login.GoogleTalk.userJid));
                        sw.WriteLine(Crypto.QuickEncrypt("googlePassword:=" + Login.GoogleTalk.password));
                    }

                    if ((Login.Icq.uin.Length > 0) && (Login.Icq.password.Length > 0))
                    {
                        sw.WriteLine(Crypto.QuickEncrypt("icqUsername:=" + Login.Icq.uin));
                        sw.WriteLine(Crypto.QuickEncrypt("icqPassword:=" + Login.Icq.password));
                    }
                }
            }

            /// <summary>
            /// Načtení a dešifrování loginu
            /// </summary>
            public static void LoadLogin()
            {
                try
                {
                    using (StreamReader sr = new StreamReader(Global.appDataFolder + "login.dat", Encoding.UTF8))
                    {
                        string line;
                        string[] splited;
                        while ((line = sr.ReadLine()) != null)
                        {
                            line = Crypto.QuickDecrypt(line);
                            splited = line.Split(new string[] { ":=" }, StringSplitOptions.None);

                            if (splited[0] == "jabberUsername") Login.Jabber.userJid = splited[1];
                            if (splited[0] == "jabberPassword") Login.Jabber.password = splited[1];

                            if (splited[0] == "facebookUsername") Login.Facebook.userJid = splited[1];
                            if (splited[0] == "facebookPassword") Login.Facebook.password = splited[1];

                            if (splited[0] == "googleUsername") Login.GoogleTalk.userJid = splited[1];
                            if (splited[0] == "googlePassword") Login.GoogleTalk.password = splited[1];

                            if (splited[0] == "icqUsername") Login.Icq.uin = splited[1];
                            if (splited[0] == "icqPassword") Login.Icq.password = splited[1];
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("cannot load login" + ex.Message);
                }
            }

            /// <summary>
            /// Smaže soubor s loginem z disku
            /// </summary>
            public static void DeleteLogin()
            {
                if (File.Exists(Global.appDataFolder + "login.dat"))
                {
                    try
                    {
                        File.Delete(Global.appDataFolder + "login.dat");
                    }
                    catch
                    {
                        Console.WriteLine("cannot del. login");
                    }
                }
            }



            /// <summary>
            /// Načte nastavení ze souboru nahraje do dictionary a vrátí tuto kolekci.
            /// </summary>
            /// <param name="filePath"></param>
            /// <returns></returns>
            public static Dictionary<string, string> LoadISIMsettingsFile(string filePath)
            {
                Dictionary<string, string> settings = new Dictionary<string, string>();
                try
                {
                    using (StreamReader sr = new StreamReader(Global.appDataFolder + filePath + ".ini", Encoding.UTF8))
                    {
                        string line;
                        string[] splited;
                        while ((line = sr.ReadLine()) != null)
                        {
                            splited = line.Split(new string[] { ":=" }, StringSplitOptions.None);
                            if (splited.Length > 2)
                                for (int i = 2; i < splited.Length; i++)
                                    splited[1] += ":=" + splited[i];

                            settings.Add(splited[0], splited[1]);
                        }
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine("load settings ex - probably file not exists");
                }

                return settings;
            }


            /// <summary>
            /// Uloží nastavení z dictionary ve formátu klíč:=hodnota
            /// </summary>
            /// <param name="filePath"></param>
            /// <param name="dictionary"></param>
            public static void SaveISIMsettingsFile(string filePath, Dictionary<string, string> dictionary)
            {
                using (StreamWriter sw = new StreamWriter(Global.appDataFolder + filePath + ".ini", false, Encoding.UTF8))
                {
                    foreach (var entry in dictionary)
                    {
                        sw.WriteLine(entry.Key + ":=" + entry.Value);
                    }
                }
            }

            /// <summary>
            /// Načte nastavení ve formátu listu. Co řádek to hodnota.
            /// </summary>
            /// <param name="filePath"></param>
            /// <returns></returns>
            public static List<string> LoadISIMsettingsListFile(string filePath)
            {
                List<string> settings = new List<string>();
                try
                {
                    using (StreamReader sr = new StreamReader(Global.appDataFolder + filePath + ".ini", Encoding.UTF8))
                    {
                        string line;
                        while ((line = sr.ReadLine()) != null)
                        {
                            settings.Add(line);
                        }
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine("load settings ex - file not exists");
                }

                return settings;
            }


            /// <summary>
            /// Uloží nastavení z listu. Co řádek to hodnota.
            /// </summary>
            /// <param name="filePath"></param>
            /// <param name="list"></param>
            public static void SaveISIMsettingsFile(string filePath, List<string> list)
            {
                using (StreamWriter sw = new StreamWriter(Global.appDataFolder + filePath + ".ini", false, Encoding.UTF8))
                {
                    foreach (string contactId in list)
                    {
                        sw.WriteLine(contactId);
                    }
                }
            }


            /// <summary>
            /// Delete all conversations
            /// </summary>
            public static void DeleteConversations()
            {
                foreach (string file in Directory.GetFiles(Global.appDataFolder + "conversations"))
                {
                    try
                    {
                        File.Delete(file);
                    }
                    catch
                    {
                        Console.WriteLine("cannot delete file" + file);
                    }
                }
            }

            public static void RenameContact(ISIMContact contact, string newName)
            {
                if (newName.Replace(" ", "").Length == 0)
                    newName = contact.name;

                if (Global.settingsDictionary["contactNames"].ContainsKey(contact.id))
                    Global.settingsDictionary["contactNames"][contact.id] = newName;
                else
                    Global.settingsDictionary["contactNames"].Add(contact.id, newName);

                Global.OpenForms.mainForm.contactListView.Invoke(new MethodInvoker(() =>
                {
                    Global.OpenForms.mainForm.contactListView.Items[contact.id].Text = newName;
                    Global.OpenForms.mainForm.contactListView.Sort();
                }));

                if (Global.OpenForms.chatForm != null)
                {
                    Global.OpenForms.chatForm.chatTabs.Invoke(new MethodInvoker(() =>
                    {
                        if (Global.OpenForms.chatForm.chatTabs.TabPages.ContainsKey(contact.id))
                        {
                            Global.OpenForms.chatForm.chatTabs.TabPages[contact.id].Text = newName.ReduceText(20);
                            Global.OpenForms.chatForm.chatTabs.Refresh();
                            if (Global.OpenForms.chatForm.chatTabs.SelectedTab.Name == contact.id)
                                Global.OpenForms.chatForm.Text = newName;
                        }
                    }));
                }
            }


            public enum Sounds { incommingMessage = 0, incommingCall = 1 };
            private static string[] soundsFileNames = new string[] { "incoming_message.wav", "incomming_call.wav" };

            static bool isPlaying = false;
            static SoundPlayer sp = new SoundPlayer();
            /// <summary>
            /// Přehraje daný zvuk.
            /// </summary>
            /// <param name="sound"></param>
            public static void PlaySound(Sounds sound, bool loop = false)
            {
                if (((Global.settingsDictionary["ISIMsettings"].ContainsKey("muteSounds")) && (bool.Parse(Global.settingsDictionary["ISIMsettings"]["muteSounds"]) == true)) || (Global.dnd) || (isPlaying)) return;

                try
                {
                    sp.SoundLocation = Global.appRunPath + "sounds" + Path.DirectorySeparatorChar + soundsFileNames[(int)sound];
                    if (loop)
                    {
                        isPlaying = true;
                        sp.PlayLooping();
                    }
                    else
                        sp.Play();

                    Console.WriteLine("playing sound");
                }
                catch
                {
                    Console.WriteLine("cannot play sound");
                }
            }

            public static void StopSound()
            {
                sp.Stop();
                isPlaying = false;
            }

            
            /// <summary>
            /// Zobrazí message dialog, který lze poté zavřít metodou CloseMessage
            /// </summary>
            /// <param name="id"></param>
            /// <param name="title"></param>
            /// <param name="message"></param>
            /// <param name="icon"></param>
            public static void ShowMessage(string id, string title, string message, MessageBoxIcon icon)
            {
                Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                {
                    if (Global.OpenForms.messageDialogList.ContainsKey(id))
                        Global.OpenForms.messageDialogList[id].Invoke(new MethodInvoker(() => { Global.OpenForms.messageDialogList[id].Activate(); }));
                    else
                    {
                        ShowDialogs.MessageDialog dialog = new ShowDialogs.MessageDialog(id);
                        dialog.Show(message, title, icon);
                    }
                }));
            }

            /// <summary>
            /// Zavře vlastní message dialog, pokud je zobrazen
            /// </summary>
            /// <param name="id"></param>
            public static void CloseMessage(string id)
            {
                if (Global.OpenForms.messageDialogList.ContainsKey(id))
                {
                    Global.OpenForms.messageDialogList[id].Invoke(new MethodInvoker(() =>
                    {
                        Global.OpenForms.messageDialogList[id].Close();
                    }));
                }
            }

            /// <summary>
            /// Pokud se něco změnilo z minulé verze co je třeba zmínit nebo předělat, taj se to šéfí
            /// </summary>
            public static void Migrate()
            {

            }
        }



        /// <summary>
        /// Konverter pro isim friendly data
        /// </summary>
        public static class Converter
        {
            public static string GetRenamedContactName(ISIMContact contact)
            {
                return GetRenamedContactName(contact.id, contact.name);
            }

            public static string GetRenamedContactName(string contactId, string contactName)
            { 
                if (Global.settingsDictionary["contactNames"].ContainsKey(contactId))
                    return Global.settingsDictionary["contactNames"][contactId];
                else
                    return contactName;
            }

            public static Availability GetContactAvailability(object availabilityObject)
            {
                if (availabilityObject is PresenceType)
                {
                    switch ((PresenceType)availabilityObject)
                    {
                        case PresenceType.available:
                            return Availability.Online;

                        case PresenceType.error:
                            return Availability.Offline;

                        case PresenceType.invisible:
                            return Availability.Invisible;

                        case PresenceType.unavailable:
                            return Availability.Offline;

                        // nemá to logiku, ale vzhledem k tomu, že k tomuhle stejně nedojde tak si to drze zneužívám brekeke :P
                        case PresenceType.probe:
                            return Availability.Away;

                        case PresenceType.subscribe:
                            return Availability.DND;

                        case PresenceType.subscribed:
                        case PresenceType.unsubscribe:
                        case PresenceType.unsubscribed:
                            return Availability.Invisible;
                    }
                }

                if (availabilityObject is IcqSharp.Base.Status)
                {
                    switch ((IcqSharp.Base.Status)availabilityObject)
                    {
                        case IcqSharp.Base.Status.Online:
                        case IcqSharp.Base.Status.FreeForChat:
                            return Availability.Online;

                        case IcqSharp.Base.Status.Away:
                            return Availability.Away;

                        case IcqSharp.Base.Status.DoNotDisturb:
                        case IcqSharp.Base.Status.Occupied:
                            return Availability.DND;

                        case IcqSharp.Base.Status.Offline:
                            return Availability.Offline;

                        case IcqSharp.Base.Status.Invisible:
                        case IcqSharp.Base.Status.NA:
                            return Availability.Invisible;
                    }
                }

                return Availability.Offline;
            }


            static Bitmap onlineIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "onlineIcon.png");
            static Bitmap awayIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "awayIcon.png");
            static Bitmap dndIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "dndIcon.png");
            static Bitmap invisibleIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "invisibleIcon.png");
            static Bitmap blockedIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "blockedIcon.png");
            static Bitmap offlineIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "offlineIcon.png");
            static Bitmap conferenceIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "conferenceIcon.png");

            public static Bitmap GetContactAvailabilityImage(Availability availability)
            {
                switch (availability)
                {
                    case Availability.Conference:
                        return conferenceIcon;

                    case Availability.Online:
                        return onlineIcon;

                    case Availability.Away:
                        return awayIcon;

                    case Availability.DND:
                        return dndIcon;

                    case Availability.Invisible:
                        return invisibleIcon;

                    case Availability.Blocked:
                        return blockedIcon;

                    case Availability.Offline:
                        return offlineIcon;

                    default:
                        return offlineIcon;
                }
            }

            static Bitmap fbIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "facebookIcon.png");
            static Bitmap gtIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "googleIcon.png");
            static Bitmap icqIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "icqIcon.png");
            static Bitmap xmppIcon = new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "xmppIcon.png");
            /// <summary>
            /// Vrátí ikonku daného protokolu
            /// </summary>
            /// <param name="protocolName">jabber, facebook, gtalk, jinak defaultní xmpp</param>
            /// <returns></returns>
            public static Bitmap GetProtocolIcon(string protocolName)
            {
                switch (protocolName)
                {
                    case "jabber":
                        return xmppIcon;

                    case "facebook":
                        return fbIcon;

                    case "gtalk":
                        return gtIcon;

                    case "ICQ":
                        return icqIcon;

                    default:
                        return xmppIcon;
                }
            }

            /// <summary>
            /// Podívá se do složky cache a zjistí, zda tam je daný soubor
            /// </summary>
            /// <param name="chatId"></param>
            /// <returns></returns>
            public static bool AvatarExists(string chatId)
            {
                chatId = TextEdit.RemoveInvalidCharactersFromFileName(chatId);
                if (Directory.Exists(Global.appDataFolder + "cache"))
                {
                    if (File.Exists(Global.appDataFolder + "cache" + Path.DirectorySeparatorChar + chatId + "_avatar.png"))
                        return true;
                }
                return false;
            }

            /// <summary>
            /// Vrátí avatar kontaktu ze složky, pokud tam není tak defaultní
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            public static Bitmap GetContactAvatar(string id)
            {
                id = TextEdit.RemoveInvalidCharactersFromFileName(id);
                if (AvatarExists(id))
                {
                    try
                    {
                        return new Bitmap(Global.appDataFolder + "cache" + Path.DirectorySeparatorChar + id + "_avatar.png");
                    }
                    catch (ArgumentException)
                    { }
                }
                return new Bitmap(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "anonymousAvatar.png");
            }

            /// <summary>
            /// Zformátuje velikost přidáním přípony označující velikost (nevím jak se tomu říká) a zkrácením čísla 1024 = 1 kB
            /// </summary>
            /// <param name="size"></param>
            /// <returns></returns>
            public static string FormatSize(int size)
            {
                if (size <= 0) return "n/a";

                string[] sizes = new string[] { " Bytes", " kB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB" };

                int i = (int)Math.Floor(Math.Log(size, 1024));
                if (i > sizes.Length - 1) i = sizes.Length - 1;

                return (Math.Round(size / Math.Pow(1024, i), 2) + sizes[i]);
            }
        }

        /// <summary>
        /// Kontrola uživatelovi aktivity pro automatické nastavení AFK módu
        /// </summary>
        public static class UserActivity
        {
            public static double afk_time = 5.0; //minuty

            /// <summary>
            /// Vrátí true jestliže je uživatel neaktivní.
            /// </summary>
            public static bool IsInactive
            {
                get { return (GetInactiveTime() < DateTime.Now.AddMinutes(-afk_time)); }
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct LASTINPUTINFO
            {
                public uint cbSize;
                public uint dwTime;
            }

            [DllImport("user32.dll")]
            static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

            /// <summary>
            /// Vrátí datum a čas (DateTime) poslední aktivity
            /// </summary>
            /// <returns>DateTime poslední aktivity, pokud nenalezne vrátí DateTime.Now</returns>
            public static DateTime GetInactiveTime()
            {
                LASTINPUTINFO info = new LASTINPUTINFO();
                info.cbSize = (uint)Marshal.SizeOf(info);
                if (GetLastInputInfo(ref info))
                    return DateTime.Now.Subtract(TimeSpan.FromMilliseconds(Environment.TickCount - info.dwTime));
                else
                    return DateTime.Now;
            }
        }


        /// <summary>
        /// Práce s registry -> spuštění po startu
        /// </summary>
        public static class RegistryManager
        {
            static RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
            /// <summary>
            /// Nastaví/odstraní spouštění po startu
            /// </summary>
            /// <param name="run"></param>
            public static void SetOnStartup(bool run)
            {
                if (run == true)
                    rkApp.SetValue(Global.appName, Application.ExecutablePath);
                else
                    rkApp.DeleteValue(Global.appName, false);
            }

            /// <summary>
            /// Zjistí, zda je program nastaven po spuštění. Pokud ano a změnila se cesta, tak ji aktualizuje.
            /// </summary>
            /// <returns></returns>
            public static bool IsOnStartup()
            {
                if (rkApp.GetValue(Global.appName) == null) return false;

                try
                {
                    if (!File.Exists(rkApp.GetValue(Global.appName).ToString()))
                        rkApp.SetValue(Global.appName, Application.ExecutablePath);
                }
                catch
                {
                    rkApp.SetValue(Global.appName, Application.ExecutablePath);
                }
                return true;
            }
        }


        

        public static class InternetConnection
        {
            /// <summary>
            /// Viz: http://www.devbook.cz/c-sharp-automaticka-kontrola-aktualizaci-programu
            /// </summary>
            public static void CheckForUpdates(bool Silent = true)
            {
                new Thread(new ThreadStart(() =>
                {
                    string uuPath = Global.appRunPath + "UniversalUpdater.exe";
                    if (File.Exists(uuPath))
                    {
                        Version InstalledVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
                        System.Diagnostics.Process uu = new System.Diagnostics.Process();
                        uu.StartInfo.FileName = uuPath;
                        uu.StartInfo.Arguments = " " + InstalledVersion + " http://www.sczdavos.eu/script/GetFileInfo/isim-messenger" + (Silent ? " s" : "") + " xml";
                        // spustí process a zabrání vyskočit z vlákna dokud se nezobrazí UIčko
                        if (uu.Start())
                            uu.WaitForInputIdle();//Thread.Sleep(1000);
                        else if (!Silent)
                            MessageBox.Show("Error, cannot start process.");
                    }
                    else if (!Silent)
                    {
                        MessageBox.Show("File UniversalUpdater.exe not found, try reinstalling ISIM and do not remove files!");
                    }
                })).Start();
            }
        }


        public static class LangManager
        {
            public static List<string> Languages = new List<string>() { "en-GB", "cs-CZ" };

            public static string currentCulture;
            public static System.Resources.ResourceManager strings;

            public static void SetLangFromFile()
            {
                string culture = null;
                if (File.Exists(Global.appDataFolder + "lang.ini"))
                {
                    using (StreamReader sr = new StreamReader(Global.appDataFolder + "lang.ini", Encoding.UTF8))
                    {
                        culture = sr.ReadLine();
                    }
                }
                SetLanguage(culture);
            }

            public static void SaveLangToFile(string culture)
            {
                using (StreamWriter sw = new StreamWriter(Global.appDataFolder + "lang.ini", false, Encoding.UTF8))
                {
                    sw.WriteLine(culture);
                }
            }

            public static void SetLanguage(string culture)
            {
                if (culture == "cs-CZ")
                {
                    Thread.CurrentThread.CurrentCulture = new CultureInfo("cs-CZ");
                    strings = Properties.Strings.cs_CZ.ResourceManager;
                    currentCulture = "cs-CZ";
                }
                else
                {
                    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
                    strings = Properties.Strings._default.ResourceManager;
                    currentCulture = "en-GB";
                }
            }

            public static string Translate(string name)
            {
                if (strings == null)
                    SetLanguage(null);

                return strings.GetString(name);
            }
        }

        /// <summary>
        /// posílá emaily přes google smtp, je tedy nutné míti gmail, níže návod jak si pošéfit posílání sms
        /// http://www.devbook.cz/c-sharp-posilani-sms
        /// </summary>
        public static class SmtpEmail
        {
            /// <summary>
            /// Odešle email na zadanou emailovou adresu.
            /// </summary>
            /// <param name="to">Email příjemce</param>
            /// <param name="msg_body">Zpráva</param>
            /// <returns>True pokud vše proběhne v pořádku, false pokud nastane chyba</returns>
            public static bool SendEmail(string to, string msg_body)
            {
                try
                {
                    // přihlášení se k smtp od google gmail
                    var client = new System.Net.Mail.SmtpClient("smtp.gmail.com", 587)
                    {
                        Credentials = new NetworkCredential(Login.GoogleTalk.userJid, Login.GoogleTalk.password),
                        EnableSsl = true
                    };

                    client.Send("elis@sczdavos.eu", to, "Elis from ISIM", msg_body);

                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }


    }

    /// <summary>
    /// Práce s textem
    /// </summary>
    public static class TextEdit
    {
        /// <summary>
        /// Splitne string na části dle zadání. Používá se ve foreach při odesílání velké zprávy na xmpp. Ono to umí jen do 900 nebo 1000 znaků.
        /// </summary>
        /// <param name="input">Kompletní zpráva</param>
        /// <param name="outputStringLength">Max velikost jednotlivých částí</param>
        /// <returns></returns>
        public static IEnumerable<string> SplitString(this string input, int outputStringLength)
        {
            var count = 0;
            while (count < input.Length)
            {
                var length = Math.Min(outputStringLength, input.Length - count);
                yield return string.Format("{0}", input.Substring(count, length));
                count += outputStringLength;
            }
        }

        /// <summary>
        /// Maže diakritiku, nějaký komba to replacuje blbě, ale normální české znaky pohodička. Používá se při odesílání smsky.
        /// </summary>
        /// <param name="inputString"></param>
        /// <returns></returns>
        public static string RemoveDiacritics(this string inputString)
        {
            //!\\ Warning 'œ' will be replaced with a 'o' not an 'oe'
            String normalizedString = inputString.Normalize(NormalizationForm.FormD);
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < normalizedString.Length; i++)
            {
                Char c = normalizedString[i];
                if (System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c) != System.Globalization.UnicodeCategory.NonSpacingMark)
                    stringBuilder.Append(c);
            }
            return stringBuilder.ToString();
        }

        /// <summary>
        /// Smaže neplatné znaky - používá se pro zápis do souboru, aby neobsahoval neplatné znaky
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static string RemoveInvalidCharactersFromFileName(string fileName)
        {
            if ((fileName.Length > 0) && (fileName.Substring(0, 1) == "-"))
                fileName = fileName.Substring(1);
            return Strip(fileName, "\\", "/", ":", "*", "?", "\"", "<", ">", "|");
        }

        private static string Strip(string input, params string[] removals)
        {
            foreach (string removal in removals)
            {
                input = input.Replace(removal, "");
            }

            return input;
        }

        /// <summary>
        /// ZKrátí text na danou délku
        /// </summary>
        /// <param name="text"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string ReduceText(this string text, int length)
        {
            if (text.Length > length + 3)
                return text.Substring(0, length) + "...";
            return text;
        }

        /// <summary>
        /// Smaže html tagy
        /// </summary>
        /// <param name="text">Text</param>
        /// <returns>Text bez html tagů</returns>
        public static string StripHtmlTags(string text)
        {
            return System.Text.RegularExpressions.Regex.Replace(text, "<.*?>", string.Empty);
        }

        /// <summary>
        /// Dekóduje html entity
        /// </summary>
        /// <param name="text">Text</param>
        /// <returns>Text s dekódovanýma entitama</returns>
        public static string DecodeHtmlEntities(string text)
        {
            // dekóduje základní entity
            string decodedText = System.Web.HttpUtility.HtmlDecode(text);

            // přidání speciálních entit, které to nedekóduje
            Dictionary<string, string> entityExtended = new Dictionary<string, string>();
            entityExtended.Add("&apos;", "'");

            foreach (var entity in entityExtended)
                decodedText = decodedText.Replace(entity.Key, entity.Value);

            return decodedText;
        }


        public static Dictionary<string, string> EmoticonsList = null;

        public static void InitializeEmoticons()
        {
            if (EmoticonsList == null)
            {
                EmoticonsList = new Dictionary<string, string>();

                try
                {
                    Image smile = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "smile.png");
                    Image sad = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "sad.png");
                    Image tongue = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "tongue.png");
                    Image cool = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "cool.png");
                    Image blink = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "blink.png");
                    Image cry = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "cry.png");
                    Image muck = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "muck.png");
                    Image laugh = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "laugh.png");
                    Image what = Image.FromFile(Global.appRunPath + "graphics" + Path.DirectorySeparatorChar + "what.png");

                    EmoticonsList.Add(":)", GetImageRtf(smile));
                    EmoticonsList.Add(":-)", EmoticonsList[":)"]);

                    EmoticonsList.Add(":(", GetImageRtf(sad));
                    EmoticonsList.Add(":-(", EmoticonsList[":("]);

                    EmoticonsList.Add(":P", GetImageRtf(tongue));
                    EmoticonsList.Add(":-P", EmoticonsList[":P"]);

                    EmoticonsList.Add("8-)", GetImageRtf(cool));
                    EmoticonsList.Add("B-)", EmoticonsList["8-)"]);

                    EmoticonsList.Add(";)", GetImageRtf(blink));
                    EmoticonsList.Add(";-)", EmoticonsList[";)"]);

                    EmoticonsList.Add(":'(", GetImageRtf(cry));

                    EmoticonsList.Add(":D", GetImageRtf(laugh));
                    EmoticonsList.Add(":-D", EmoticonsList[":D"]);

                    EmoticonsList.Add(":*", GetImageRtf(muck));
                    EmoticonsList.Add(":-*", EmoticonsList[":*"]);

                    EmoticonsList.Add(":O", GetImageRtf(what));
                    EmoticonsList.Add(":o", EmoticonsList[":O"]);
                    EmoticonsList.Add(":-O", EmoticonsList[":O"]);
                    EmoticonsList.Add(":-o", EmoticonsList[":O"]);
                }
                catch (FileNotFoundException)
                {
                    Console.WriteLine("cannot load smileys");
                }
            }
        }

        // Tohle se už rači nesnažim pochopit. Každopádně, než sem to zprovoznil to bylo strašný. ClipBoard nepoužívat, není Davos-Friendly.
        private enum EmfToWmfBitsFlags
        {

            // Use the default conversion
            EmfToWmfBitsFlagsDefault = 0x00000000,

            // Embedded the source of the EMF metafiel within the resulting WMF
            // metafile
            EmfToWmfBitsFlagsEmbedEmf = 0x00000001,

            // Place a 22-byte header in the resulting WMF file.  The header is
            // required for the metafile to be considered placeable.
            EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,

            // Don't simulate clipping by using the XOR operator.
            EmfToWmfBitsFlagsNoXORClip = 0x00000004
        };

        [DllImportAttribute("gdiplus.dll")]
        private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize,
            byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);

        /// <summary>
        /// Převede obrázek do jeho RTF podoby bez RTF hlavičky. Pak se vkládá pomocí Rtb.Insert
        /// </summary>
        /// <param name="image">Obrázek (asi bych tam necpal nějaký mega velký, ono je to stejně myslim převede na velikost smajlíků) a inicialozovat je pokud možno na začátku a pak už ne</param>
        /// <returns>RichTextBox RTF bez hlavičky</returns>
        public static string GetImageRtf(Image image)
        {
            // proč to dělat jednoduše? -> musí se používat clipboard to nejni upe davos friendly
            /*System.Windows.Forms.RichTextBox rtb = new System.Windows.Forms.RichTextBox();

            System.Windows.Forms.IDataObject obj = System.Windows.Forms.Clipboard.GetDataObject();
            System.Windows.Forms.Clipboard.Clear();

            System.Windows.Forms.Clipboard.SetImage(image);
            rtb.Paste();

            System.Windows.Forms.Clipboard.Clear();
            System.Windows.Forms.Clipboard.SetDataObject(obj);

            string imgRtf = rtb.Rtf;
            imgRtf = imgRtf.Remove(0, imgRtf.IndexOf(@"{\pict"));
            imgRtf = imgRtf.Replace(@"\par", string.Empty);
            imgRtf = imgRtf.Remove(imgRtf.LastIndexOf("}"));

            return imgRtf;*/

            // když to jde složitě :D
            using (System.Windows.Forms.RichTextBox rtb = new System.Windows.Forms.RichTextBox())
            {
                rtb.Font = Global.chatFont;
                StringBuilder _rtf = null;
                // Used to store the enhanced metafile
                MemoryStream _stream = null;
                // Used to create the metafile and draw the image
                Graphics _graphics = null;
                // The enhanced metafile
                Metafile _metaFile = null;
                // Handle to the device context used to create the metafile
                IntPtr _hdc;

                string hexString = "";

                try
                {
                    _rtf = new StringBuilder();
                    _stream = new MemoryStream();

                    // Get a graphics context from the RichTextBox
                    using (_graphics = rtb.CreateGraphics())
                    {

                        // Get the device context from the graphics context
                        _hdc = _graphics.GetHdc();

                        // Create a new Enhanced Metafile from the device context
                        _metaFile = new Metafile(_stream, _hdc);

                        // Release the device context
                        _graphics.ReleaseHdc(_hdc);
                    }

                    // Get a graphics context from the Enhanced Metafile
                    using (_graphics = Graphics.FromImage(_metaFile))
                    {
                        // Draw the image on the Enhanced Metafile
                        _graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height));

                    }

                    // Get the handle of the Enhanced Metafile
                    IntPtr _hEmf = _metaFile.GetHenhmetafile();

                    // A call to EmfToWmfBits with a null buffer return the size of the
                    // buffer need to store the WMF bits.  Use this to get the buffer
                    // size.
                    uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, 8, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);

                    // Create an array to hold the bits
                    byte[] _buffer = new byte[_bufferSize];

                    // A call to EmfToWmfBits with a valid buffer copies the bits into the
                    // buffer an returns the number of bits in the WMF.  
                    uint _convertedSize = GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, 8, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);

                    // Append the bits to the RTF string
                    for (int i = 0; i < _buffer.Length; ++i)
                    {
                        _rtf.Append(String.Format("{0:X2}", _buffer[i]));
                    }

                    hexString = _rtf.ToString();
                }
                finally
                {
                    if (_graphics != null)
                        _graphics.Dispose();
                    if (_metaFile != null)
                        _metaFile.Dispose();
                    if (_stream != null)
                        _stream.Close();
                }

                string mpic = @"{\pict\wmetafile8\picw396\pich396\picwgoal225\pichgoal225 ";
                mpic += hexString;
                mpic += "}";

                return mpic;
            }
        }


        public static string CloseTags(string message)
        {
            // uzavře případné neuzavřené color elementy
            for (int i = 1; i <= 3; i++)
            {
                while ((message.Split(new string[] { "[color:" + i + "]" }, StringSplitOptions.None).Length > message.Split(new string[] { "[/color:" + i + "]" }, StringSplitOptions.None).Length))
                    message += "[/color:" + i + "]";
            }

            return message;
        }


    }


    /// <summary>
    /// LockDead safe Lock. S timoutem. Pokud je lock pužíván a nepodaří se do 500vteřin provést tak to skončí prostě :D Vykopal sem  toho timout exception abych to nemusel všude ošetřovat
    /// </summary>
    public class Safe : IDisposable
    {
        private readonly object[] _padlocks;
        private readonly bool[] _securedFlags;

        private Safe(object padlock, int milliSecondTimeout)
        {
            _padlocks = new[] { padlock };
            _securedFlags = new[] { Monitor.TryEnter(padlock, milliSecondTimeout) };
        }

        private Safe(object[] padlocks, int milliSecondTimeout)
        {
            _padlocks = padlocks;
            _securedFlags = new bool[_padlocks.Length];
            for (int i = 0; i < _padlocks.Length; i++)
                _securedFlags[i] = Monitor.TryEnter(padlocks[i], milliSecondTimeout);
        }

        public bool Secured
        {
            get { return _securedFlags.All(s => s); }
        }

        public static void Lock(object[] padlocks, int millisecondTimeout, Action codeToRun)
        {
            using (var bolt = new Safe(padlocks, millisecondTimeout))
                if (bolt.Secured)
                    codeToRun();
                else
                    throw new TimeoutException(string.Format("Safe.Lock wasn't able to acquire a lock in {0}ms",
                                                             millisecondTimeout));
        }

        public static void Lock(object padlock, int millisecondTimeout, Action codeToRun)
        {
            using (var bolt = new Safe(padlock, millisecondTimeout))
                if (bolt.Secured)
                    codeToRun();
            /*else
                throw new TimeoutException(string.Format("Safe.Lock wasn't able to acquire a lock in {0}ms",
                                                         millisecondTimeout));*/
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            for (int i = 0; i < _securedFlags.Length; i++)
                if (_securedFlags[i])
                {
                    Monitor.Exit(_padlocks[i]);
                    _securedFlags[i] = false;
                }
        }

        #endregion
    }


    /// <summary>
    /// Šifrace/dešifrace používá se hlavně u loginu
    /// </summary>
    public class Crypto
    {
        public static readonly string defaultSalt = "ISIM-980187eb-0922-4d8a-af80-4ee007580c11";

        private static byte[] _salt = Encoding.ASCII.GetBytes("o6806642kbM7c5");

        public static string QuickEncrypt(string plainText)
        {
            return EncryptStringAES(plainText, defaultSalt);
        }

        public static string QuickDecrypt(string cipherText)
        {
            return DecryptStringAES(cipherText, defaultSalt);
        }

        /// <summary>
        /// Encrypt the given string using AES.  The string can be decrypted using 
        /// DecryptStringAES().  The sharedSecret parameters must match.
        /// </summary>
        /// <param name="plainText">The text to encrypt.</param>
        /// <param name="sharedSecret">A password used to generate a key for encryption.</param>
        public static string EncryptStringAES(string plainText, string sharedSecret)
        {
            if (string.IsNullOrEmpty(plainText))
                throw new ArgumentNullException("plainText");
            if (string.IsNullOrEmpty(sharedSecret))
                throw new ArgumentNullException("sharedSecret");

            string outStr = null;                       // Encrypted string to return
            RijndaelManaged aesAlg = null;              // RijndaelManaged object used to encrypt the data.

            try
            {
                // generate the key from the shared secret and the salt
                Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

                // Create a RijndaelManaged object
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    // prepend the IV
                    msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                    msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                    }
                    outStr = Convert.ToBase64String(msEncrypt.ToArray());
                }
            }
            finally
            {
                // Clear the RijndaelManaged object.
                if (aesAlg != null)
                    aesAlg.Clear();
            }

            // Return the encrypted bytes from the memory stream.
            return outStr;
        }

        /// <summary>
        /// Decrypt the given string.  Assumes the string was encrypted using 
        /// EncryptStringAES(), using an identical sharedSecret.
        /// </summary>
        /// <param name="cipherText">The text to decrypt.</param>
        /// <param name="sharedSecret">A password used to generate a key for decryption.</param>
        public static string DecryptStringAES(string cipherText, string sharedSecret)
        {
            if (string.IsNullOrEmpty(cipherText))
                throw new ArgumentNullException("cipherText");
            if (string.IsNullOrEmpty(sharedSecret))
                throw new ArgumentNullException("sharedSecret");

            // Declare the RijndaelManaged object
            // used to decrypt the data.
            RijndaelManaged aesAlg = null;

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            try
            {
                // generate the key from the shared secret and the salt
                Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

                // Create the streams used for decryption.                
                byte[] bytes = Convert.FromBase64String(cipherText);
                using (MemoryStream msDecrypt = new MemoryStream(bytes))
                {
                    // Create a RijndaelManaged object
                    // with the specified key and IV.
                    aesAlg = new RijndaelManaged();
                    aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                    // Get the initialization vector from the encrypted stream
                    aesAlg.IV = ReadByteArray(msDecrypt);
                    // Create a decrytor to perform the stream transform.
                    ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
            finally
            {
                // Clear the RijndaelManaged object.
                if (aesAlg != null)
                    aesAlg.Clear();
            }

            return plaintext;
        }

        private static byte[] ReadByteArray(Stream s)
        {
            byte[] rawLength = new byte[sizeof(int)];
            if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
            {
                throw new SystemException("Stream did not contain properly formatted byte array");
            }

            byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
            if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
            {
                throw new SystemException("Did not read byte array properly");
            }

            return buffer;
        }
    }

    /// <summary>
    /// Blikání okna. Příchozí zpráva, neaktivní kovnerzace. Indikace v taskbaru.
    /// </summary>
    public static class FlashWindow
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
        [StructLayout(LayoutKind.Sequential)]
        private struct FLASHWINFO
        {
            /// <summary>
            /// The size of the structure in bytes.
            /// </summary>
            public uint cbSize;
            /// <summary>
            /// A Handle to the Window to be Flashed. The window can be either opened or minimized.
            /// </summary>
            public IntPtr hwnd;
            /// <summary>
            /// The Flash Status.
            /// </summary>
            public uint dwFlags;
            /// <summary>
            /// The number of times to Flash the window.
            /// </summary>
            public uint uCount;
            /// <summary>
            /// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink rate.
            /// </summary>
            public uint dwTimeout;
        }
        /// <summary>
        /// Stop flashing. The system restores the window to its original stae.
        /// </summary>
        public const uint FLASHW_STOP = 0;

        /// <summary>
        /// Flash the window caption.
        /// </summary>
        public const uint FLASHW_CAPTION = 1;

        /// <summary>
        /// Flash the taskbar button.
        /// </summary>
        public const uint FLASHW_TRAY = 2;

        /// <summary>
        /// Flash both the window caption and taskbar button.
        /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
        /// </summary>
        public const uint FLASHW_ALL = 3;
        /// <summary>
        /// Flash continuously, until the FLASHW_STOP flag is set.
        /// </summary>
        public const uint FLASHW_TIMER = 4;
        /// <summary>
        /// Flash continuously until the window comes to the foreground.
        /// </summary>
        public const uint FLASHW_TIMERNOFG = 12;

        /// <summary>
        /// Flash the spacified Window (Form) until it recieves focus.
        /// </summary>
        /// <param name="form">The Form (Window) to Flash.</param>
        /// <returns></returns>
        public static bool Flash(System.Windows.Forms.Form form)
        {
            // Make sure we're running under Windows 2000 or later
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }
        private static FLASHWINFO Create_FLASHWINFO(IntPtr handle, uint flags, uint count, uint timeout)
        {
            FLASHWINFO fi = new FLASHWINFO();
            fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
            fi.hwnd = handle;
            fi.dwFlags = flags;
            fi.uCount = count;
            fi.dwTimeout = timeout;
            return fi;
        }
        /// <summary>
        /// Flash the specified Window (form) for the specified number of times
        /// </summary>
        /// <param name="form">The Form (Window) to Flash.</param>
        /// <param name="count">The number of times to Flash.</param>
        /// <returns></returns>
        public static bool Flash(System.Windows.Forms.Form form, uint count)
        {
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, count, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }
        /// <summary>
        /// Start Flashing the specified Window (form)
        /// </summary>
        /// <param name="form">The Form (Window) to Flash.</param>
        /// <returns></returns>
        public static bool Start(System.Windows.Forms.Form form)
        {
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, uint.MaxValue, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }
        /// <summary>
        /// Stop Flashing the specified Window (form)
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        public static bool Stop(System.Windows.Forms.Form form)
        {
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_STOP, uint.MaxValue, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }
        /// <summary>
        /// A boolean value indicating whether the application is running on Windows 2000 or later.
        /// </summary>
        private static bool Win2000OrLater
        {
            get { return System.Environment.OSVersion.Version.Major >= 5; }
        }
    }
}
