using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Threading;
namespace CFPingProject
{
    public class Ping
    {
        private int packetSize = 32;        // size of packet in bytes 
        private int numberOfPackets = 3;    // number of packets send 
        private int timeOut = 3000;         // for how long we wait for returning packet 
        private IPAddress destination = null; // destination address
        private ICMP[] packets = new ICMP[3]; //
        private int numberOfLostPackets = 0;
        public List<PingReplay> pingReplies = new List<PingReplay>();
        private List<int> TimeOutIdentifiers = new List<int>();
        private Form2 MyForm2;
        private DateTime executionTime;


        byte[] data;
        int recv;
        double pingstart;
        double pingstop;
        IPEndPoint iep;
        EndPoint ep;
        CFStopWatch stopwatch;
        bool flag = false;
        double minRtt = 10000;
        double maxRtt = 0;
        double avgRtt = 0;



        public Ping(int _packetSize, int _numberOfPackets, int _timeOut, string _destination)
        {
            packetSize = _packetSize;
            numberOfPackets = _numberOfPackets;
            timeOut = _timeOut;
            destination = IPAddress.Parse(_destination);

            packets = new ICMP[numberOfPackets];

            int identifier = 1;

            for (int i = 0; i < numberOfPackets; i++)
            {
                packets[i] = new ICMP(packetSize - 4);
                packets[i].Type = 0x08;
                packets[i].Code = 0x00;
                packets[i].Checksum = 0;
                Buffer.BlockCopy(BitConverter.GetBytes((short)identifier), 0, packets[i].Message, 0, 2);    //sets value of 1st and 2nd byte in message
                Buffer.BlockCopy(BitConverter.GetBytes((short)1), 0, packets[i].Message, 2, 2);             //sets value of 3rd and 4th byte in message
                int packetsize = packets[i].MessageSize + 4;
                UInt16 chcksum = packets[i].getChecksum();
                packets[i].Checksum = chcksum;
                identifier++;

            }



        }


        public Ping(string _destination)
        {
            destination = IPAddress.Parse(_destination);


            int identifier = 1;
            for (int i = 0; i < numberOfPackets; i++)
            {
                packets[i] = new ICMP(packetSize - 4);
            }

            foreach (ICMP packet in packets)
            {
                packet.Type = 0x08;
                packet.Code = 0x00;
                packet.Checksum = 0;
                Buffer.BlockCopy(BitConverter.GetBytes((short)identifier), 0, packet.Message, 0, 2);    //sets value of 1st and 2nd byte in message
                Buffer.BlockCopy(BitConverter.GetBytes((short)1), 0, packet.Message, 2, 2);             //sets value of 3rd and 4th byte in message
                //Buffer.BlockCopy(data, 0, packet.Message, 4, data.Length);
                //packet.MessageSize = data.Length + 4;
                int packetsize = packet.MessageSize + 4;
                UInt16 chcksum = packet.getChecksum();
                packet.Checksum = chcksum;
                identifier++;

                Console.Write("identifier " + identifier.ToString());
                Console.WriteLine(" packetsize " + packetsize.ToString());
            }


        }

        public string Destination
        {
            get
            {
                return destination.ToString();
            }
            set
            {
                destination = IPAddress.Parse(value);
            }
        }

        public int NumberOfPackets
        {
            get
            {
                return numberOfPackets;
            }
            set
            {
                numberOfPackets = value;
            }
        }

        public int TimeOut
        {
            get
            {
                return timeOut;
            }
            set
            {
                timeOut = value;
            }
        }

        public int PacketSize
        {
            get
            {
                return packetSize;
            }
            set
            {
                packetSize = value;
            }
        }

        public int NumberOfLostPackets
        {
            get
            {
                return numberOfLostPackets;
            }
        }

        public List<PingReplay> PingReplies
        {
            get
            { 
                return pingReplies; 
            }
            set 
            {
            }
        }

