using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;


namespace EduNetworkBuilder
{
    /// <summary>
    /// This is a whole network.  LAN, WAN, Internet; everything combined
    /// </summary>
    public class Network
    {
        public string PuzzleName="";
        public int myHeight = 1024;
        public int myWidth = 1024;
        public int Level = 0;
        public double SortOrder = 0;
        public bool ShowLabels = false;
        public LanguageStrings NetMessage;
        public LanguageStrings NetTitle;
        List<NetworkComponent> NetComponents = new List<NetworkComponent>();
        //should have background image
        Image TheNetImage = new Bitmap(1024, 1024);
        public int itemsize = 100; //The size of network components
        PictureBox myPBox=null;
        private int UniqueIdentifier = 100; //This gets used for all sorts of things.  is auto-incremented every time someone asks for one
        private List<Packet> myPackets = new List<Packet>();
        private List<PacketMessage> myMessages = new List<PacketMessage>();
        private bool _isDirty = false;
        private IPAddress lastAddress = new IPAddress("0.0.0.0/24");
        public string NetworkFilename = "";
        public List<NetTest> NetTests = new List<NetTest>();
        private bool AlreadyDisplayedMessage = false;
        public NetTestVerbosity HintsToDisplay = NetTestVerbosity.none;
        public NetTestVerbosity StartingHelpLevel = NetTestVerbosity.none;
        public bool PuzzleIsSolved = true;  //only set to false if we load a puzzle
        public List<HelpTopics> SuggestedReadings = new List<HelpTopics>();
        private DateTime NetworkStartTime = DateTime.Now;
        private bool AlreadyChosenTimeout = false;
        private int DefaultTimeout = 10;
        private int NumberOfSecondsForTimeout = 10;

        public Network(string Name)
        {
            TheNetImage = new Bitmap(myWidth, myHeight);
            PuzzleName = Name;
            NetMessage = new LanguageStrings("message");
            NetTitle = new LanguageStrings("title");
        }

        private bool isDirty()
        {
            if (_isDirty) return true;
            foreach (NetworkComponent nc in NetComponents)
            {
                if (nc.IsDirty) return true;
            }
            return false;
        }


        public void ClearComponents()
        {
            NetComponents.Clear();
        }
        public void ClearPackets()
        {   
            myPackets.Clear();
        }
        public void ClearMessages()
        {
            myMessages.Clear();
        }

        /// <summary>
        /// Load the file from a xml resource
        /// </summary>
        public void Load()
        {
            Load(@"C:\Users\tyoung\Desktop\Test.enbx");
        }

        public void Load(string filename)
        {
            NetworkFilename = filename;
            XmlDocument xmlDoc = new XmlDocument();
            PuzzleName = Path.GetFileNameWithoutExtension(filename);
            if (File.Exists(filename))
            {
                xmlDoc.Load(filename);
                Load(xmlDoc,PuzzleName);
            }
        }

        public void Load(XmlNode TheNode, string Name)
        {
            _isDirty = true;
            NetworkDevice newND;
            NetworkLink newNL;
            int newUnique=-1;
            PuzzleName = Name;
            foreach (XmlNode Individual in TheNode.ChildNodes)
            {
                XmlNodeType myNodetype = Individual.NodeType;
                if (myNodetype == XmlNodeType.Element)
                {
                    switch (Individual.Name.ToLower())
                    {
                        case "edunetworkbuilder":
                        case "network":
                            Load(Individual,PuzzleName);
                            break;
                        case "showlabels":
                            bool.TryParse(Individual.InnerText, out ShowLabels);
                            break;
                        case "itemsize":
                            int.TryParse(Individual.InnerText, out itemsize);
                            break;
                        case "height":
                            int.TryParse(Individual.InnerText, out myHeight);
                            break;
                        case "width":
                            int.TryParse(Individual.InnerText, out myWidth);
                            break;
                        case "uniqueidentifier":
                            int.TryParse(Individual.InnerText, out UniqueIdentifier);
                            newUnique = UniqueIdentifier;
                            break;
                        case "link":
                            newNL = new NetworkLink(Individual);
                            NetComponents.Add(newNL);
                            break;
                        case "device":
                            newND = new NetworkDevice(Individual);
                            NetComponents.Add(newND);
                            break;
                        case "nettest":
                            NetTest nt = new NetTest(Individual);
                            NetTests.Add(nt);
                            break;
                        case "tag":
                            HelpTopics tempHelpTopic = NB.TryParseEnum<HelpTopics>(Individual.InnerText, HelpTopics.None);
                            if (tempHelpTopic != HelpTopics.None)
                            {
                                SuggestedReadings.Add(tempHelpTopic);
                            }
                            break;
                        case "level":
                            int.TryParse(Individual.InnerText, out Level);
                            //Tags.Add("Level_" + Individual.InnerText);
                            break;
                        case "sortorder":
                            double.TryParse(Individual.InnerText, out SortOrder);
                            break;
                        case "startinghelplevel":
                           StartingHelpLevel = NB.ParseEnum<NetTestVerbosity>(Individual.InnerText);
                           HintsToDisplay = StartingHelpLevel;
                            break;
                        default:
                            if(Regex.IsMatch(Individual.Name.ToLower(),"message"))
                            {
                                NetMessage.Add(Individual);
                            }
                            else
                                if (Regex.IsMatch(Individual.Name.ToLower(), "title"))
                                {
                                    NetTitle.Add(Individual);
                                }

                            break;
                    }
                }
                DoAllVerifyLinks();
                DoAllAutoJoin();
            }
            if (NetMessage.GetText() != "" && !AlreadyDisplayedMessage)
            {
                //We have a message loaded on this network.  Display it
                BuilderWindow myWin = (BuilderWindow)Application.OpenForms["BuilderWindow"];
                if (myWin != null)
                {
                    myWin.OpenNetHelpWindow();
                }
                else
                {
                    MessageBox.Show(NetMessage.GetText(), NetTitle.GetText(), MessageBoxButtons.OK);
                }
                AlreadyDisplayedMessage = true;
            }
            if(NetTests.Count > 0)
                PuzzleIsSolved = false; //When we load the puzzle.
                
            if (newUnique != -1)
                UniqueIdentifier = newUnique;
        }


