using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using System.Xml;
using System.Windows.Forms;


namespace EduNetworkBuilder
{
    [Serializable]
    public class IPAddress
    {

        private UInt32 _ip;
        private UInt32 _mask;
        private UInt32 _gw;
        private IPAddressType myType;

        public IPAddress(string ip, string mask, IPAddressType WhatType)
        {
            myType = WhatType;
            _ip = ip.ParseIp();
            _mask = mask.ParseIp();
        }

        public IPAddress(string ip, string mask, string gw)
        {
            myType = IPAddressType.route;
            _ip = ip.ParseIp();
            _mask = mask.ParseIp();
            _gw = gw.ParseIp();
        }

        public IPAddress(XmlNode theNode)
        {
            foreach (XmlNode Individual in theNode.ChildNodes)
            {
                XmlNodeType myNodetype = Individual.NodeType;
                if (myNodetype == XmlNodeType.Element)
                {
                    switch (Individual.Name.ToLower())
                    {
                        case "ip":
                            _ip = Individual.InnerText.ParseIp();
                            break;
                        case "mask":
                            _mask = Individual.InnerText.ParseIp();
                            break;
                        case "gateway":
                            _gw = Individual.InnerText.ParseIp();
                            break;
                        case "type":
                            myType = NB.ParseEnum<IPAddressType>(Individual.InnerText);
                            break;
                    }
                }
            }
        }

        public void Save(XmlWriter writer, string tag)
        {
            writer.WriteStartElement(tag);
            writer.WriteElementString("ip", _ip.ToIpString());
            writer.WriteElementString("mask", _mask.ToIpString());
            writer.WriteElementString("gateway", _gw.ToIpString());
            writer.WriteElementString("type", myType.ToString());
            writer.WriteEndElement();
        }

        public void Reparse(string ip, string mask, string gw)
        {
            _ip = ip.ParseIp();
            _mask = mask.ParseIp();
            _gw = gw.ParseIp();
        }
        public void Reparse(string ip, string mask)
        {
            _ip = ip.ParseIp();
            _mask = mask.ParseIp();
        }

        public IPAddressType GetAddressType
        {
            get { return myType; }
        }

        public bool IsLocal(IPAddress dest)
        {
            if (dest == null) return false;
            UInt32 answer = dest.GetIP & _mask;
            if ((dest.GetIP & _mask) == NetworkAddress)
                return true;
            return false;
        }

        public bool Edit(NetworkDevice FromWhat, Form ParentForm, string message="")
        {
            IPAddressEntry IPe = new IPAddressEntry(this, FromWhat, ParentForm);
            if (message != "")
                IPe.Text = message;
            return IPe.Edit();
        }

        public bool Edit(NetworkDevice FromWhat, IPAddress DHCPif, Form ParentForm)
        {
            IPAddressEntry IPe = new IPAddressEntry(this, FromWhat, ParentForm);
            return IPe.Edit(FromWhat, DHCPif);
        }

        public IPAddress(string ip)
        {
            myType = IPAddressType.ip;
            _ip = ip.ParseIp();
            var mySplitVal = ip.Split('/');
            if (mySplitVal.Count() < 2)
            {
                //We do not have a cidr.  We need to guess
                //For now, use 255.255.255.0
                _mask = "255.255.255.0".ParseIp();
                mySplitVal = ip.Split('.');
                if(mySplitVal.Count() > 0)
                {
                    //If it is not one of these three, we already use 255.255.255.0
                    if(mySplitVal[0] == "10")
                    {
                        _mask = "255.0.0.0".ParseIp();
                    }
                    if (mySplitVal[0] == "172")
                    {
                        _mask = "255.255.0.0".ParseIp();
                    }
                    if (mySplitVal[0] == "192")
                    {
                        _mask = "255.255.255.0".ParseIp();
                    }
                }
            }
            else
            {
                UInt32 tInt = 0;
                int cdr;
                int.TryParse(mySplitVal[1], out cdr);
                for (int loop = 0; loop < 32; loop++)
                {
                    tInt = (tInt << 1);
                    if (loop < cdr) tInt++;
                }
                _mask = tInt;
            }
        }


        public bool Equals(UInt32 IP)
        {
            return IP == _ip;
        }

        public bool Equals(UInt32 IP, UInt32 mask)
        {
            return (IP == _ip && mask == _mask);
        }


        public UInt32 NumberOfHosts
        {
            get { return ~_mask + 1; }
        }

        public UInt32 GetIP
        {
            get { return _ip; }
        }
        public string GetIPString
        {
            get { return _ip.ToIpString(); }
        }
        public string GetBroadcastString
        {
            get { return BroadcastAddress.ToIpString(); }
        }
        public string GetMaskString
        {
            get { return _mask.ToIpString(); }
        }

        public UInt32 GetMask
        {
            get { return _mask; }
        }
        public UInt32 GetGateway
        {
            get { return _gw; }
        }

        public UInt32 NetworkAddress
        {
            get { return _ip & _mask; }
        }

        public UInt32 BroadcastAddress
        {
            get { return NetworkAddress + ~_mask; }
        }

        public IEnumerable<UInt32> Hosts()
        {
            for (var host = NetworkAddress + 1; host < BroadcastAddress; host++)
            {
                yield return host;
            }
        }
        public string IPFormat()
        {
            return IPFormat(_gw.ToIpString());
        }
        public string PadIt(string Addr)
        {
            string myaddr = Addr.PadRight(12);
            return myaddr;
        }
        public string IPFormat(string gw)
        {
            string tstring = string.Format(NB.Translate("IPA_IPFormatStr"), 
                PadIt(_ip.ToIpString()), PadIt(_mask.ToIpString()), PadIt(gw));
            return tstring;
        }
    }

    public static class IpHelpers
    {
        public static string ToIpString(this UInt32 value)
        {
            var bitmask = 0xff000000;
            var parts = new string[4];
            for (var i = 0; i < 4; i++)
            {
                var masked = (value & bitmask) >> ((3 - i) * 8);
                bitmask >>= 8;
                parts[i] = masked.ToString(CultureInfo.InvariantCulture);
            }
            return String.Join(".", parts);
        }

        public static string ToBitString(this UInt32 value)
        {
            var item = System.Convert.ToString(value, 2);
            return (string)item;
        }

        public static UInt32 ParseIp(this string ipAddress)
        {
            if (ipAddress == null) ipAddress = "";
            var gw = ipAddress.Split('/'); //Pull off any cdr
            var mySplitVal = gw[0].Split('.');
            if (mySplitVal.Count() != 4) 
                return 0;
            UInt32 ip = 0;
            uint part;
            if (mySplitVal.Count() == 4)
            {
                for (var i = 0; i < 4; i++)
                {
                    part = 0;
                    UInt32.TryParse(mySplitVal[i], out part);
                    ip = (ip << 8) + part;
                }
            }
            return ip;
        }
    }
}