        public double AverageRTT
        {
            get
            {
                return avgRtt;
            }
        }

        public double MinRTT
        {
            get
            {
                return minRtt;
            }
        }
        public double MaxRTT
        {
            get
            {
                return maxRtt;
            }
        }
        public void Start()
        {
            executionTime = DateTime.Now;
            Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
            iep = new IPEndPoint(destination, 0);
            ep = (EndPoint)iep;

            Form2 MyForm2 = new Form2(this);
            MyForm2.Show();
            MyForm2.Visible = true;
            MyForm2.Activate();
            MyForm2.TopMost = true;
            MyForm2.Show();
            MyForm2.Refresh();

            foreach (ICMP packet in packets)
            {
                stopwatch = new CFStopWatch();
                stopwatch.Calibrate();
                stopwatch.Start();
                host.SendTo(packet.getBytes(), packetSize, SocketFlags.None, iep);
                pingstart = stopwatch.GetSplitTimeInMicroseconds();
                int SentIdentifier = BitConverter.ToInt16(packet.Message, 0);
                flag = false;
                try
                {

                    data = new byte[packetSize + 100];
                    host.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceivedData), host);

                    while (flag == false && ((pingstart + stopwatch.GetSplitTimeInMicroseconds()) < timeOut * 1000)) ;
                    if (flag == false)//jesli byl timeout
                    {
                        TimeOutIdentifiers.Add(SentIdentifier); // dodaje id do timeo  utow
                        PingReplies.Add(new PingReplay(destination.ToString(), 0, SentIdentifier, 0, 0, true));
                        MyForm2.upDateList(PingReplies.Last().Identifier.ToString() + ". Destination :" + PingReplies.Last().ReplyIP.ToString());
                        MyForm2.upDateList("    Timeout");

                    }
                    else
                    {
                        ICMP response = new ICMP(data, recv);
                        int ReceivedIdentifier = BitConverter.ToInt16(response.Message, 0);
                        if (!TimeOutIdentifiers.Contains(ReceivedIdentifier)) //jesli nie bylo timeout
                        {
                            PingReplies.Add(new PingReplay(destination.ToString(), Math.Round((pingstop - pingstart) / 1000, 1), ReceivedIdentifier, response.Type, response.Code, false));
                            MyForm2.upDateList(PingReplies.Last().Identifier.ToString() + ". Reply From " + PingReplies.Last().ReplyIP.ToString());
                            MyForm2.upDateList("    RTT: " + (PingReplies.Last().Rtt).ToString());
                        }
                    }
                }
                catch (SocketException e)
                {

                }
            }
            MakeStatistics();
            MyForm2.upDateList("==================== ");
            MyForm2.upDateList("number of lost packets: " + numberOfLostPackets.ToString());
            MyForm2.upDateList("average rtt           : " + avgRtt.ToString() + "ms");
            MyForm2.upDateList("max rtt               : " + maxRtt.ToString() + "ms");
            MyForm2.upDateList("min rtt               : " + minRtt.ToString() + "ms");

        }

        void ReceivedData(IAsyncResult iar)
        {
            Socket remote = (Socket)iar.AsyncState;
            recv = remote.EndReceive(iar);
            pingstop = stopwatch.GetSplitTimeInMicroseconds();
            flag = true;
        }

        public DateTime getExecutionTime()
        {
            return executionTime;
        }


        void MakeStatistics()
        {

            foreach (PingReplay pingReplay in PingReplies)
            {
                if (pingReplay.TimeOut) numberOfLostPackets++;
                avgRtt = avgRtt + pingReplay.Rtt;
                if (pingReplay.Rtt > maxRtt) maxRtt = pingReplay.Rtt;
                if (pingReplay.Rtt < minRtt) minRtt = pingReplay.Rtt;
            }
            if (numberOfLostPackets != numberOfPackets) avgRtt = Math.Round((avgRtt / (numberOfPackets - numberOfLostPackets)), 2);

        }

    }
}