        public void Save()
        {
            Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Test.enbx"));
        }

        public void Save(string filename)
        {
            NetworkFilename = filename;
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.NewLineOnAttributes = true;
            XmlWriter writer = XmlWriter.Create(filename, settings);

            //Now we write the file:
            writer.WriteStartDocument();
            writer.WriteStartElement("EduNetworkBuilder");
            writer.WriteComment("This is a network file for EduNetworkBuilder.");
            Save(writer);
            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Flush();
            writer.Close();
        }


        public void Save(XmlWriter writer)
        {
            //Save the language name
            //save the number of items
            //Save all the items
            writer.WriteStartElement("Network");
            NetMessage.Save(writer);
            NetTitle.Save(writer);
            writer.WriteElementString("height", myHeight.ToString());
            writer.WriteElementString("width", myWidth.ToString());
            writer.WriteElementString("itemsize", itemsize.ToString());
            writer.WriteElementString("showlabels", ShowLabels.ToString());
            writer.WriteElementString("level", Level.ToString());
            writer.WriteElementString("sortorder", SortOrder.ToString());
            writer.WriteElementString("uniqueidentifier", UniqueIdentifier.ToString());
            writer.WriteElementString("startinghelplevel", StartingHelpLevel.ToString());
            //Save all the devices
            for (int loop = 0; loop < NetComponents.Count; loop++)
            {
                if (NB.GetComponentType(NetComponents[loop]) == GeneralComponentType.device)
                NetComponents[loop].Save(writer);
            }
            //Then save the links
            for (int loop = 0; loop < NetComponents.Count; loop++)
            {
                if (NB.GetComponentType(NetComponents[loop]) == GeneralComponentType.link)
                    NetComponents[loop].Save(writer);
            }
            foreach(NetTest nt in NetTests)
            {
                nt.Save(writer);
            }
            foreach(HelpTopics HT in SuggestedReadings)
            {
                writer.WriteElementString("tag",HT.ToString());
            }
            writer.WriteEndElement();
        }

        public void UpdateDeviceSizes()
        {
            NetworkDevice nd;
            foreach (NetworkComponent NC in NetComponents)
            {
                if (NB.GetComponentType(NC) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)NC;
                    nd.SetSize(itemsize);
                }
            }
        }

        public bool MAC_Exists(string MAC)
        {
            foreach (NetworkComponent nc in NetComponents)
            {
                if (nc.HasMac(MAC)) return true;
            }
            return false;
        }

        public void RegisterDisplayArea(PictureBox What)
        {
            myPBox = What;
        }

        public Point clickedPos(Point pixelClickedOn)
        {
            if (myPBox == null) return new Point(-1, -1);
            double deltaX = (double)TheNetImage.Width / myPBox.Width;
            double deltaY = (double)TheNetImage.Height / myPBox.Height;
            Point Dest = new Point((int)(pixelClickedOn.X * deltaX), (int)(pixelClickedOn.Y * deltaY));
            if (Dest.X > TheNetImage.Width) Dest = new Point(TheNetImage.Width, Dest.Y);
            if (Dest.Y > TheNetImage.Height) Dest = new Point(Dest.X, TheNetImage.Height);
            if (Dest.X <0) Dest = new Point(0, Dest.Y);
            if (Dest.Y <0) Dest = new Point(Dest.X, 0);

            return Dest;
        }

