using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EduNetworkBuilder
{
    public class NBRenumberer
    {
        private bool renumbered = false;
        public List<NBRenumberData> ListOfItems = new List<NBRenumberData>();

        public void Count(string what)
        {
            NBRenumberData entry = DataFromName(what);
            if(entry != null)
            {
                entry.ActiveCount++;
                return;
            }
            //If we get here, we have not found it yet.
            NBRenumberData One = new NBRenumberData(what, ListOfItems.Count);
            One.ActiveCount++;
            ListOfItems.Add(One);
        }
        public void SetAsGateway(string what)
        {
            NBRenumberData GW = DataFromName(what);
            if (GW == null) {
                GW = new NBRenumberData(what, ListOfItems.Count);
                ListOfItems.Add(GW);
            }
            GW.isTheGateway = true;
            GW.UpdateSortOrder();
            //We can only have one gateway.  Make sure all the rest are set not to be the GW
            foreach(NBRenumberData one in ListOfItems)
            {
                if (one != GW) {
                    one.isTheGateway = false;
                    one.UpdateSortOrder();
                }

            }
        }

        public NBRenumberData DataFromName(string hostname)
        {
            foreach (NBRenumberData NBRD in ListOfItems)
            {
                if (NBRD.Hostname == hostname)
                {
                    return NBRD;
                }
            }
            return null;
        }

        public bool TryRenumbering(NB_IPAddress Network, NB_IPAddress Gateway)
        {
            renumbered = true;
            //Separate out the devices by type
            //  Find the gateway too
            int biggestcount = 0;
            int spacing = 10;
            bool HasGateway = false;
            foreach (NBRenumberData NBRD in ListOfItems)
            {
                if (NBRD.ActiveCount > biggestcount) biggestcount = NBRD.ActiveCount;
            }
            //now, we can prune off everything that is not part of biggestcount.
            for(int i=ListOfItems.Count-1; i>=0; i--)
            {
                if (ListOfItems[i].ActiveCount != biggestcount)
                    ListOfItems.RemoveAt(i);
                else
                {
                    if (ListOfItems[i].isTheGateway) HasGateway = true;
                    ListOfItems[i].SuggestedIP = "";
                }
            }
            if (!HasGateway)
                for (int i = ListOfItems.Count - 1; i >= 0; i--)
                {
                    if (ListOfItems[i].Device.HasIPAddress(Gateway))
                        SetAsGateway(ListOfItems[i].Hostname);
                }

            int switches = 0;
            int servers = 0;
            int printers = 0;
            int wireless = 0;
            int clients = 0;
            foreach(NBRenumberData NBRD in ListOfItems)
            {                
                switch(NBRD.TypeString())
                {
                    case "client":
                        clients++;
                        break;

                    case "printer":
                        printers++;
                        break;

                    case "switch":
                        switches++;
                        break;

                    case "server":
                        servers++;
                        break;
                    case "wireless":
                        wireless++;
                        break;
                }
            }
            //Decide on which way we want to do this.
            biggestcount = 0;
            if (servers > biggestcount) biggestcount = servers;
            if (switches > biggestcount) biggestcount = switches;
            if (printers > biggestcount) biggestcount = printers;
            if (wireless > biggestcount) biggestcount = wireless;
            if (clients > biggestcount) biggestcount = clients;

            ListOfItems.Sort((p, q) => p.sortorder.CompareTo(q.sortorder));

            //If it works to do 10 for each bunch, do that.  Otherwise, see what the smallest bunch that works is.
            if (biggestcount < 10)
            {
                spacing = 10;
            }
            if (biggestcount > 10)
                spacing = 99; //Don't bother with anything fancy.  Punt if it does not fit the first time
            //Fallback is to simply count up from the gateway.
            if (Network.NumberOfHosts < ListOfItems.Count)
                return false;
            if(spacing * 5 > Network.NumberOfHosts)
            {
                renumberlist(Network, 1, "");
            }
            else
            {
                //We can do this in parts
                renumberlist(Network, 1, "Gateway");
                renumberlist(Network, 2, "server");
                renumberlist(Network, spacing * 1, "switch");
                renumberlist(Network, spacing * 2, "wireless");
                renumberlist(Network, spacing * 3, "printer");
                renumberlist(Network, spacing * 4, "client");
            }
            return true;
        }

        private void renumberlist(NB_IPAddress Network, int Base, string filter)
        {
            int count = Base;
            for (int i = 0; i < ListOfItems.Count; i++)
            {
                if (ListOfItems[i].SuggestedIP != "") continue;
                if (filter == "" || ListOfItems[i].TypeString() == filter || filter == "Gateway" && ListOfItems[i].isTheGateway)
                {
                    NB_IPAddress next = new NB_IPAddress(Network);
                    next._ip += (UInt32)count;
                    ListOfItems[i].SuggestedIP = next.GetIPString;
                    count++;
                }
            }
        }

        public void DoRenumbering(NB_IPAddress Network, NB_IPAddress Gateway, int vlanID)
        {
            if (!renumbered) TryRenumbering(Network, Gateway);

            //loop through and set all the IPs and the gateway IP if not DHCPS
            for(int i = 0; i < ListOfItems.Count; i++)
            {
                ListOfItems[i].Device.RenumberIP(ListOfItems[i].SuggestedIP, Gateway, vlanID);
            }
        }
    }

    public class NBRenumberData
    {
        public string Hostname = "";
        public NetworkDevice Device {
            get {
                Network myNet = NB.GetNetwork();
                if (myNet == null) return null;
                return myNet.DeviceFromName(Hostname);
            }
        }
        public int ActiveCount=0;
        public string nicname;
        public string SuggestedIP;
        public int sortorder = 0;
        private int _index = 0;
        public NetworkComponentType mytype = NetworkComponentType.none;

        public bool isTheGateway = false;

        private bool _isRouter = false;
        /// <summary>
        /// Return true if the Device specified by hostname does routing.
        /// </summary>
        public bool IsRouter {
            get {
                if (_isRouter) return true;
                NetworkDevice ND = Device;
                if(ND.RoutesPackets())
                {
                    _isRouter = true;
                    return true;
                }
                return false;
            }
        }

        public NBRenumberData(string hostname, int index)
        {
            Hostname = hostname;
            NetworkDevice nd = Device;
            mytype = nd.myType;
            _index = index;
            UpdateSortOrder();
        }

        public void UpdateSortOrder()
        {
            switch (TypeString())
            {
                case "client":
                    sortorder=5000;
                    break;

                case "printer":
                    sortorder = 4000;
                    break;

                case "switch":
                    sortorder=2000;
                    break;

                case "server":
                    sortorder=1000;
                    break;
                case "wireless":
                    sortorder=3000;
                    break;
            }
            sortorder = sortorder + _index;
            if (isTheGateway) sortorder = 0;
        }

        public string TypeString()
        {
            string what = "";
            switch (mytype)
            {
                case NetworkComponentType.cellphone:
                case NetworkComponentType.ip_phone:
                case NetworkComponentType.laptop:
                case NetworkComponentType.pc:
                case NetworkComponentType.tablet:
                    what = "client";
                    break;

                case NetworkComponentType.printer:
                case NetworkComponentType.copier:
                    what = "printer";
                    break;

                case NetworkComponentType.net_hub:
                case NetworkComponentType.net_switch:
                    what = "switch";
                    break;

                case NetworkComponentType.firewall:
                case NetworkComponentType.router:
                case NetworkComponentType.server:
                    what = "server";
                    break;
                case NetworkComponentType.wap:
                case NetworkComponentType.wbridge:
                case NetworkComponentType.wrepeater:
                case NetworkComponentType.wrouter:
                    what = "wireless";
                    break;
            }
            return what;
        }
    }
}