        public Point clickedPosCentered(Point pixelClickedOn)
        {
            Point NetPoint = clickedPos(pixelClickedOn);
            int shift = (itemsize / 2);
            Point Dest = new Point((int)(NetPoint.X - shift), (int)(NetPoint.Y - shift));
            if (Dest.X + itemsize > TheNetImage.Width) Dest = new Point(TheNetImage.Width - itemsize, Dest.Y);
            if (Dest.Y + itemsize > TheNetImage.Height) Dest = new Point(Dest.X, TheNetImage.Height - itemsize);
            if (Dest.X < 0) Dest = new Point(0, Dest.Y);
            if (Dest.Y < 0) Dest = new Point(Dest.X, 0);
            return Dest;
        }

        public NetworkDevice ItemAtPosition(Point NetworkLocation)
        {
            NetworkDevice tDevice;
            foreach (NetworkComponent tItem in NetComponents)
            {
                if (tItem.GetType().ToString() == "EduNetworkBuilder.NetworkDevice")
                {
                    tDevice = (NetworkDevice)tItem;
                    if (tDevice.AtLocation(NetworkLocation)) 
                        return tDevice;
                }
            }
            return null;
        }

        public NetworkDevice ItemFromName(string hostname)
        {
            NetworkDevice tDevice;
            foreach (NetworkComponent tItem in NetComponents)
            {
                if (tItem.GetType().ToString() == "EduNetworkBuilder.NetworkDevice")
                {
                    tDevice = (NetworkDevice)tItem;
                    if (tDevice.hostname == hostname)
                        return tDevice;
                }
            }
            return null;
        }

        public bool HasItemCalled(string itemname)
        {
            NetworkDevice tDevice;
            foreach(NetworkComponent tItem in NetComponents)
            {
                if(tItem.GetType().ToString() == "EduNetworkBuilder.NetworkDevice")
                {
                    tDevice = (NetworkDevice)tItem;
                    if (tDevice.hostname == itemname) return true;
                }
            }
            return false;
        }

        

        public NetworkComponent AddItem(NetworkComponentType WhatType, Point location)
        {
            if (WhatType == NetworkComponentType.none) return null; //Do not put none in
            if (WhatType == NetworkComponentType.link) return null; //We need to add links another way
            string basename = WhatType.ToString();
            int count=0;
            while (HasItemCalled(basename + count))
                count++;

            Point newlocation = NB.GetSnapped(location);

            NetworkComponent NewItem = new NetworkDevice(WhatType, basename + count, newlocation);
            ((NetworkDevice)NewItem).SetSize(itemsize);

            NetComponents.Add(NewItem);
            TestForCompletion(true);
            myPBox.Invalidate(); //redraw the screen
            return NewItem;
        }

        /// <summary>
        /// Search throuh all the network components and delete any links that are attacked to the specified nic.
        /// </summary>
        /// <param name="NicStr"></param>
        public void RemoveLinksToNic(HostNicID NicID)
        {
            for(int looper=NetComponents.Count()-1; looper >=0; looper--)
            {
                if(NetComponents[looper].GetType().ToString() == "EduNetworkBuilder.NetworkLink")
                {
                    NetworkLink nLink = (NetworkLink)NetComponents[looper];
                    if (nLink.HasLink(NicID))
                    {
                        nLink.Destroy();
                        NetComponents.RemoveAt(looper);
                        _isDirty = true;
                    }
                }
            }
        }

        public NetworkComponent AddItem(NetworkComponent ToAdd)
        {
            NetComponents.Add(ToAdd);
            TestForCompletion(true);
            return ToAdd;
        }
        public List<string> UnavailableNics()
        {
            NetworkLink myLink;
            List<string> usedList = new List<string>();
            foreach (NetworkComponent NC in NetComponents)
            {
                if (NC.GetType().ToString() == "EduNetworkBuilder.NetworkLink")
                {
                    myLink = (NetworkLink)NC;
                    usedList.AddRange(myLink.UsedNicIDStrings());
                }
            }
            return usedList;
        }

        public void RemoveComponent(NetworkComponent tItem)
        {
            NetComponents.Remove(tItem);
            tItem.Destroy();
            _isDirty = true;
        }

        public void StoreLastIP(IPAddress ip)
        {
            lastAddress = ip;
        }

        public IPAddress RetrieveLastIP()
        {
            return lastAddress;
        }

        public List<string> GetTestMessages(string host)
        {
            string tString;
            List<string> tMessages = new List<string>();
            foreach(NetTest nt in NetTests)
            {
                if(nt.sHost == host && !nt.TestComplete())
                {
                    tString = nt.GetDescription(HintsToDisplay);
                    if(tString != "")
                        tMessages.Add(tString);
                }
            }
            return tMessages;
        }
        public List<string> GetIncompleteTestDestinations(string Source, bool forPing=true)
        {
            List<string> tDests = new List<string>();
            foreach (NetTest nt in NetTests)
            {
                if (nt.sHost == Source && !nt.TestComplete())
                {
                    if (forPing && (nt.TheTest == NetTestType.FailedPing || nt.TheTest == NetTestType.SuccessfullyPings))
                        tDests.Add(nt.dHost);
                    if (!forPing && nt.TheTest == NetTestType.SuccessfullyArps)
                        tDests.Add(nt.dHost);
                }
            }
            return tDests;
        }

        public void TestForCompletion(bool report_as_done)
        {
            NetworkDevice TmpDevice;
            bool PreviouslyUnsolved = !PuzzleIsSolved;  //Only if we have an unsolved puzzle
            int PuzzleCount = 0;

            foreach (NetworkComponent nc in NetComponents)
            {
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    TmpDevice = (NetworkDevice)nc;
                    if (TmpDevice.BackgroundColor != Color.Empty)
                    {
                        TmpDevice.BackgroundColor = Color.Empty;
                        TmpDevice.IsDirty = true;
                    }
                }
            }
            foreach (NetTest nt in NetTests)
            {
                if (nt.ColorItemsIfNeeded(HintsToDisplay != NetTestVerbosity.none)) //only change the color if we are not "none"
                    PuzzleCount++;
            }
            if (report_as_done && PuzzleCount == 0 && PreviouslyUnsolved)
            {
                //The puzzle was just solved
                MarkAsSolved();
            }
        }

        public void Print(PaintEventArgs e)
        {
            //we have the whole thing to print, and the display image already done
            TestForCompletion(false);
            if(isDirty() || _isDirty)
            {
                TheNetImage= new Bitmap(TheNetImage.Width, TheNetImage.Height);
                //re-generate the image
                //Do all the links first
                foreach(NetworkComponent NC in NetComponents)
                {
                    if (NC.GetType().ToString() == "EduNetworkBuilder.NetworkLink")
                        NC.Print(TheNetImage, false);
                }
                //Now, do all the devices
                foreach (NetworkComponent NC in NetComponents)
                {
                    if (NC.GetType().ToString() == "EduNetworkBuilder.NetworkDevice")
                        NC.Print(TheNetImage, ShowLabels);
                }
            }
            if (myPackets.Count > 0)
            {
                Image tempNetImage = new Bitmap(TheNetImage);
                foreach(Packet pkt in myPackets)
                {
                    pkt.Print(tempNetImage);  //Print all the packets over the network image
                }
                e.Graphics.DrawImage(tempNetImage, 0, 0, myPBox.Width, myPBox.Height);
                tempNetImage.Dispose();
            }
            else
            {
                e.Graphics.DrawImage(TheNetImage, 0, 0, myPBox.Width, myPBox.Height);
            }
            _isDirty = false;
        }

        public void InvalidateEverything()
        {
            foreach(NetworkComponent nc in NetComponents)
            {
                nc.IsDirty = true;
            }
        }

        public List<string> arp(UInt32 IP)
        {
            List<string> arps = new List<string>();
            List<string> tlist;
            foreach (NetworkComponent nc in NB.Randomize(NetComponents))
            {
                tlist = nc.arp(IP);
                if (tlist.Count() > 0)
                {
                    foreach (string mac in tlist)
                    {
                        arps.Add(mac);
                    }
                }
            }
            return arps;
        }
        public int GetUniqueIdentifier()
        {
            return UniqueIdentifier++;
        }

        public void SortNetComponents()
        {
            NetComponents.Sort((x, y) => x.hostname.CompareTo(y.hostname));
        }

        public NetworkDevice HostMatchingHostNicID(HostNicID ToFind)
        {
            NetworkDevice tDevice;
            //We cheat.  the first portion of the host/nic ID is the host_id so we just find that
            foreach (NetworkComponent nc in NetComponents)
            {
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    if (nc.GetUniqueIdentifier == ToFind.HostID)
                    {
                        tDevice = (NetworkDevice)nc;
                        return tDevice;
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// Search all the devices and make sure the one device is the only one that has the specified IP
        /// </summary>
        /// <param name="ToFind">The IP address to find</param>
        /// <param name="Source">The device that has it</param>
        /// <returns></returns>
        public bool HasUniqueIP(IPAddress ToFind, NetworkDevice Source)
        {
            NetworkDevice ND;
            foreach (NetworkComponent nc in NetComponents)
            {
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    ND = (NetworkDevice)nc;
                    if(ND != Source) //Skip the source
                    {
                        if (ND.HasIPAddress(ToFind))
                            return false; //Something has that IP
                    }
                }
            }
            return true; //We did not find any device with that IP
        }

        public bool ItemIsCritical(string host)
        {
            foreach(NetTest nt in NetTests)
            {
                if (nt.dHost == host) return true;
                if (nt.sHost == host) return true;
            }
            return false;
        }

        public bool ItemIsCritical(HostNicID host)
        {
            NetworkDevice ND = GetDeviceFromID(host);
            if (ND == null) return false; //cannot find it
            foreach (NetTest nt in NetTests)
            {
                if (nt.dHost == ND.hostname) return true;
                if (nt.sHost == ND.hostname) return true;
            }
            return false;
        }

        public bool ItemIsLocked(string host, string dest, NetTestType WhatToCheck)
        {
            foreach (NetTest nt in NetTests)
            {
                if(nt.sHost == host)
                {
                    if (nt.TheTest == NetTestType.LockAll) 
                        return true;
                    if (WhatToCheck == nt.TheTest && dest == nt.dHost)
                        return true;
                }
            }
            return false;
        }

        private void MarkAsSolved()
        {
            PuzzleIsSolved = true;
            if (PuzzleName != "" && PuzzleName != null)
            {
                if (Properties.Settings.Default.ScoreList == null)
                    Properties.Settings.Default.ScoreList = new System.Collections.Specialized.StringCollection();
                if (!Properties.Settings.Default.ScoreList.Contains(PuzzleName))
                {
                    Properties.Settings.Default.ScoreList.Add(PuzzleName);
                    Properties.Settings.Default.Save();
                }
            }
            DialogResult answer = MessageBox.Show("Solved!  Would you like to open the puzzle box to go to the next puzzle? 'No' will let you continue to play with this one.","Solved",MessageBoxButtons.YesNo);
            if(answer == DialogResult.Yes)
            {
                ListBoxWindow LBW = new ListBoxWindow();
                LBW.ShowDialog();
            }
        }

        /// <summary>
        /// See if we have any tests that are supposed to check for packet arrival.
        /// </summary>
        /// <param name="packet_type">The type of packet that arrived</param>
        /// <param name="sHost">The host it originated from</param>
        /// <param name="dHost">The machine it went to</param>
        public void NotePacketArrived(PacketType packet_type, NetworkDevice source, IPAddress sIP, IPAddress dIP)
        {
            string sHost = ReverseDNSLookup(source, sIP);
            string dHost = ReverseDNSLookup(source, dIP);
            foreach (NetTest nt in NetTests)
            {
                if (nt.TheTest == NetTestType.SuccessfullyArps && packet_type == PacketType.arp_answer && sHost == nt.sHost && dHost == nt.dHost)
                    nt.SetDone();
                if (nt.TheTest == NetTestType.SuccessfullyDHCPs && packet_type == PacketType.dhcp_answer && sHost == nt.sHost && dHost == nt.dHost)
                    nt.SetDone();
                if (nt.TheTest == NetTestType.SuccessfullyPings && packet_type == PacketType.ping_answer && sHost == nt.sHost && dHost == nt.dHost)
                    nt.SetDone();
                if (nt.TheTest == NetTestType.SuccessfullyPings && packet_type == PacketType.ping_answer && sHost == nt.sHost && dHost == null && dIP != null && dIP.BroadcastAddress == dIP.GetIP && dIP.GetIPString == nt.dHost)
                    nt.SetDone();
            }
        }

        public bool NoteActionDone(NetTestType theTest, string sHost, string dHost)
        {
            bool OldVal = false;
            IPAddress sourceIP;
            string sourceIPstring;
            IPAddress destIP;
            string destIPstring;
            foreach (NetTest nt in NetTests)
            {
                sourceIP = new IPAddress(sHost);
                sourceIPstring = ReverseDNSLookup(null, sourceIP); //this will either be an ip address or the host name
                destIP = new IPAddress(dHost);
                destIPstring = ReverseDNSLookup(null, destIP); //this will either be an ip address or the host name             
                if ((nt.TheTest == NetTestType.HelpRequest || nt.TheTest == NetTestType.FailedPing || nt.TheTest == NetTestType.ReadContextHelp) && 
                    (sHost == nt.sHost || sourceIPstring == nt.sHost) && 
                    (dHost == nt.dHost || destIPstring == nt.dHost))
                {
                    OldVal = nt.TaskWasDone;
                    nt.SetDone();
                    if (nt.TaskWasDone != OldVal)
                        return true;
                }
                if (nt.TheTest == NetTestType.HelpRequest && sHost == "" && dHost == nt.dHost && dHost == "?Button")
                {
                    OldVal = nt.TaskWasDone;
                    nt.SetDone();
                    if (nt.TaskWasDone != OldVal)
                        return true;
                }

            }
            return false;
        }

        public List<string> NetworkCardForHostList(string hostname, bool OnlyUnused = true, bool OnlyLinkable = false, NicType fromNIC = NicType.none)
        {
            List<string> theList = new List<string>();
            NetworkDevice tDevice;
            foreach (NetworkComponent nc in NetComponents)
            {
                if (nc.GetType().ToString() == "EduNetworkBuilder.NetworkDevice")
                {
                    if(nc.hostname == hostname)
                    {
                        tDevice = (NetworkDevice)nc;
                        theList.AddRange(tDevice.NetworkCardStrings(OnlyUnused,OnlyLinkable, fromNIC));
                    }
                }
            }
            return theList;
        }
    
        public List<string> GetMessageStrings()
        {
            List<string> themessages = new List<string>();
            foreach (PacketMessage msg in myMessages)
            {
                themessages.AddRange(msg.GetMessagesSummary());
            }
            return themessages;
        }
        public int CountMessages()
        {
            return myMessages.Count();
        }
        public PacketMessage GetMessageAtIndex(int index)
        {
            if (index < 0 || index > myMessages.Count()) return null;
            return myMessages[index];
        }

        public void addPacket(Packet toadd)
        {
            if(toadd != null && !myPackets.Contains(toadd))
                myPackets.Add(toadd);
        }

        public int CountPackets(PacketType WhatType)
        {
            int count = 0;
            foreach (Packet pkt in myPackets)
            {
                if (pkt.MyType == WhatType)
                    count++;
            }
            return count;
        }

        public void ProcessPacketsOnce()
        {
            foreach(Packet tpackets in myPackets.ToList())
            {
                if(tpackets != null)
                    tpackets.ProcessTick();
                if (tpackets.DebugOn)
                    Console.WriteLine("Debugging");
            }
            for(int loop=myPackets.Count -1; loop >=0; loop--)
            {
                //we delete if it has finished.
                if (myPackets[loop].DebugOn)
                    Console.WriteLine("Debugging");
                if (myPackets[loop].TickTTL < 1)  //If the packet has been around too long, get rid of it
                {
                    myPackets[loop].AddMessage(DebugLevel.info, "Packet Tick Counter reached.  Did the packet go into an infinite loop?");
                    myPackets[loop].PrepareToDelete();
                    myPackets.RemoveAt(loop);
                } 
                else if (myPackets[loop].ready_to_delete)
                {
                    myPackets[loop].PrepareToDelete();
                    myPackets.RemoveAt(loop);
                }
            }
            GC.Collect();//Try to clean up memory.
            myPackets = NB.Randomize(myPackets);
        }

        /// <summary>
        /// This checks to see if there is any reason we should stop processing the packets
        /// </summary>
        /// <returns>true if we should continue, false if we should pause</returns>
        public bool ProcessingShouldContinue()
        {
            TimeSpan Duration = DateTime.Now - NetworkStartTime;
            NB.SetProgress(Duration.TotalSeconds, NumberOfSecondsForTimeout);
            if (Duration.TotalSeconds > NumberOfSecondsForTimeout)
            {
                Console.WriteLine("Total seconds = " + Duration.TotalSeconds.ToString() + "  Stopping.");
                foreach (Packet pkt in myPackets)
                {
                    pkt.Tracking.AddMessage(DebugLevel.packet, "Network", "The packet timed out.");
                    pkt.Tracking.Status = "The packet timed out and was dropped.";
                    pkt.MyStatus = PacketStatus.finished_failed;
                    pkt.PrepareToDelete();
                }
                ClearPackets();
                return false;
            }
            return true;
        }

        public void RegisterTimeOfArrival()
        {
            TimeSpan Duration = DateTime.Now - NetworkStartTime;
            int MaxTime = (int)Duration.TotalSeconds + 2;
            if(!AlreadyChosenTimeout)
            {
                NumberOfSecondsForTimeout = MaxTime * 2;
                AlreadyChosenTimeout = true;
            }
        }

        public void ProcessPackets()
        {
            AlreadyChosenTimeout = false; //we do this at the beginning of processing
            NumberOfSecondsForTimeout = DefaultTimeout;
            NetworkStartTime = DateTime.Now;
            while(myPackets.Count > 0)
            {
                ProcessPacketsOnce();
                if (!ProcessingShouldContinue())
                {
                    
                    //we need to break out for some reason
                    myPBox.Refresh();
                    break;
                }
                else 
                {
                    myPBox.Refresh();
                    NB.Delay(5);
                }
            }
            //remove any IP connection tracking info
            NetworkDevice ND;
            foreach(NetworkComponent nc in NetComponents)
            {
                if(NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    ND = (NetworkDevice)nc;
                    ND.ClearIPConnectionInfo();
                }
            }
            DebugPausePoint WhatIsSet = NB.GetDebugPauseSetting();
            if(WhatIsSet != 0)
            {
                Console.WriteLine("----All Done----");
            }
            TestForCompletion(true); //Now, report on the progress if we solved something in the middle of the packets going out
            AlreadyChosenTimeout = false;
            NB.SetProgress(0, NumberOfSecondsForTimeout);
        }

        public void AddMessage(PacketMessage toAdd)
        {
            //Only add this if it has not already been added
            if(myMessages.IndexOf(toAdd) <0 )
            {
                myMessages.Add(toAdd);
            }
        }

        public NetworkComponent GetComponentFromID(int TheID)
        {
            foreach(NetworkComponent nc in NetComponents)
            {
                if(nc.GetUniqueIdentifier == TheID)
                {
                    return nc;
                }
            }
            return null;
        }

        public NetworkLink GetLinkFromID(int TheID)
        {
            NetworkComponent nc = GetComponentFromID(TheID);
            if (nc == null) return null;
            if (nc.GetType().ToString() == "EduNetworkBuilder.NetworkLink")
                return (NetworkLink)nc;
            return null;
        }
        public NetworkDevice GetDeviceFromID(int ID)
        {
            NetworkComponent nc = GetComponentFromID(ID);
            if (nc == null) return null;
            if (nc.GetType().ToString() == "EduNetworkBuilder.NetworkDevice")
                return (NetworkDevice)nc;
            return null;
        }

        public NetworkDevice GetDeviceFromID(HostNicID LinkedNic)
        {
            return GetDeviceFromID(LinkedNic.HostID);
        }

        public NetworkDevice GetDeviceFromName(string DeviceName)
        {
            NetworkComponent nc = ItemFromName(DeviceName);
            if (nc == null) return null;
            if (NB.GetComponentType(nc) == GeneralComponentType.device)
                return (NetworkDevice)nc;
            return null;
        }

        public List<string> GetHostnames(bool EvenNonNetworked = false)
        {
            List<string> tList = new List<string>();
            NetworkDevice ND;
            foreach(NetworkComponent NC in NetComponents)
            {
                if(NB.GetComponentType(NC) == GeneralComponentType.device)
                {
                    ND = (NetworkDevice)NC;
                    if (!EvenNonNetworked && (ND.GetNetType() == NetworkComponentType.microwave || ND.GetNetType() == NetworkComponentType.fluorescent))
                        continue;
                    tList.Add(ND.hostname);
                }
            }
            tList.Sort();
            return tList;
        }

        public List<string> GetSubnets()
        {
            List<string> tList = new List<string>();
            NetworkDevice ND;
            List<string> subnets;
            foreach (NetworkComponent NC in NetComponents)
            {
                if (NB.GetComponentType(NC) == GeneralComponentType.device)
                {
                    ND = (NetworkDevice)NC;
                    subnets = ND.SubnetList();
                    foreach(string subnet in subnets)
                    {
                        if (!tList.Contains(subnet))
                            tList.Add(subnet);
                    }
                }
            }
            tList.Sort();
            return tList;
        }

        public List<string> GetBroadcasts()
        {
            List<string> tList = new List<string>();
            NetworkDevice ND;
            List<string> subnets;
            foreach (NetworkComponent NC in NetComponents)
            {
                if (NB.GetComponentType(NC) == GeneralComponentType.device)
                {
                    ND = (NetworkDevice)NC;
                    subnets = ND.BroadcastList();
                    foreach (string subnet in subnets)
                    {
                        if (!tList.Contains(subnet))
                            tList.Add(subnet);
                    }
                }
            }
            tList.Sort();
            return tList;
        }
        public void MarkAsLinked(HostNicID LinkedNic, int LinkID)
        {
            NetworkDevice nd = GetDeviceFromID(LinkedNic);
            //If the host exists, now mark the nic
            if(nd != null)
            {
                NetworkCard nic = nd.NicFromID(LinkedNic);
                if(nic != null)
                    nic.ConnectedLink = LinkID;
            }
        }
        public void MarkAsUnlinked(HostNicID LinkedNic, int LinkID)
        {
            NetworkDevice nd = GetDeviceFromID(LinkedNic);
            //If the host exists, now mark the nic
            if (nd != null)
            {
                NetworkCard nic = nd.NicFromID(LinkedNic);
                if ((nic != null && nic.ConnectedLink == LinkID) || LinkID == -1)
                {
                    nic.ConnectedLink = -1;
                }
            }
        }

        public IPAddress DNSLookup(NetworkDevice source, string toFind)
        {

            foreach(NetworkComponent nc in NB.Randomize(NetComponents))
            {
                NetworkDevice nd;
                if(NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)nc;
                    if(nd.hostname == toFind)
                    {
                        IPAddress found = nd.BestIPForThis(source);
                        return found;
                    }
                }
            }
            return null;
        }

        public string ReverseDNSLookup(NetworkDevice source, IPAddress toFind)
        {
            if (source != null && source.HasIPAddress(toFind))
                return source.hostname; //if the host is 127.0.0.1 or something.
            foreach (NetworkComponent nc in NB.Randomize(NetComponents))
            {
                NetworkDevice nd;
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)nc;
                    if (nd.HasIPAddress(toFind))
                        return nd.hostname;
                }
            }
            return null;
        }

        public bool DeviceIsOverDamaging(LinkType myLink, Point location)
        {
            NetworkDevice ND;
            int countDistance = 25;
            double HowFar;
            foreach(NetworkComponent NC in NetComponents)
            {
                if (NB.GetComponentType(NC) == GeneralComponentType.device)
                {
                    ND = (NetworkDevice)NC;
                    if(myLink == LinkType.wireless && ND.GetNetType() == NetworkComponentType.microwave)
                    {
                        HowFar = distance(location, ND.myLocation());
                        //Console.WriteLine("position=" + HowFar.ToString());
                        if (HowFar < countDistance)
                            if (HowFar < countDistance)
                            {
                                return true;
                            }
                    }
                    if (myLink != LinkType.wireless && ND.GetNetType() == NetworkComponentType.fluorescent)
                    {
                        HowFar = distance(location, ND.myLocation());
                        //Console.WriteLine("position=" + HowFar.ToString());
                        if (HowFar < countDistance)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        public double distance(Point start, Point dest)
        {
            return Math.Sqrt(Math.Pow((start.X - dest.X),2) + Math.Pow((start.Y - dest.Y),2)) / 5;  //use grid size...
        }

        public double distance(NetworkDevice start, NetworkDevice dest)
        {
            if (start == null || dest == null) return 0;
            return distance(start.myLocation(), dest.myLocation());
        }

        /// <summary>
        /// Return the closest wireless device we can connect to
        /// </summary>
        /// <param name="start"></param>
        /// <returns></returns>
        public NetworkCard BestWirelessLinkForDevice(NetworkCard start)
        {
            NetworkDevice starting = GetDeviceFromID(start.myID);
            NetworkCard found = null;
            NetworkDevice checking = null;
            double l_distance = 10000;
            if (starting == null) return null;
            NetworkCard answer=null;
            foreach(NetworkComponent nc in NetComponents)
            {
                if(NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    checking = (NetworkDevice)nc;
                    if (checking == starting) continue;
                    answer = checking.HasWPortSSIDKey(start.SSID, start.EncryptionKey);
                    if(answer != null)
                    {
                        double tdist = distance(starting, checking);
                       if(tdist < l_distance && tdist < NB.WirelessMaxUnsuccessfulLink)
                       {
                           l_distance = tdist;
                           found = answer;
                       }
                    }
                }
            }
            return found;
        }

        /****************************************
         * Do On All Devices
         * **************************************/
        public void DoAllDHCP()
        {
            NetworkDevice nd;
            foreach (NetworkComponent nc in NetComponents)
            {
                if(NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)nc;
                    nd.DHCPRequestFromHere();
                }
            }
        }

        public bool DoAllVerifyLinks()
        {
            NetworkLink nl;
            bool didanything = false;
            NetworkComponent nc;
            for (int i = NetComponents.Count -1; i >= 0; i-- )
            {
                nc = NetComponents[i];
                if (NB.GetComponentType(nc) == GeneralComponentType.link)
                {
                    nl = (NetworkLink)nc;
                    didanything = nl.VerifyLinkIntegrity() || didanything;
                }
            }
            return didanything;
        }

        public bool DoAllAutoJoin()
        {
            bool didanything = false;
            NetworkDevice nd;
            foreach (NetworkComponent nc in NetComponents.ToList())
            {
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)nc;
                    didanything = nd.AutoJoinWireless() || didanything;
                }
            }

            return didanything;
        }

        public void DoAllClearArp()
        {
            NetworkDevice nd;
            foreach (NetworkComponent nc in NetComponents)
            {
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)nc;
                    nd.ClearArps();
                }
            }
        }
        public void DoAllPing(IPAddress destination)
        {
            NetworkDevice nd;
            foreach (NetworkComponent nc in NetComponents)
            {
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)nc;
                    nd.PingFromHere(destination);
                }
            }
        }
        public void DoAllClearIPs()
        {
            NetworkDevice nd;
            foreach (NetworkComponent nc in NetComponents)
            {
                if (NB.GetComponentType(nc) == GeneralComponentType.device)
                {
                    nd = (NetworkDevice)nc;
                    nd.ClearIPs();
                }
            }
        }
    }

}